You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by ji...@apache.org on 2015/09/10 15:00:41 UTC
tajo git commit: TAJO-1833: Refine LogicalPlanPreprocessor to add new
rules easily.
Repository: tajo
Updated Branches:
refs/heads/master fe99e0fff -> 508d17a2d
TAJO-1833: Refine LogicalPlanPreprocessor to add new rules easily.
Closes #752
Project: http://git-wip-us.apache.org/repos/asf/tajo/repo
Commit: http://git-wip-us.apache.org/repos/asf/tajo/commit/508d17a2
Tree: http://git-wip-us.apache.org/repos/asf/tajo/tree/508d17a2
Diff: http://git-wip-us.apache.org/repos/asf/tajo/diff/508d17a2
Branch: refs/heads/master
Commit: 508d17a2d7fdb3f5ac9a9dabec46b6ec4e5fb1cb
Parents: fe99e0f
Author: Jihoon Son <ji...@apache.org>
Authored: Thu Sep 10 22:00:00 2015 +0900
Committer: Jihoon Son <ji...@apache.org>
Committed: Thu Sep 10 22:00:00 2015 +0900
----------------------------------------------------------------------
CHANGES | 3 +
.../tajo/plan/LogicalPlanPreprocessor.java | 551 +----------------
.../org/apache/tajo/plan/LogicalPlanner.java | 6 +-
.../BaseLogicalPlanPreprocessEngine.java | 100 ++++
.../BaseLogicalPlanPreprocessPhaseProvider.java | 34 ++
.../rewrite/BaseLogicalPlanRewriteEngine.java | 2 +-
.../tajo/plan/rewrite/BaseSchemaBuildPhase.java | 591 +++++++++++++++++++
.../rewrite/LogicalPlanPreprocessEngine.java | 28 +
.../rewrite/LogicalPlanPreprocessPhase.java | 64 ++
.../LogicalPlanPreprocessPhaseProvider.java | 30 +
.../plan/rewrite/LogicalPlanRewriteEngine.java | 2 +-
.../plan/rewrite/LogicalPlanRewriteRule.java | 4 +-
12 files changed, 873 insertions(+), 542 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index ccbca88..250a612 100644
--- a/CHANGES
+++ b/CHANGES
@@ -526,6 +526,9 @@ Release 0.11.0 - unreleased
TASKS
+ TAJO-1833: Refine LogicalPlanPreprocessor to add new rules easily.
+ (jihoon)
+
TAJO-1809: Change default value of several configurations. (jihoon)
TAJO-1803: Use in-memory derby as the default catalog for unit tests.
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
index f2c15ac..1daba17 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
@@ -18,551 +18,28 @@
package org.apache.tajo.plan;
-import org.apache.tajo.SessionVars;
-import org.apache.tajo.algebra.*;
-import org.apache.tajo.catalog.*;
-import org.apache.tajo.exception.UndefinedColumnException;
-import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.catalog.CatalogService;
import org.apache.tajo.exception.TajoException;
-import org.apache.tajo.plan.LogicalPlan.QueryBlock;
-import org.apache.tajo.plan.algebra.BaseAlgebraVisitor;
-import org.apache.tajo.plan.expr.ConstEval;
-import org.apache.tajo.plan.expr.EvalNode;
-import org.apache.tajo.plan.expr.FieldEval;
-import org.apache.tajo.plan.logical.*;
-import org.apache.tajo.plan.nameresolver.NameResolver;
-import org.apache.tajo.plan.nameresolver.NameResolvingMode;
-import org.apache.tajo.plan.util.PlannerUtil;
-import org.apache.tajo.plan.visitor.SimpleAlgebraVisitor;
-import org.apache.tajo.util.TUtil;
-
-import java.util.*;
+import org.apache.tajo.plan.LogicalPlanner.PlanContext;
+import org.apache.tajo.plan.logical.LogicalNode;
+import org.apache.tajo.plan.rewrite.BaseLogicalPlanPreprocessEngine;
+import org.apache.tajo.plan.rewrite.BaseLogicalPlanPreprocessPhaseProvider;
/**
* It finds all relations for each block and builds base schema information.
*/
-public class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanner.PlanContext, LogicalNode> {
- private TypeDeterminant typeDeterminant;
- private ExprAnnotator annotator;
-
- /** Catalog service */
- private CatalogService catalog;
+public class LogicalPlanPreprocessor {
+ private BaseLogicalPlanPreprocessEngine engine;
+ private BaseLogicalPlanPreprocessPhaseProvider provider;
LogicalPlanPreprocessor(CatalogService catalog, ExprAnnotator annotator) {
- this.catalog = catalog;
- this.annotator = annotator;
- this.typeDeterminant = new TypeDeterminant(catalog);
- }
-
- @Override
- public void preHook(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Expr expr) throws TajoException {
- ctx.queryBlock.setAlgebraicExpr(expr);
- ctx.plan.mapExprToBlock(expr, ctx.queryBlock.getName());
- }
-
- @Override
- public LogicalNode postHook(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Expr expr, LogicalNode result)
- throws TajoException {
- // If non-from statement, result can be null. It avoids that case.
- if (result != null) {
- // setNode method registers each node to corresponding block and plan.
- ctx.queryBlock.registerNode(result);
- // It makes a map between an expr and a logical node.
- ctx.queryBlock.registerExprWithNode(expr, result);
- }
- return result;
- }
-
- /**
- * Get all columns of the relations correspondent to the asterisk expression.
- * @param ctx
- * @param asteriskExpr
- * @return array of columns
- * @throws TajoException
- */
- public static Column[] getColumns(LogicalPlanner.PlanContext ctx, QualifiedAsteriskExpr asteriskExpr)
- throws TajoException {
- RelationNode relationOp = null;
- QueryBlock block = ctx.queryBlock;
- Collection<QueryBlock> queryBlocks = ctx.plan.getQueryBlocks();
- if (asteriskExpr.hasQualifier()) {
- String qualifier;
-
- if (CatalogUtil.isFQTableName(asteriskExpr.getQualifier())) {
- qualifier = asteriskExpr.getQualifier();
- } else {
- qualifier = CatalogUtil.buildFQName(
- ctx.queryContext.get(SessionVars.CURRENT_DATABASE), asteriskExpr.getQualifier());
- }
-
- relationOp = block.getRelation(qualifier);
-
- // if a column name is outside of this query block
- if (relationOp == null) {
- // TODO - nested query can only refer outer query block? or not?
- for (QueryBlock eachBlock : queryBlocks) {
- if (eachBlock.existsRelation(qualifier)) {
- relationOp = eachBlock.getRelation(qualifier);
- }
- }
- }
-
- // If we cannot find any relation against a qualified column name
- if (relationOp == null) {
- throw new UndefinedColumnException(CatalogUtil.buildFQName(qualifier, "*"));
- }
-
- Schema schema = relationOp.getLogicalSchema();
- Column[] resolvedColumns = new Column[schema.size()];
- return schema.getRootColumns().toArray(resolvedColumns);
- } else { // if a column reference is not qualified
- // columns of every relation should be resolved.
- Iterator<RelationNode> iterator = block.getRelations().iterator();
- Schema schema;
- List<Column> resolvedColumns = TUtil.newList();
-
- while (iterator.hasNext()) {
- relationOp = iterator.next();
- if (relationOp.isNameResolveBase()) {
- schema = relationOp.getLogicalSchema();
- resolvedColumns.addAll(schema.getRootColumns());
- }
- }
-
- if (resolvedColumns.size() == 0) {
- throw new UndefinedColumnException(asteriskExpr.toString());
- }
-
- return resolvedColumns.toArray(new Column[resolvedColumns.size()]);
- }
- }
-
- /**
- * Resolve an asterisk expression to the real column reference expressions.
- * @param ctx context
- * @param asteriskExpr asterisk expression
- * @return a list of NamedExpr each of which has ColumnReferenceExprs as its child
- * @throws TajoException
- */
- private static List<NamedExpr> resolveAsterisk(LogicalPlanner.PlanContext ctx, QualifiedAsteriskExpr asteriskExpr)
- throws TajoException {
- Column[] columns = getColumns(ctx, asteriskExpr);
- List<NamedExpr> newTargetExprs = new ArrayList<NamedExpr>(columns.length);
- int i;
- for (i = 0; i < columns.length; i++) {
- newTargetExprs.add(new NamedExpr(new ColumnReferenceExpr(columns[i].getQualifier(), columns[i].getSimpleName())));
- }
- return newTargetExprs;
- }
-
- private static boolean hasAsterisk(NamedExpr [] namedExprs) {
- for (NamedExpr eachTarget : namedExprs) {
- if (eachTarget.getExpr().getType() == OpType.Asterisk) {
- return true;
- }
- }
- return false;
- }
-
- private static NamedExpr [] voidResolveAsteriskNamedExpr(LogicalPlanner.PlanContext context,
- NamedExpr [] namedExprs) throws TajoException {
- List<NamedExpr> rewrittenTargets = TUtil.newList();
- for (NamedExpr originTarget : namedExprs) {
- if (originTarget.getExpr().getType() == OpType.Asterisk) {
- // rewrite targets
- rewrittenTargets.addAll(resolveAsterisk(context, (QualifiedAsteriskExpr) originTarget.getExpr()));
- } else {
- rewrittenTargets.add(originTarget);
- }
- }
- return rewrittenTargets.toArray(new NamedExpr[rewrittenTargets.size()]);
- }
-
- @Override
- public LogicalNode visitSetSession(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SetSession expr)
- throws TajoException {
- SetSessionNode setSession = ctx.plan.createNode(SetSessionNode.class);
- return setSession;
- }
-
- @Override
- public LogicalNode visitProjection(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Projection expr)
- throws TajoException {
- // If Non-from statement, it immediately returns.
- if (!expr.hasChild()) {
- EvalExprNode exprNode = ctx.plan.createNode(EvalExprNode.class);
- exprNode.setTargets(buildTargets(ctx, expr.getNamedExprs()));
- return exprNode;
- }
-
- stack.push(expr); // <--- push
- LogicalNode child = visit(ctx, stack, expr.getChild());
-
- // Resolve the asterisk expression
- if (hasAsterisk(expr.getNamedExprs())) {
- expr.setNamedExprs(voidResolveAsteriskNamedExpr(ctx, expr.getNamedExprs()));
- }
-
- NamedExpr[] projectTargetExprs = expr.getNamedExprs();
- for (int i = 0; i < expr.getNamedExprs().length; i++) {
- NamedExpr namedExpr = projectTargetExprs[i];
-
- // 1) Normalize all field names occurred in each expr into full qualified names
- NameRefInSelectListNormalizer.normalize(ctx, namedExpr.getExpr());
-
- // 2) Register explicit column aliases to block
- if (namedExpr.getExpr().getType() == OpType.Column && namedExpr.hasAlias()) {
- ctx.queryBlock.addColumnAlias(((ColumnReferenceExpr)namedExpr.getExpr()).getCanonicalName(),
- namedExpr.getAlias());
- } else if (OpType.isLiteralType(namedExpr.getExpr().getType()) && namedExpr.hasAlias()) {
- Expr constExpr = namedExpr.getExpr();
- ConstEval constEval = (ConstEval) annotator.createEvalNode(ctx, constExpr, NameResolvingMode.RELS_ONLY);
- ctx.queryBlock.addConstReference(namedExpr.getAlias(), constExpr, constEval);
- }
- }
-
- Target[] targets = buildTargets(ctx, expr.getNamedExprs());
-
- stack.pop(); // <--- Pop
-
- ProjectionNode projectionNode = ctx.plan.createNode(ProjectionNode.class);
- projectionNode.setInSchema(child.getOutSchema());
- projectionNode.setOutSchema(PlannerUtil.targetToSchema(targets));
-
- ctx.queryBlock.setSchema(projectionNode.getOutSchema());
- return projectionNode;
- }
-
- private Target [] buildTargets(LogicalPlanner.PlanContext context, NamedExpr [] exprs) throws TajoException {
- Target [] targets = new Target[exprs.length];
- for (int i = 0; i < exprs.length; i++) {
- NamedExpr namedExpr = exprs[i];
- TajoDataTypes.DataType dataType = typeDeterminant.determineDataType(context, namedExpr.getExpr());
-
- if (namedExpr.hasAlias()) {
- targets[i] = new Target(new FieldEval(new Column(namedExpr.getAlias(), dataType)));
- } else {
- String generatedName = context.plan.generateUniqueColumnName(namedExpr.getExpr());
- targets[i] = new Target(new FieldEval(new Column(generatedName, dataType)));
- }
- }
- return targets;
- }
-
- @Override
- public LogicalNode visitLimit(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Limit expr)
- throws TajoException {
- stack.push(expr);
- LogicalNode child = visit(ctx, stack, expr.getChild());
- stack.pop();
-
- LimitNode limitNode = ctx.plan.createNode(LimitNode.class);
- limitNode.setInSchema(child.getOutSchema());
- limitNode.setOutSchema(child.getOutSchema());
- return limitNode;
- }
-
- @Override
- public LogicalNode visitSort(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Sort expr) throws TajoException {
- stack.push(expr);
- LogicalNode child = visit(ctx, stack, expr.getChild());
- stack.pop();
-
- SortNode sortNode = ctx.plan.createNode(SortNode.class);
- sortNode.setInSchema(child.getOutSchema());
- sortNode.setOutSchema(child.getOutSchema());
- return sortNode;
- }
-
- @Override
- public LogicalNode visitHaving(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Having expr)
- throws TajoException {
- stack.push(expr);
- LogicalNode child = visit(ctx, stack, expr.getChild());
- stack.pop();
-
- HavingNode havingNode = ctx.plan.createNode(HavingNode.class);
- havingNode.setInSchema(child.getOutSchema());
- havingNode.setOutSchema(child.getOutSchema());
- return havingNode;
- }
-
- @Override
- public LogicalNode visitGroupBy(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Aggregation expr)
- throws TajoException {
- stack.push(expr); // <--- push
- LogicalNode child = visit(ctx, stack, expr.getChild());
-
- Projection projection = ctx.queryBlock.getSingletonExpr(OpType.Projection);
- int finalTargetNum = projection.getNamedExprs().length;
- Target [] targets = new Target[finalTargetNum];
-
- if (hasAsterisk(projection.getNamedExprs())) {
- projection.setNamedExprs(voidResolveAsteriskNamedExpr(ctx, projection.getNamedExprs()));
- }
-
- for (int i = 0; i < finalTargetNum; i++) {
- NamedExpr namedExpr = projection.getNamedExprs()[i];
- EvalNode evalNode = annotator.createEvalNode(ctx, namedExpr.getExpr(), NameResolvingMode.SUBEXPRS_AND_RELS);
-
- if (namedExpr.hasAlias()) {
- targets[i] = new Target(evalNode, namedExpr.getAlias());
- } else {
- targets[i] = new Target(evalNode, "?name_" + i);
- }
- }
- stack.pop();
-
- GroupbyNode groupByNode = ctx.plan.createNode(GroupbyNode.class);
- groupByNode.setInSchema(child.getOutSchema());
- groupByNode.setOutSchema(PlannerUtil.targetToSchema(targets));
- return groupByNode;
- }
-
- @Override
- public LogicalNode visitUnion(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SetOperation expr)
- throws TajoException {
- LogicalPlan.QueryBlock leftBlock = ctx.plan.newQueryBlock();
- LogicalPlanner.PlanContext leftContext = new LogicalPlanner.PlanContext(ctx, leftBlock);
- LogicalNode leftChild = visit(leftContext, new Stack<Expr>(), expr.getLeft());
- leftBlock.setRoot(leftChild);
- ctx.queryBlock.registerExprWithNode(expr.getLeft(), leftChild);
-
- LogicalPlan.QueryBlock rightBlock = ctx.plan.newQueryBlock();
- LogicalPlanner.PlanContext rightContext = new LogicalPlanner.PlanContext(ctx, rightBlock);
- LogicalNode rightChild = visit(rightContext, new Stack<Expr>(), expr.getRight());
- rightBlock.setRoot(rightChild);
- ctx.queryBlock.registerExprWithNode(expr.getRight(), rightChild);
-
- UnionNode unionNode = new UnionNode(ctx.plan.newPID());
- unionNode.setLeftChild(leftChild);
- unionNode.setRightChild(rightChild);
- unionNode.setInSchema(leftChild.getOutSchema());
- unionNode.setOutSchema(leftChild.getOutSchema());
- unionNode.setDistinct(expr.isDistinct());
-
- return unionNode;
- }
-
- public LogicalNode visitFilter(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Selection expr)
- throws TajoException {
- stack.push(expr);
- // Since filter push down will be done later, it is guaranteed that in-subqueries are found at only selection.
- for (Expr eachQual : PlannerUtil.extractInSubquery(expr.getQual())) {
- InPredicate inPredicate = (InPredicate) eachQual;
- stack.push(inPredicate);
- visit(ctx, stack, inPredicate.getRight());
- stack.pop();
- }
- LogicalNode child = visit(ctx, stack, expr.getChild());
- stack.pop();
-
- SelectionNode selectionNode = ctx.plan.createNode(SelectionNode.class);
- selectionNode.setInSchema(child.getOutSchema());
- selectionNode.setOutSchema(child.getOutSchema());
- return selectionNode;
- }
-
- @Override
- public LogicalNode visitJoin(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Join expr) throws TajoException {
- stack.push(expr);
- LogicalNode left = visit(ctx, stack, expr.getLeft());
- LogicalNode right = visit(ctx, stack, expr.getRight());
- stack.pop();
- JoinNode joinNode = ctx.plan.createNode(JoinNode.class);
- joinNode.setJoinType(expr.getJoinType());
- Schema merged = SchemaUtil.merge(left.getOutSchema(), right.getOutSchema());
- joinNode.setInSchema(merged);
- joinNode.setOutSchema(merged);
-
- ctx.queryBlock.addJoinType(expr.getJoinType());
- return joinNode;
- }
-
- @Override
- public LogicalNode visitRelation(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Relation expr)
- throws TajoException {
- Relation relation = expr;
-
- String actualRelationName;
- if (CatalogUtil.isFQTableName(expr.getName())) {
- actualRelationName = relation.getName();
- } else {
- actualRelationName =
- CatalogUtil.buildFQName(ctx.queryContext.get(SessionVars.CURRENT_DATABASE), relation.getName());
- }
-
- TableDesc desc = catalog.getTableDesc(actualRelationName);
-
- ScanNode scanNode = ctx.plan.createNode(ScanNode.class);
- if (relation.hasAlias()) {
- scanNode.init(desc, relation.getAlias());
- } else {
- scanNode.init(desc);
- }
-
- TablePropertyUtil.setTableProperty(ctx.getQueryContext(), scanNode);
-
- ctx.queryBlock.addRelation(scanNode);
- return scanNode;
- }
-
- @Override
- public LogicalNode visitTableSubQuery(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, TablePrimarySubQuery expr)
- throws TajoException {
-
- LogicalPlanner.PlanContext newContext;
- // Note: TableSubQuery always has a table name.
- // SELECT .... FROM (SELECT ...) TB_NAME <-
- QueryBlock queryBlock = ctx.plan.newQueryBlock();
- newContext = new LogicalPlanner.PlanContext(ctx, queryBlock);
- LogicalNode child = super.visitTableSubQuery(newContext, stack, expr);
- queryBlock.setRoot(child);
-
- // a table subquery should be dealt as a relation.
- TableSubQueryNode node = ctx.plan.createNode(TableSubQueryNode.class);
- node.init(CatalogUtil.buildFQName(ctx.queryContext.get(SessionVars.CURRENT_DATABASE), expr.getName()), child);
- ctx.queryBlock.addRelation(node);
-
- return node;
- }
-
- @Override
- public LogicalNode visitSimpleTableSubquery(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SimpleTableSubquery expr)
- throws TajoException {
- LogicalPlanner.PlanContext newContext;
- // Note: TableSubQuery always has a table name.
- // SELECT .... FROM (SELECT ...) TB_NAME <-
- QueryBlock queryBlock = ctx.plan.newQueryBlock();
- newContext = new LogicalPlanner.PlanContext(ctx, queryBlock);
- LogicalNode child = super.visitSimpleTableSubquery(newContext, stack, expr);
- queryBlock.setRoot(child);
-
- // a table subquery should be dealt as a relation.
- TableSubQueryNode node = ctx.plan.createNode(TableSubQueryNode.class);
- node.init(CatalogUtil.buildFQName(ctx.queryContext.get(SessionVars.CURRENT_DATABASE),
- ctx.generateUniqueSubQueryName()), child);
- ctx.queryBlock.addRelation(node);
- if (stack.peek().getType() == OpType.InPredicate) {
- // In-subquery and scalar subquery cannot be the base for name resolution.
- node.setNameResolveBase(false);
- }
- return node;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Data Definition Language Section
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- @Override
- public LogicalNode visitCreateDatabase(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, CreateDatabase expr)
- throws TajoException {
- CreateDatabaseNode createDatabaseNode = ctx.plan.createNode(CreateDatabaseNode.class);
- return createDatabaseNode;
- }
-
- @Override
- public LogicalNode visitDropDatabase(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, DropDatabase expr)
- throws TajoException {
- DropDatabaseNode dropDatabaseNode = ctx.plan.createNode(DropDatabaseNode.class);
- return dropDatabaseNode;
- }
-
- @Override
- public LogicalNode visitCreateTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, CreateTable expr)
- throws TajoException {
-
- CreateTableNode createTableNode = ctx.plan.createNode(CreateTableNode.class);
-
- if (expr.hasSubQuery()) {
- stack.push(expr);
- visit(ctx, stack, expr.getSubQuery());
- stack.pop();
- }
-
- return createTableNode;
- }
-
- @Override
- public LogicalNode visitDropTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, DropTable expr)
- throws TajoException {
- DropTableNode dropTable = ctx.plan.createNode(DropTableNode.class);
- return dropTable;
- }
-
- @Override
- public LogicalNode visitAlterTablespace(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, AlterTablespace expr)
- throws TajoException {
- AlterTablespaceNode alterTablespace = ctx.plan.createNode(AlterTablespaceNode.class);
- return alterTablespace;
- }
-
- @Override
- public LogicalNode visitAlterTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, AlterTable expr)
- throws TajoException {
- AlterTableNode alterTableNode = ctx.plan.createNode(AlterTableNode.class);
- return alterTableNode;
- }
-
- @Override
- public LogicalNode visitCreateIndex(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, CreateIndex expr)
- throws TajoException {
- stack.push(expr);
- LogicalNode child = visit(ctx, stack, expr.getChild());
- stack.pop();
-
- CreateIndexNode createIndex = ctx.plan.createNode(CreateIndexNode.class);
- createIndex.setInSchema(child.getOutSchema());
- createIndex.setOutSchema(child.getOutSchema());
- return createIndex;
- }
-
- @Override
- public LogicalNode visitDropIndex(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, DropIndex expr) {
- return ctx.plan.createNode(DropIndexNode.class);
- }
-
- public LogicalNode visitTruncateTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, TruncateTable expr)
- throws TajoException {
- TruncateTableNode truncateTableNode = ctx.plan.createNode(TruncateTableNode.class);
- return truncateTableNode;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Insert or Update Section
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- public LogicalNode visitInsert(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Insert expr)
- throws TajoException {
- LogicalNode child = super.visitInsert(ctx, stack, expr);
-
- InsertNode insertNode = new InsertNode(ctx.plan.newPID());
- insertNode.setInSchema(child.getOutSchema());
- insertNode.setOutSchema(child.getOutSchema());
- return insertNode;
+ provider = new BaseLogicalPlanPreprocessPhaseProvider();
+ engine = new BaseLogicalPlanPreprocessEngine(catalog, annotator);
+ engine.addProcessPhase(provider.getPhases());
}
- static class NameRefInSelectListNormalizer extends SimpleAlgebraVisitor<LogicalPlanner.PlanContext, Object> {
- private static final NameRefInSelectListNormalizer instance;
-
- static {
- instance = new NameRefInSelectListNormalizer();
- }
-
- public static void normalize(LogicalPlanner.PlanContext context, Expr expr) throws TajoException {
- NameRefInSelectListNormalizer normalizer = new NameRefInSelectListNormalizer();
- normalizer.visit(context,new Stack<Expr>(), expr);
- }
-
- @Override
- public Expr visitColumnReference(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, ColumnReferenceExpr expr)
- throws TajoException {
-
- String normalized = NameResolver.resolve(ctx.plan, ctx.queryBlock, expr,
- NameResolvingMode.RELS_ONLY).getQualifiedName();
- expr.setName(normalized);
-
- return expr;
- }
+ public LogicalNode process(PlanContext context, Expr expr) throws TajoException {
+ return engine.process(context, expr);
}
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
index 24dcfd5..4e74862 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
@@ -126,6 +126,10 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
return queryBlock;
}
+ public LogicalPlan getPlan() {
+ return plan;
+ }
+
public OverridableConf getQueryContext() {
return queryContext;
}
@@ -160,7 +164,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
QueryBlock rootBlock = plan.newAndGetBlock(LogicalPlan.ROOT_BLOCK);
PlanContext context = new PlanContext(queryContext, plan, rootBlock, evalOptimizer, debug);
- preprocessor.visit(context, new Stack<Expr>(), expr);
+ preprocessor.process(context, expr);
plan.resetGeneratedId();
LogicalNode topMostNode = this.visit(context, new Stack<Expr>(), expr);
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessEngine.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessEngine.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessEngine.java
new file mode 100644
index 0000000..f02faef
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessEngine.java
@@ -0,0 +1,100 @@
+/**
+ * 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.plan.rewrite;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.catalog.CatalogService;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.plan.ExprAnnotator;
+import org.apache.tajo.plan.LogicalPlanner.PlanContext;
+import org.apache.tajo.plan.logical.LogicalNode;
+
+import java.lang.reflect.Constructor;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class BaseLogicalPlanPreprocessEngine implements LogicalPlanPreprocessEngine {
+ private final CatalogService catalogService;
+ private final ExprAnnotator exprAnnotator;
+
+ public BaseLogicalPlanPreprocessEngine(CatalogService catalogService, ExprAnnotator exprAnnotator) {
+ this.catalogService = catalogService;
+ this.exprAnnotator = exprAnnotator;
+ }
+
+ /** class logger */
+ private Log LOG = LogFactory.getLog(BaseLogicalPlanPreprocessEngine.class);
+
+ /** a map for pre-process phases */
+ private Map<String, LogicalPlanPreprocessPhase> preprocessPhases = new LinkedHashMap<>();
+
+ /**
+ * Add a pre-process phase to this engine.
+ *
+ * @param phases pre-process phase
+ */
+ public void addProcessPhase(Iterable<Class<? extends LogicalPlanPreprocessPhase>> phases) {
+ for (Class<? extends LogicalPlanPreprocessPhase> clazz : phases) {
+ try {
+ Constructor cons = clazz.getConstructor(CatalogService.class, ExprAnnotator.class);
+ LogicalPlanPreprocessPhase rule = (LogicalPlanPreprocessPhase) cons.newInstance(catalogService, exprAnnotator);
+ addProcessPhase(rule);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ /**
+ * Add a pre-process phase to this engine.
+ *
+ * @param phase The pre-process phase to be added to this engine.
+ */
+ public void addProcessPhase(LogicalPlanPreprocessPhase phase) {
+ if (!preprocessPhases.containsKey(phase.getName())) {
+ preprocessPhases.put(phase.getName(), phase);
+ }
+ }
+
+ /**
+ * Do every pre-process phase added to this engine.
+ *
+ * @param context
+ * @return The rewritten logical node.
+ */
+ @Override
+ public LogicalNode process(PlanContext context, Expr expr) throws TajoException {
+ LogicalPlanPreprocessPhase rule;
+ LogicalNode node = null;
+ for (Entry<String, LogicalPlanPreprocessPhase> preprocessPhase : preprocessPhases.entrySet()) {
+ rule = preprocessPhase.getValue();
+ if (rule.isEligible(context, expr)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("The rule \"" + rule.getName() + " \" rewrites the query.");
+ }
+ node = rule.process(context, expr);
+ }
+ }
+
+ return node;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessPhaseProvider.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessPhaseProvider.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessPhaseProvider.java
new file mode 100644
index 0000000..92af709
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanPreprocessPhaseProvider.java
@@ -0,0 +1,34 @@
+/**
+ * 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.plan.rewrite;
+
+import org.apache.tajo.util.TUtil;
+
+import java.util.Collection;
+import java.util.List;
+
+public class BaseLogicalPlanPreprocessPhaseProvider extends LogicalPlanPreprocessPhaseProvider {
+ @Override
+ public Collection<Class<? extends LogicalPlanPreprocessPhase>> getPhases() {
+ List phases = TUtil.newList(
+ BaseSchemaBuildPhase.class
+ );
+ return phases;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteEngine.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteEngine.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteEngine.java
index 9a541d9..35ec85c 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteEngine.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteEngine.java
@@ -37,7 +37,7 @@ public class BaseLogicalPlanRewriteEngine implements LogicalPlanRewriteEngine {
private Log LOG = LogFactory.getLog(BaseLogicalPlanRewriteEngine.class);
/** a map for query rewrite rules */
- private Map<String, LogicalPlanRewriteRule> rewriteRules = new LinkedHashMap<String, LogicalPlanRewriteRule>();
+ private Map<String, LogicalPlanRewriteRule> rewriteRules = new LinkedHashMap<>();
/**
* Add a query rewrite rule to this engine.
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseSchemaBuildPhase.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseSchemaBuildPhase.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseSchemaBuildPhase.java
new file mode 100644
index 0000000..81e9b2c
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseSchemaBuildPhase.java
@@ -0,0 +1,591 @@
+/**
+ * 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.plan.rewrite;
+
+import org.apache.tajo.SessionVars;
+import org.apache.tajo.algebra.*;
+import org.apache.tajo.catalog.*;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.exception.UndefinedColumnException;
+import org.apache.tajo.plan.*;
+import org.apache.tajo.plan.LogicalPlan.QueryBlock;
+import org.apache.tajo.plan.LogicalPlanner.PlanContext;
+import org.apache.tajo.plan.algebra.BaseAlgebraVisitor;
+import org.apache.tajo.plan.expr.ConstEval;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.plan.expr.FieldEval;
+import org.apache.tajo.plan.logical.*;
+import org.apache.tajo.plan.nameresolver.NameResolver;
+import org.apache.tajo.plan.nameresolver.NameResolvingMode;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.visitor.SimpleAlgebraVisitor;
+import org.apache.tajo.util.TUtil;
+
+import java.util.*;
+
+public class BaseSchemaBuildPhase extends LogicalPlanPreprocessPhase {
+
+ private final Processor processor;
+
+ public BaseSchemaBuildPhase(CatalogService catalog, ExprAnnotator annotator) {
+ super(catalog, annotator);
+ processor = new Processor(catalog, annotator);
+ }
+
+ @Override
+ public String getName() {
+ return "Base schema build phase";
+ }
+
+ @Override
+ public boolean isEligible(PlanContext context, Expr expr) {
+ return true;
+ }
+
+ @Override
+ public LogicalNode process(PlanContext context, Expr expr) throws TajoException {
+ return processor.visit(context, new Stack<Expr>(), expr);
+ }
+
+ static class Processor extends BaseAlgebraVisitor<PlanContext, LogicalNode> {
+ private TypeDeterminant typeDeterminant;
+ private ExprAnnotator annotator;
+
+ /** Catalog service */
+ private CatalogService catalog;
+
+ Processor(CatalogService catalog, ExprAnnotator annotator) {
+ this.catalog = catalog;
+ this.annotator = annotator;
+ this.typeDeterminant = new TypeDeterminant(catalog);
+ }
+
+ @Override
+ public void preHook(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Expr expr) throws TajoException {
+ ctx.getQueryBlock().setAlgebraicExpr(expr);
+ ctx.getPlan().mapExprToBlock(expr, ctx.getQueryBlock().getName());
+ }
+
+ @Override
+ public LogicalNode postHook(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Expr expr, LogicalNode result)
+ throws TajoException {
+ // If non-from statement, result can be null. It avoids that case.
+ if (result != null) {
+ // setNode method registers each node to corresponding block and plan.
+ ctx.getQueryBlock().registerNode(result);
+ // It makes a map between an expr and a logical node.
+ ctx.getQueryBlock().registerExprWithNode(expr, result);
+ }
+ return result;
+ }
+
+ /**
+ * Get all columns of the relations correspondent to the asterisk expression.
+ * @param ctx
+ * @param asteriskExpr
+ * @return array of columns
+ * @throws TajoException
+ */
+ public static Column[] getColumns(LogicalPlanner.PlanContext ctx, QualifiedAsteriskExpr asteriskExpr)
+ throws TajoException {
+ RelationNode relationOp = null;
+ QueryBlock block = ctx.getQueryBlock();
+ Collection<QueryBlock> queryBlocks = ctx.getPlan().getQueryBlocks();
+ if (asteriskExpr.hasQualifier()) {
+ String qualifier;
+
+ if (CatalogUtil.isFQTableName(asteriskExpr.getQualifier())) {
+ qualifier = asteriskExpr.getQualifier();
+ } else {
+ qualifier = CatalogUtil.buildFQName(
+ ctx.getQueryContext().get(SessionVars.CURRENT_DATABASE), asteriskExpr.getQualifier());
+ }
+
+ relationOp = block.getRelation(qualifier);
+
+ // if a column name is outside of this query block
+ if (relationOp == null) {
+ // TODO - nested query can only refer outer query block? or not?
+ for (QueryBlock eachBlock : queryBlocks) {
+ if (eachBlock.existsRelation(qualifier)) {
+ relationOp = eachBlock.getRelation(qualifier);
+ }
+ }
+ }
+
+ // If we cannot find any relation against a qualified column name
+ if (relationOp == null) {
+ throw new UndefinedColumnException(CatalogUtil.buildFQName(qualifier, "*"));
+ }
+
+ Schema schema = relationOp.getLogicalSchema();
+ Column[] resolvedColumns = new Column[schema.size()];
+ return schema.getRootColumns().toArray(resolvedColumns);
+ } else { // if a column reference is not qualified
+ // columns of every relation should be resolved.
+ Iterator<RelationNode> iterator = block.getRelations().iterator();
+ Schema schema;
+ List<Column> resolvedColumns = TUtil.newList();
+
+ while (iterator.hasNext()) {
+ relationOp = iterator.next();
+ if (relationOp.isNameResolveBase()) {
+ schema = relationOp.getLogicalSchema();
+ resolvedColumns.addAll(schema.getRootColumns());
+ }
+ }
+
+ if (resolvedColumns.size() == 0) {
+ throw new UndefinedColumnException(asteriskExpr.toString());
+ }
+
+ return resolvedColumns.toArray(new Column[resolvedColumns.size()]);
+ }
+ }
+
+ /**
+ * Resolve an asterisk expression to the real column reference expressions.
+ * @param ctx context
+ * @param asteriskExpr asterisk expression
+ * @return a list of NamedExpr each of which has ColumnReferenceExprs as its child
+ * @throws TajoException
+ */
+ private static List<NamedExpr> resolveAsterisk(LogicalPlanner.PlanContext ctx, QualifiedAsteriskExpr asteriskExpr)
+ throws TajoException {
+ Column[] columns = getColumns(ctx, asteriskExpr);
+ List<NamedExpr> newTargetExprs = new ArrayList<NamedExpr>(columns.length);
+ int i;
+ for (i = 0; i < columns.length; i++) {
+ newTargetExprs.add(new NamedExpr(new ColumnReferenceExpr(columns[i].getQualifier(), columns[i].getSimpleName())));
+ }
+ return newTargetExprs;
+ }
+
+ private static boolean hasAsterisk(NamedExpr [] namedExprs) {
+ for (NamedExpr eachTarget : namedExprs) {
+ if (eachTarget.getExpr().getType() == OpType.Asterisk) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static NamedExpr [] voidResolveAsteriskNamedExpr(LogicalPlanner.PlanContext context,
+ NamedExpr [] namedExprs) throws TajoException {
+ List<NamedExpr> rewrittenTargets = TUtil.newList();
+ for (NamedExpr originTarget : namedExprs) {
+ if (originTarget.getExpr().getType() == OpType.Asterisk) {
+ // rewrite targets
+ rewrittenTargets.addAll(resolveAsterisk(context, (QualifiedAsteriskExpr) originTarget.getExpr()));
+ } else {
+ rewrittenTargets.add(originTarget);
+ }
+ }
+ return rewrittenTargets.toArray(new NamedExpr[rewrittenTargets.size()]);
+ }
+
+ @Override
+ public LogicalNode visitSetSession(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SetSession expr)
+ throws TajoException {
+ SetSessionNode setSession = ctx.getPlan().createNode(SetSessionNode.class);
+ return setSession;
+ }
+
+ @Override
+ public LogicalNode visitProjection(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Projection expr)
+ throws TajoException {
+ // If Non-from statement, it immediately returns.
+ if (!expr.hasChild()) {
+ EvalExprNode exprNode = ctx.getPlan().createNode(EvalExprNode.class);
+ exprNode.setTargets(buildTargets(ctx, expr.getNamedExprs()));
+ return exprNode;
+ }
+
+ stack.push(expr); // <--- push
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+
+ // Resolve the asterisk expression
+ if (hasAsterisk(expr.getNamedExprs())) {
+ expr.setNamedExprs(voidResolveAsteriskNamedExpr(ctx, expr.getNamedExprs()));
+ }
+
+ NamedExpr[] projectTargetExprs = expr.getNamedExprs();
+ for (int i = 0; i < expr.getNamedExprs().length; i++) {
+ NamedExpr namedExpr = projectTargetExprs[i];
+
+ // 1) Normalize all field names occurred in each expr into full qualified names
+ NameRefInSelectListNormalizer.normalize(ctx, namedExpr.getExpr());
+
+ // 2) Register explicit column aliases to block
+ if (namedExpr.getExpr().getType() == OpType.Column && namedExpr.hasAlias()) {
+ ctx.getQueryBlock().addColumnAlias(((ColumnReferenceExpr)namedExpr.getExpr()).getCanonicalName(),
+ namedExpr.getAlias());
+ } else if (OpType.isLiteralType(namedExpr.getExpr().getType()) && namedExpr.hasAlias()) {
+ Expr constExpr = namedExpr.getExpr();
+ ConstEval constEval = (ConstEval) annotator.createEvalNode(ctx, constExpr, NameResolvingMode.RELS_ONLY);
+ ctx.getQueryBlock().addConstReference(namedExpr.getAlias(), constExpr, constEval);
+ }
+ }
+
+ Target[] targets = buildTargets(ctx, expr.getNamedExprs());
+
+ stack.pop(); // <--- Pop
+
+ ProjectionNode projectionNode = ctx.getPlan().createNode(ProjectionNode.class);
+ projectionNode.setInSchema(child.getOutSchema());
+ projectionNode.setOutSchema(PlannerUtil.targetToSchema(targets));
+
+ ctx.getQueryBlock().setSchema(projectionNode.getOutSchema());
+ return projectionNode;
+ }
+
+ private Target [] buildTargets(LogicalPlanner.PlanContext context, NamedExpr [] exprs) throws TajoException {
+ Target [] targets = new Target[exprs.length];
+ for (int i = 0; i < exprs.length; i++) {
+ NamedExpr namedExpr = exprs[i];
+ TajoDataTypes.DataType dataType = typeDeterminant.determineDataType(context, namedExpr.getExpr());
+
+ if (namedExpr.hasAlias()) {
+ targets[i] = new Target(new FieldEval(new Column(namedExpr.getAlias(), dataType)));
+ } else {
+ String generatedName = context.getPlan().generateUniqueColumnName(namedExpr.getExpr());
+ targets[i] = new Target(new FieldEval(new Column(generatedName, dataType)));
+ }
+ }
+ return targets;
+ }
+
+ @Override
+ public LogicalNode visitLimit(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Limit expr)
+ throws TajoException {
+ stack.push(expr);
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+
+ LimitNode limitNode = ctx.getPlan().createNode(LimitNode.class);
+ limitNode.setInSchema(child.getOutSchema());
+ limitNode.setOutSchema(child.getOutSchema());
+ return limitNode;
+ }
+
+ @Override
+ public LogicalNode visitSort(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Sort expr) throws TajoException {
+ stack.push(expr);
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+
+ SortNode sortNode = ctx.getPlan().createNode(SortNode.class);
+ sortNode.setInSchema(child.getOutSchema());
+ sortNode.setOutSchema(child.getOutSchema());
+ return sortNode;
+ }
+
+ @Override
+ public LogicalNode visitHaving(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Having expr)
+ throws TajoException {
+ stack.push(expr);
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+
+ HavingNode havingNode = ctx.getPlan().createNode(HavingNode.class);
+ havingNode.setInSchema(child.getOutSchema());
+ havingNode.setOutSchema(child.getOutSchema());
+ return havingNode;
+ }
+
+ @Override
+ public LogicalNode visitGroupBy(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Aggregation expr)
+ throws TajoException {
+ stack.push(expr); // <--- push
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+
+ Projection projection = ctx.getQueryBlock().getSingletonExpr(OpType.Projection);
+ int finalTargetNum = projection.getNamedExprs().length;
+ Target [] targets = new Target[finalTargetNum];
+
+ if (hasAsterisk(projection.getNamedExprs())) {
+ projection.setNamedExprs(voidResolveAsteriskNamedExpr(ctx, projection.getNamedExprs()));
+ }
+
+ for (int i = 0; i < finalTargetNum; i++) {
+ NamedExpr namedExpr = projection.getNamedExprs()[i];
+ EvalNode evalNode = annotator.createEvalNode(ctx, namedExpr.getExpr(), NameResolvingMode.SUBEXPRS_AND_RELS);
+
+ if (namedExpr.hasAlias()) {
+ targets[i] = new Target(evalNode, namedExpr.getAlias());
+ } else {
+ targets[i] = new Target(evalNode, "?name_" + i);
+ }
+ }
+ stack.pop();
+
+ GroupbyNode groupByNode = ctx.getPlan().createNode(GroupbyNode.class);
+ groupByNode.setInSchema(child.getOutSchema());
+ groupByNode.setOutSchema(PlannerUtil.targetToSchema(targets));
+ return groupByNode;
+ }
+
+ @Override
+ public LogicalNode visitUnion(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SetOperation expr)
+ throws TajoException {
+ LogicalPlan.QueryBlock leftBlock = ctx.getPlan().newQueryBlock();
+ LogicalPlanner.PlanContext leftContext = new LogicalPlanner.PlanContext(ctx, leftBlock);
+ LogicalNode leftChild = visit(leftContext, new Stack<Expr>(), expr.getLeft());
+ leftBlock.setRoot(leftChild);
+ ctx.getQueryBlock().registerExprWithNode(expr.getLeft(), leftChild);
+
+ LogicalPlan.QueryBlock rightBlock = ctx.getPlan().newQueryBlock();
+ LogicalPlanner.PlanContext rightContext = new LogicalPlanner.PlanContext(ctx, rightBlock);
+ LogicalNode rightChild = visit(rightContext, new Stack<Expr>(), expr.getRight());
+ rightBlock.setRoot(rightChild);
+ ctx.getQueryBlock().registerExprWithNode(expr.getRight(), rightChild);
+
+ UnionNode unionNode = new UnionNode(ctx.getPlan().newPID());
+ unionNode.setLeftChild(leftChild);
+ unionNode.setRightChild(rightChild);
+ unionNode.setInSchema(leftChild.getOutSchema());
+ unionNode.setOutSchema(leftChild.getOutSchema());
+ unionNode.setDistinct(expr.isDistinct());
+
+ return unionNode;
+ }
+
+ public LogicalNode visitFilter(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Selection expr)
+ throws TajoException {
+ stack.push(expr);
+ // Since filter push down will be done later, it is guaranteed that in-subqueries are found at only selection.
+ for (Expr eachQual : PlannerUtil.extractInSubquery(expr.getQual())) {
+ InPredicate inPredicate = (InPredicate) eachQual;
+ stack.push(inPredicate);
+ visit(ctx, stack, inPredicate.getRight());
+ stack.pop();
+ }
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+
+ SelectionNode selectionNode = ctx.getPlan().createNode(SelectionNode.class);
+ selectionNode.setInSchema(child.getOutSchema());
+ selectionNode.setOutSchema(child.getOutSchema());
+ return selectionNode;
+ }
+
+ @Override
+ public LogicalNode visitJoin(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Join expr) throws TajoException {
+ stack.push(expr);
+ LogicalNode left = visit(ctx, stack, expr.getLeft());
+ LogicalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+ JoinNode joinNode = ctx.getPlan().createNode(JoinNode.class);
+ joinNode.setJoinType(expr.getJoinType());
+ Schema merged = SchemaUtil.merge(left.getOutSchema(), right.getOutSchema());
+ joinNode.setInSchema(merged);
+ joinNode.setOutSchema(merged);
+
+ ctx.getQueryBlock().addJoinType(expr.getJoinType());
+ return joinNode;
+ }
+
+ @Override
+ public LogicalNode visitRelation(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Relation relation)
+ throws TajoException {
+
+ String actualRelationName;
+ if (CatalogUtil.isFQTableName(relation.getName())) {
+ actualRelationName = relation.getName();
+ } else {
+ actualRelationName =
+ CatalogUtil.buildFQName(ctx.getQueryContext().get(SessionVars.CURRENT_DATABASE), relation.getName());
+ }
+
+ TableDesc desc = catalog.getTableDesc(actualRelationName);
+
+ ScanNode scanNode = ctx.getPlan().createNode(ScanNode.class);
+ if (relation.hasAlias()) {
+ scanNode.init(desc, relation.getAlias());
+ } else {
+ scanNode.init(desc);
+ }
+
+ TablePropertyUtil.setTableProperty(ctx.getQueryContext(), scanNode);
+
+ ctx.getQueryBlock().addRelation(scanNode);
+ return scanNode;
+ }
+
+ @Override
+ public LogicalNode visitTableSubQuery(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, TablePrimarySubQuery expr)
+ throws TajoException {
+
+ LogicalPlanner.PlanContext newContext;
+ // Note: TableSubQuery always has a table name.
+ // SELECT .... FROM (SELECT ...) TB_NAME <-
+ QueryBlock queryBlock = ctx.getPlan().newQueryBlock();
+ newContext = new LogicalPlanner.PlanContext(ctx, queryBlock);
+ LogicalNode child = super.visitTableSubQuery(newContext, stack, expr);
+ queryBlock.setRoot(child);
+
+ // a table subquery should be dealt as a relation.
+ TableSubQueryNode node = ctx.getPlan().createNode(TableSubQueryNode.class);
+ node.init(CatalogUtil.buildFQName(ctx.getQueryContext().get(SessionVars.CURRENT_DATABASE), expr.getName()), child);
+ ctx.getQueryBlock().addRelation(node);
+
+ return node;
+ }
+
+ @Override
+ public LogicalNode visitSimpleTableSubquery(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SimpleTableSubquery expr)
+ throws TajoException {
+ LogicalPlanner.PlanContext newContext;
+ // Note: TableSubQuery always has a table name.
+ // SELECT .... FROM (SELECT ...) TB_NAME <-
+ QueryBlock queryBlock = ctx.getPlan().newQueryBlock();
+ newContext = new LogicalPlanner.PlanContext(ctx, queryBlock);
+ LogicalNode child = super.visitSimpleTableSubquery(newContext, stack, expr);
+ queryBlock.setRoot(child);
+
+ // a table subquery should be dealt as a relation.
+ TableSubQueryNode node = ctx.getPlan().createNode(TableSubQueryNode.class);
+ node.init(CatalogUtil.buildFQName(ctx.getQueryContext().get(SessionVars.CURRENT_DATABASE),
+ ctx.generateUniqueSubQueryName()), child);
+ ctx.getQueryBlock().addRelation(node);
+ if (stack.peek().getType() == OpType.InPredicate) {
+ // In-subquery and scalar subquery cannot be the base for name resolution.
+ node.setNameResolveBase(false);
+ }
+ return node;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Data Definition Language Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public LogicalNode visitCreateDatabase(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, CreateDatabase expr)
+ throws TajoException {
+ CreateDatabaseNode createDatabaseNode = ctx.getPlan().createNode(CreateDatabaseNode.class);
+ return createDatabaseNode;
+ }
+
+ @Override
+ public LogicalNode visitDropDatabase(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, DropDatabase expr)
+ throws TajoException {
+ DropDatabaseNode dropDatabaseNode = ctx.getPlan().createNode(DropDatabaseNode.class);
+ return dropDatabaseNode;
+ }
+
+ @Override
+ public LogicalNode visitCreateTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, CreateTable expr)
+ throws TajoException {
+
+ CreateTableNode createTableNode = ctx.getPlan().createNode(CreateTableNode.class);
+
+ if (expr.hasSubQuery()) {
+ stack.push(expr);
+ visit(ctx, stack, expr.getSubQuery());
+ stack.pop();
+ }
+
+ return createTableNode;
+ }
+
+ @Override
+ public LogicalNode visitDropTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, DropTable expr)
+ throws TajoException {
+ DropTableNode dropTable = ctx.getPlan().createNode(DropTableNode.class);
+ return dropTable;
+ }
+
+ @Override
+ public LogicalNode visitAlterTablespace(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, AlterTablespace expr)
+ throws TajoException {
+ AlterTablespaceNode alterTablespace = ctx.getPlan().createNode(AlterTablespaceNode.class);
+ return alterTablespace;
+ }
+
+ @Override
+ public LogicalNode visitAlterTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, AlterTable expr)
+ throws TajoException {
+ AlterTableNode alterTableNode = ctx.getPlan().createNode(AlterTableNode.class);
+ return alterTableNode;
+ }
+
+ @Override
+ public LogicalNode visitCreateIndex(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, CreateIndex expr)
+ throws TajoException {
+ stack.push(expr);
+ LogicalNode child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+
+ CreateIndexNode createIndex = ctx.getPlan().createNode(CreateIndexNode.class);
+ createIndex.setInSchema(child.getOutSchema());
+ createIndex.setOutSchema(child.getOutSchema());
+ return createIndex;
+ }
+
+ @Override
+ public LogicalNode visitDropIndex(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, DropIndex expr) {
+ return ctx.getPlan().createNode(DropIndexNode.class);
+ }
+
+ public LogicalNode visitTruncateTable(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, TruncateTable expr)
+ throws TajoException {
+ TruncateTableNode truncateTableNode = ctx.getPlan().createNode(TruncateTableNode.class);
+ return truncateTableNode;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Insert or Update Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public LogicalNode visitInsert(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Insert expr)
+ throws TajoException {
+ LogicalNode child = super.visitInsert(ctx, stack, expr);
+
+ InsertNode insertNode = new InsertNode(ctx.getPlan().newPID());
+ insertNode.setInSchema(child.getOutSchema());
+ insertNode.setOutSchema(child.getOutSchema());
+ return insertNode;
+ }
+
+ static class NameRefInSelectListNormalizer extends SimpleAlgebraVisitor<PlanContext, Object> {
+ private static final NameRefInSelectListNormalizer instance;
+
+ static {
+ instance = new NameRefInSelectListNormalizer();
+ }
+
+ public static void normalize(LogicalPlanner.PlanContext context, Expr expr) throws TajoException {
+ NameRefInSelectListNormalizer normalizer = new NameRefInSelectListNormalizer();
+ normalizer.visit(context,new Stack<Expr>(), expr);
+ }
+
+ @Override
+ public Expr visitColumnReference(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, ColumnReferenceExpr expr)
+ throws TajoException {
+
+ String normalized = NameResolver.resolve(ctx.getPlan(), ctx.getQueryBlock(), expr,
+ NameResolvingMode.RELS_ONLY).getQualifiedName();
+ expr.setName(normalized);
+
+ return expr;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessEngine.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessEngine.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessEngine.java
new file mode 100644
index 0000000..f5cf7ab
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessEngine.java
@@ -0,0 +1,28 @@
+/**
+ * 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.plan.rewrite;
+
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.plan.LogicalPlanner.PlanContext;
+import org.apache.tajo.plan.logical.LogicalNode;
+
+public interface LogicalPlanPreprocessEngine {
+ LogicalNode process(PlanContext planContext, Expr expr) throws TajoException;
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhase.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhase.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhase.java
new file mode 100644
index 0000000..21f8b5c
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhase.java
@@ -0,0 +1,64 @@
+/**
+ * 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.plan.rewrite;
+
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.catalog.CatalogService;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.plan.ExprAnnotator;
+import org.apache.tajo.plan.LogicalPlanner.PlanContext;
+import org.apache.tajo.plan.logical.LogicalNode;
+
+public abstract class LogicalPlanPreprocessPhase {
+
+ protected final CatalogService catalog;
+ protected final ExprAnnotator annotator;
+
+ public LogicalPlanPreprocessPhase(CatalogService catalog, ExprAnnotator annotator) {
+ this.catalog = catalog;
+ this.annotator = annotator;
+ }
+ /**
+ * It returns the pre-process phase name. It will be used for debugging and
+ * building a optimization history.
+ *
+ * @return The pre-process phase name
+ */
+ public abstract String getName();
+
+ /**
+ * This method checks if this pre-process phase can be applied to the given expression tree.
+ *
+ * @param context
+ * @param expr
+ * @return
+ */
+ public abstract boolean isEligible(PlanContext context, Expr expr);
+
+ /**
+ * Do a pre-process phase for an expression tree and returns it.
+ * It must be guaranteed that the input expression tree is not modified even after rewrite.
+ * In other words, the rewrite has to modify a copy of the expression tree.
+ *
+ * @param context
+ * @param expr
+ * @return The rewritten logical plan.
+ */
+ public abstract LogicalNode process(PlanContext context, Expr expr) throws TajoException;
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhaseProvider.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhaseProvider.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhaseProvider.java
new file mode 100644
index 0000000..a59f1d8
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanPreprocessPhaseProvider.java
@@ -0,0 +1,30 @@
+/**
+ * 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.plan.rewrite;
+
+import java.util.Collection;
+
+public abstract class LogicalPlanPreprocessPhaseProvider {
+ /**
+ * It returns LogicalPlanPreprocessPhase classes.
+ *
+ * @return LogicalPlanPreprocessPhase classes
+ */
+ public abstract Collection<Class<? extends LogicalPlanPreprocessPhase>> getPhases();
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteEngine.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteEngine.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteEngine.java
index a621921..f70dfe0 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteEngine.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteEngine.java
@@ -25,7 +25,7 @@ public interface LogicalPlanRewriteEngine {
/**
* Rewrite a logical plan with all query rewrite rules added to this engine.
*
- * @param plan The plan to be rewritten with all query rewrite rule.
+ * @param context The context containing query context, logical plan, and catalog.
* @return The rewritten plan.
*/
LogicalPlan rewrite(LogicalPlanRewriteRuleContext context) throws TajoException;
http://git-wip-us.apache.org/repos/asf/tajo/blob/508d17a2/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteRule.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteRule.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteRule.java
index 6643d28..9fbc7af 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteRule.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/LogicalPlanRewriteRule.java
@@ -39,7 +39,7 @@ public interface LogicalPlanRewriteRule {
* For example, the selection push down can not be applied to the query plan without any filter.
* In such case, it will return false.
*
- * @param plan The plan to be checked
+ * @param context rewrite rule context.
* @return True if this rule can be applied to a given plan. Otherwise, false.
*/
boolean isEligible(LogicalPlanRewriteRuleContext context);
@@ -49,7 +49,7 @@ public interface LogicalPlanRewriteRule {
* It must be guaranteed that the input logical plan is not modified even after rewrite.
* In other words, the rewrite has to modify an plan copied from the input plan.
*
- * @param plan Input logical plan. It will not be modified.
+ * @param context rewrite rule context.
* @return The rewritten logical plan.
*/
LogicalPlan rewrite(LogicalPlanRewriteRuleContext context) throws TajoException;