| /** |
| * 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.NotImplementedException; |
| 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.schema.IdentifierUtil; |
| |
| import java.util.*; |
| |
| import static org.apache.tajo.common.TajoDataTypes.Type.RECORD; |
| |
| /** |
| * BaseSchemaBuildPhase builds a basic schema information of tables which have pre-defined schema. |
| * For example, tables like the below example have pre-defined schema. |
| * |
| * CREATE TABLE t1 (id int8, name text); |
| * CREATE EXTERNAL TABLE t2 (id int8, score int8, dept text); |
| */ |
| 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); |
| } |
| |
| 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 (IdentifierUtil.isFQTableName(asteriskExpr.getQualifier())) { |
| qualifier = asteriskExpr.getQualifier(); |
| } else { |
| qualifier = IdentifierUtil.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(IdentifierUtil.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 = new ArrayList<>(); |
| |
| 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<>(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 List<NamedExpr> voidResolveAsteriskNamedExpr(LogicalPlanner.PlanContext context, |
| List<NamedExpr> namedExprs) throws TajoException { |
| List<NamedExpr> rewrittenTargets = new ArrayList<>(); |
| 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; |
| } |
| |
| @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 (PlannerUtil.hasAsterisk(expr.getNamedExprs())) { |
| expr.setNamedExprs(voidResolveAsteriskNamedExpr(ctx, expr.getNamedExprs())); |
| } |
| |
| List<NamedExpr> projectTargetExprs = expr.getNamedExprs(); |
| for (int i = 0; i < expr.getNamedExprs().size(); i++) { |
| NamedExpr namedExpr = projectTargetExprs.get(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, true); |
| ctx.getQueryBlock().addConstReference(namedExpr.getAlias(), constExpr, constEval); |
| } |
| } |
| |
| List<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 List<Target> buildTargets(LogicalPlanner.PlanContext context, List<NamedExpr> exprs) throws TajoException { |
| List<Target> targets = new ArrayList<>(); |
| for (NamedExpr namedExpr : exprs) { |
| TajoDataTypes.DataType dataType = typeDeterminant.determineDataType(context, namedExpr.getExpr()); |
| |
| if (dataType.getType() == RECORD) { |
| throw new NotImplementedException("record projection"); |
| } |
| |
| if (namedExpr.hasAlias()) { |
| targets.add(new Target(new FieldEval(new Column(namedExpr.getAlias(), dataType)))); |
| } else { |
| String generatedName = context.getPlan().generateUniqueColumnName(namedExpr.getExpr()); |
| targets.add(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().size(); |
| List<Target> targets = new ArrayList<>(); |
| |
| if (PlannerUtil.hasAsterisk(projection.getNamedExprs())) { |
| projection.setNamedExprs(voidResolveAsteriskNamedExpr(ctx, projection.getNamedExprs())); |
| } |
| |
| for (int i = 0; i < finalTargetNum; i++) { |
| NamedExpr namedExpr = projection.getNamedExprs().get(i); |
| EvalNode evalNode = annotator.createEvalNode(ctx, namedExpr.getExpr(), NameResolvingMode.SUBEXPRS_AND_RELS, true); |
| |
| if (namedExpr.hasAlias()) { |
| targets.add(new Target(evalNode, namedExpr.getAlias())); |
| } else { |
| targets.add(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.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.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 (IdentifierUtil.isFQTableName(relation.getName())) { |
| actualRelationName = relation.getName(); |
| } else { |
| actualRelationName = |
| IdentifierUtil.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(IdentifierUtil.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(IdentifierUtil.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; |
| } |
| |
| public 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); |
| } |
| |
| @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, true).getQualifiedName(); |
| expr.setName(normalized); |
| |
| return expr; |
| } |
| } |
| } |
| } |