/* * Lisensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tajo.plan.serder; import com.google.common.collect.Maps; import org.apache.hadoop.fs.Path; import org.apache.tajo.algebra.JoinType; import org.apache.tajo.catalog.SortSpec; import org.apache.tajo.catalog.proto.CatalogProtos; import org.apache.tajo.common.ProtoObject; import org.apache.tajo.exception.TajoException; import org.apache.tajo.exception.TajoInternalError; import org.apache.tajo.exception.NotImplementedException; import org.apache.tajo.exception.TajoRuntimeException; import org.apache.tajo.plan.LogicalPlan; import org.apache.tajo.plan.Target; import org.apache.tajo.plan.expr.EvalNode; import org.apache.tajo.plan.logical.*; import org.apache.tajo.plan.rewrite.rules.IndexScanInfo.SimplePredicate; import org.apache.tajo.plan.serder.PlanProto.AlterTableNode.AddColumn; import org.apache.tajo.plan.serder.PlanProto.AlterTableNode.RenameColumn; import org.apache.tajo.plan.serder.PlanProto.AlterTableNode.RenameTable; import org.apache.tajo.plan.serder.PlanProto.AlterTablespaceNode.SetLocation; import org.apache.tajo.plan.serder.PlanProto.LogicalNodeTree; import org.apache.tajo.plan.visitor.BasicLogicalPlanVisitor; import org.apache.tajo.util.ProtoUtil; import java.util.*; /** * It serializes a logical plan into a protobuf-based serialized bytes. * * In detail, it traverses all logical nodes in a postfix order. * For each visiting node, it serializes the node and adds the serialized bytes into a list. * Then, a list will contains a list of serialized nodes in a postfix order. * * @see org.apache.tajo.plan.serder.LogicalNodeDeserializer */ public class LogicalNodeSerializer extends BasicLogicalPlanVisitor<LogicalNodeSerializer.SerializeContext, LogicalNode> { private static final LogicalNodeSerializer instance; static { instance = new LogicalNodeSerializer(); } /** * Serialize a logical plan into a protobuf-based serialized bytes. * * @param node LogicalNode to be serialized * @return A list of serialized nodes */ public static LogicalNodeTree serialize(LogicalNode node) { SerializeContext context = new SerializeContext(); try { instance.visit(context, null, null, node, new Stack<>()); } catch (TajoException e) { throw new TajoInternalError(e); } return context.treeBuilder.build(); } private static PlanProto.LogicalNode.Builder createNodeBuilder(SerializeContext context, LogicalNode node) { int selfId; if (context.idMap.containsKey(node.getPID())) { selfId = context.idMap.get(node.getPID()); } else { selfId = context.seqId++; context.idMap.put(node.getPID(), selfId); } PlanProto.LogicalNode.Builder nodeBuilder = PlanProto.LogicalNode.newBuilder(); nodeBuilder.setVisitSeq(selfId); nodeBuilder.setNodeId(node.getPID()); nodeBuilder.setType(convertType(node.getType())); // some DDL statements like DropTable or DropDatabase do not have in/out schemas if (node.getInSchema() != null) { nodeBuilder.setInSchema(node.getInSchema().getProto()); } if (node.getOutSchema() != null) { nodeBuilder.setOutSchema(node.getOutSchema().getProto()); } return nodeBuilder; } public static class SerializeContext { private int seqId = 0; private Map<Integer, Integer> idMap = Maps.newHashMap(); // map for PID and visit sequence private LogicalNodeTree.Builder treeBuilder = LogicalNodeTree.newBuilder(); } @Override public LogicalNode visitRoot(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, LogicalRootNode root, Stack<LogicalNode> stack) throws TajoException { super.visitRoot(context, plan, block, root, stack); int [] childIds = registerGetChildIds(context, root); PlanProto.RootNode.Builder rootBuilder = PlanProto.RootNode.newBuilder(); rootBuilder.setChildSeq(childIds[0]); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, root); nodeBuilder.setRoot(rootBuilder); context.treeBuilder.addNodes(nodeBuilder); return root; } @Override public LogicalNode visitSetSession(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, SetSessionNode node, Stack<LogicalNode> stack) throws TajoException { super.visitSetSession(context, plan, block, node, stack); PlanProto.SetSessionNode.Builder builder = PlanProto.SetSessionNode.newBuilder(); builder.setName(node.getName()); if (node.hasValue()) { builder.setValue(node.getValue()); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setSetSession(builder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitEvalExpr(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, EvalExprNode exprEval, Stack<LogicalNode> stack) throws TajoException { PlanProto.EvalExprNode.Builder exprEvalBuilder = PlanProto.EvalExprNode.newBuilder(); exprEvalBuilder.addAllTargets( ProtoUtil.<PlanProto.Target>toProtoObjects(exprEval.getTargets().toArray(new ProtoObject[exprEval.getTargets().size()]))); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, exprEval); nodeBuilder.setExprEval(exprEvalBuilder); context.treeBuilder.addNodes(nodeBuilder); return exprEval; } @Override public LogicalNode visitProjection(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, ProjectionNode projection, Stack<LogicalNode> stack) throws TajoException { super.visitProjection(context, plan, block, projection, stack); int [] childIds = registerGetChildIds(context, projection); PlanProto.ProjectionNode.Builder projectionBuilder = PlanProto.ProjectionNode.newBuilder(); projectionBuilder.setChildSeq(childIds[0]); projectionBuilder.addAllTargets( ProtoUtil.<PlanProto.Target>toProtoObjects(projection.getTargets().toArray(new ProtoObject[projection.getTargets().size()]))); projectionBuilder.setDistinct(projection.isDistinct()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, projection); nodeBuilder.setProjection(projectionBuilder); context.treeBuilder.addNodes(nodeBuilder); return projection; } @Override public LogicalNode visitLimit(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, LimitNode limit, Stack<LogicalNode> stack) throws TajoException { super.visitLimit(context, plan, block, limit, stack); int [] childIds = registerGetChildIds(context, limit); PlanProto.LimitNode.Builder limitBuilder = PlanProto.LimitNode.newBuilder(); limitBuilder.setChildSeq(childIds[0]); limitBuilder.setFetchFirstNum(limit.getFetchFirstNum()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, limit); nodeBuilder.setLimit(limitBuilder); context.treeBuilder.addNodes(nodeBuilder); return limit; } @Override public LogicalNode visitWindowAgg(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, WindowAggNode windowAgg, Stack<LogicalNode> stack) throws TajoException { super.visitWindowAgg(context, plan, block, windowAgg, stack); int [] childIds = registerGetChildIds(context, windowAgg); PlanProto.WindowAggNode.Builder windowAggBuilder = PlanProto.WindowAggNode.newBuilder(); windowAggBuilder.setChildSeq(childIds[0]); if (windowAgg.hasPartitionKeys()) { windowAggBuilder.addAllPartitionKeys( ProtoUtil.<CatalogProtos.ColumnProto>toProtoObjects(windowAgg.getPartitionKeys())); } if (windowAgg.hasAggFunctions()) { windowAggBuilder.addAllWindowFunctions( ProtoUtil.<PlanProto.EvalNodeTree>toProtoObjects(windowAgg.getWindowFunctions())); } windowAggBuilder.setDistinct(windowAgg.isDistinct()); if (windowAgg.hasSortSpecs()) { windowAggBuilder.addAllSortSpecs( ProtoUtil.<CatalogProtos.SortSpecProto>toProtoObjects(windowAgg.getSortSpecs())); } if (windowAgg.hasTargets()) { windowAggBuilder.addAllTargets( ProtoUtil.<PlanProto.Target>toProtoObjects(windowAgg.getTargets().toArray(new ProtoObject[windowAgg.getTargets().size()]))); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, windowAgg); nodeBuilder.setWindowAgg(windowAggBuilder); context.treeBuilder.addNodes(nodeBuilder); return windowAgg; } @Override public LogicalNode visitSort(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, SortNode sort, Stack<LogicalNode> stack) throws TajoException { super.visitSort(context, plan, block, sort, stack); int [] childIds = registerGetChildIds(context, sort); PlanProto.SortNode.Builder sortBuilder = PlanProto.SortNode.newBuilder(); sortBuilder.setChildSeq(childIds[0]); for (int i = 0; i < sort.getSortKeys().length; i++) { sortBuilder.addSortSpecs(sort.getSortKeys()[i].getProto()); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, sort); nodeBuilder.setSort(sortBuilder); context.treeBuilder.addNodes(nodeBuilder); return sort; } @Override public LogicalNode visitHaving(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, HavingNode having, Stack<LogicalNode> stack) throws TajoException { super.visitHaving(context, plan, block, having, stack); int [] childIds = registerGetChildIds(context, having); PlanProto.FilterNode.Builder filterBuilder = PlanProto.FilterNode.newBuilder(); filterBuilder.setChildSeq(childIds[0]); filterBuilder.setQual(EvalNodeSerializer.serialize(having.getQual())); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, having); nodeBuilder.setFilter(filterBuilder); context.treeBuilder.addNodes(nodeBuilder); return having; } @Override public LogicalNode visitGroupBy(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, GroupbyNode node, Stack<LogicalNode> stack) throws TajoException { super.visitGroupBy(context, plan, block, node, new Stack<>()); PlanProto.LogicalNode.Builder nodeBuilder = buildGroupby(context, node); context.treeBuilder.addNodes(nodeBuilder); return node; } private PlanProto.LogicalNode.Builder buildGroupby(SerializeContext context, GroupbyNode node) throws TajoException { int [] childIds = registerGetChildIds(context, node); PlanProto.GroupbyNode.Builder groupbyBuilder = PlanProto.GroupbyNode.newBuilder(); groupbyBuilder.setChildSeq(childIds[0]); groupbyBuilder.setDistinct(node.isDistinct()); if (node.groupingKeyNum() > 0) { groupbyBuilder.addAllGroupingKeys( ProtoUtil.<CatalogProtos.ColumnProto>toProtoObjects(node.getGroupingColumns())); } if (node.hasAggFunctions()) { groupbyBuilder.addAllAggFunctions( ProtoUtil.<PlanProto.EvalNodeTree>toProtoObjects(node.getAggFunctions().toArray(new ProtoObject[node.getAggFunctions().size()]))); } if (node.hasTargets()) { groupbyBuilder.addAllTargets(ProtoUtil.<PlanProto.Target>toProtoObjects(node.getTargets().toArray(new ProtoObject[node.getTargets().size()]))); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setGroupby(groupbyBuilder); return nodeBuilder; } @Override public LogicalNode visitDistinctGroupby(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, DistinctGroupbyNode node, Stack<LogicalNode> stack) throws TajoException { super.visitDistinctGroupby(context, plan, block, node, new Stack<>()); int [] childIds = registerGetChildIds(context, node); PlanProto.DistinctGroupbyNode.Builder distGroupbyBuilder = PlanProto.DistinctGroupbyNode.newBuilder(); distGroupbyBuilder.setChildSeq(childIds[0]); if (node.getGroupbyPlan() != null) { distGroupbyBuilder.setGroupbyNode(buildGroupby(context, node.getGroupbyPlan())); } for (GroupbyNode subPlan : node.getSubPlans()) { distGroupbyBuilder.addSubPlans(buildGroupby(context, subPlan)); } if (node.getGroupingColumns().length > 0) { distGroupbyBuilder.addAllGroupingKeys( ProtoUtil.<CatalogProtos.ColumnProto>toProtoObjects(node.getGroupingColumns())); } if (node.getAggFunctions().size() > 0) { distGroupbyBuilder.addAllAggFunctions( ProtoUtil.<PlanProto.EvalNodeTree>toProtoObjects(node.getAggFunctions().toArray(new ProtoObject[node.getAggFunctions().size()]))); } if (node.hasTargets()) { distGroupbyBuilder.addAllTargets(ProtoUtil.<PlanProto.Target>toProtoObjects(node.getTargets().toArray(new ProtoObject[node.getTargets().size()]))); } for (int cid : node.getResultColumnIds()) { distGroupbyBuilder.addResultId(cid); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setDistinctGroupby(distGroupbyBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitFilter(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, SelectionNode filter, Stack<LogicalNode> stack) throws TajoException { super.visitFilter(context, plan, block, filter, stack); int [] childIds = registerGetChildIds(context, filter); PlanProto.FilterNode.Builder filterBuilder = PlanProto.FilterNode.newBuilder(); filterBuilder.setChildSeq(childIds[0]); filterBuilder.setQual(EvalNodeSerializer.serialize(filter.getQual())); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, filter); nodeBuilder.setFilter(filterBuilder); context.treeBuilder.addNodes(nodeBuilder); return filter; } @Override public LogicalNode visitJoin(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode join, Stack<LogicalNode> stack) throws TajoException { super.visitJoin(context, plan, block, join, stack); int [] childIds = registerGetChildIds(context, join); // building itself PlanProto.JoinNode.Builder joinBuilder = PlanProto.JoinNode.newBuilder(); joinBuilder.setJoinType(convertJoinType(join.getJoinType())); joinBuilder.setLeftChildSeq(childIds[0]); joinBuilder.setRightChilSeq(childIds[1]); if (join.hasJoinQual()) { joinBuilder.setJoinQual(EvalNodeSerializer.serialize(join.getJoinQual())); } if (join.hasTargets()) { joinBuilder.setExistsTargets(true); joinBuilder.addAllTargets(ProtoUtil.<PlanProto.Target>toProtoObjects(join.getTargets().toArray(new ProtoObject[join.getTargets().size()]))); } else { joinBuilder.setExistsTargets(false); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, join); nodeBuilder.setJoin(joinBuilder); context.treeBuilder.addNodes(nodeBuilder); return join; } @Override public LogicalNode visitUnion(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, UnionNode node, Stack<LogicalNode> stack) throws TajoException { super.visitUnion(context, plan, block, node, stack); int [] childIds = registerGetChildIds(context, node); PlanProto.UnionNode.Builder unionBuilder = PlanProto.UnionNode.newBuilder(); unionBuilder.setAll(true); unionBuilder.setLeftChildSeq(childIds[0]); unionBuilder.setRightChildSeq(childIds[1]); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setUnion(unionBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitScan(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, ScanNode scan, Stack<LogicalNode> stack) throws TajoException { PlanProto.ScanNode.Builder scanBuilder = buildScanNode(scan); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, scan); nodeBuilder.setScan(scanBuilder); context.treeBuilder.addNodes(nodeBuilder); return scan; } public PlanProto.ScanNode.Builder buildScanNode(ScanNode scan) { PlanProto.ScanNode.Builder scanBuilder = PlanProto.ScanNode.newBuilder(); scanBuilder.setTable(scan.getTableDesc().getProto()); if (scan.hasAlias()) { scanBuilder.setAlias(scan.getAlias()); } if (scan.hasTargets()) { scanBuilder.setExistTargets(true); scanBuilder.addAllTargets(ProtoUtil.<PlanProto.Target>toProtoObjects(scan.getTargets().toArray(new ProtoObject[scan.getTargets().size()]))); } else { scanBuilder.setExistTargets(false); } if (scan.hasQual()) { scanBuilder.setQual(EvalNodeSerializer.serialize(scan.getQual())); } scanBuilder.setBroadcast(scan.isBroadcastTable()); scanBuilder.setNameResolveBase(scan.isNameResolveBase()); return scanBuilder; } @Override public LogicalNode visitIndexScan(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, IndexScanNode node, Stack<LogicalNode> stack) throws TajoException { PlanProto.ScanNode.Builder scanBuilder = buildScanNode(node); PlanProto.IndexScanSpec.Builder indexScanSpecBuilder = PlanProto.IndexScanSpec.newBuilder(); indexScanSpecBuilder.setKeySchema(node.getKeySchema().getProto()); indexScanSpecBuilder.setIndexPath(node.getIndexPath().toString()); for (SimplePredicate predicate : node.getPredicates()) { indexScanSpecBuilder.addPredicates(predicate.getProto()); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setScan(scanBuilder); nodeBuilder.setIndexScan(indexScanSpecBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitPartitionedTableScan(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, PartitionedTableScanNode node, Stack<LogicalNode> stack) throws TajoException { PlanProto.ScanNode.Builder scanBuilder = buildScanNode(node); PlanProto.PartitionScanSpec.Builder partitionScan = PlanProto.PartitionScanSpec.newBuilder(); List<String> pathStrs = new ArrayList<>(); if (node.getInputPaths() != null) { for (Path p : node.getInputPaths()) { pathStrs.add(p.toString()); } partitionScan.addAllPaths(pathStrs); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setScan(scanBuilder); nodeBuilder.setPartitionScan(partitionScan); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitTableSubQuery(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, TableSubQueryNode node, Stack<LogicalNode> stack) throws TajoException { super.visitTableSubQuery(context, plan, block, node, stack); int [] childIds = registerGetChildIds(context, node); PlanProto.TableSubQueryNode.Builder builder = PlanProto.TableSubQueryNode.newBuilder(); builder.setChildSeq(childIds[0]); builder.setTableName(node.getTableName()); if (node.hasTargets()) { builder.addAllTargets(ProtoUtil.<PlanProto.Target>toProtoObjects(node.getTargets().toArray(new ProtoObject[node.getTargets().size()]))); } builder.setNameResolveBase(node.isNameResolveBase()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setTableSubQuery(builder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitCreateTable(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, CreateTableNode node, Stack<LogicalNode> stack) throws TajoException { super.visitCreateTable(context, plan, block, node, stack); int [] childIds = registerGetChildIds(context, node); PlanProto.PersistentStoreNode.Builder persistentStoreBuilder = buildPersistentStoreBuilder(node, childIds); PlanProto.StoreTableNodeSpec.Builder storeTableBuilder = buildStoreTableNodeSpec(node); PlanProto.CreateTableNodeSpec.Builder createTableBuilder = PlanProto.CreateTableNodeSpec.newBuilder(); if (node.hasTableSpaceName()) { createTableBuilder.setTablespaceName(node.getTableSpaceName()); } createTableBuilder.setExternal(node.isExternal()); createTableBuilder.setIfNotExists(node.isIfNotExists()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setPersistentStore(persistentStoreBuilder); nodeBuilder.setStoreTable(storeTableBuilder); nodeBuilder.setCreateTable(createTableBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitDropTable(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, DropTableNode node, Stack<LogicalNode> stack) { PlanProto.DropTableNode.Builder dropTableBuilder = PlanProto.DropTableNode.newBuilder(); dropTableBuilder.setTableName(node.getTableName()); dropTableBuilder.setIfExists(node.isIfExists()); dropTableBuilder.setPurge(node.isPurge()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setDropTable(dropTableBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitAlterTablespace(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, AlterTablespaceNode node, Stack<LogicalNode> stack) throws TajoException { PlanProto.AlterTablespaceNode.Builder alterTablespaceBuilder = PlanProto.AlterTablespaceNode.newBuilder(); alterTablespaceBuilder.setTableSpaceName(node.getTablespaceName()); switch (node.getSetType()) { case LOCATION: alterTablespaceBuilder.setSetType(PlanProto.AlterTablespaceNode.Type.LOCATION); alterTablespaceBuilder.setSetLocation(SetLocation.newBuilder().setLocation(node.getLocation())); break; default: throw new NotImplementedException("Unknown SET type in ALTER TABLESPACE: " + node.getSetType().name()); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setAlterTablespace(alterTablespaceBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitAlterTable(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, AlterTableNode node, Stack<LogicalNode> stack) { PlanProto.AlterTableNode.Builder alterTableBuilder = PlanProto.AlterTableNode.newBuilder(); alterTableBuilder.setTableName(node.getTableName()); PlanProto.AlterTableNode.AlterPartition.Builder partitionBuilder = null; switch (node.getAlterTableOpType()) { case RENAME_TABLE: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.RENAME_TABLE); alterTableBuilder.setRenameTable(RenameTable.newBuilder().setNewName(node.getNewTableName())); break; case ADD_COLUMN: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.ADD_COLUMN); alterTableBuilder.setAddColumn(AddColumn.newBuilder().setAddColumn(node.getAddNewColumn().getProto())); break; case RENAME_COLUMN: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.RENAME_COLUMN); alterTableBuilder.setRenameColumn(RenameColumn.newBuilder() .setOldName(node.getColumnName()) .setNewName(node.getNewColumnName())); break; case SET_PROPERTY: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.SET_PROPERTY); alterTableBuilder.setProperties(node.getProperties().getProto()); break; case UNSET_PROPERTY: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.UNSET_PROPERTY); alterTableBuilder.setUnsetPropertyKeys(ProtoUtil.convertStrings(Arrays.asList(node.getUnsetPropertyKeys()))); break; case ADD_PARTITION: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.ADD_PARTITION); partitionBuilder = PlanProto.AlterTableNode.AlterPartition.newBuilder(); for (String columnName : node.getPartitionColumns()) { partitionBuilder.addColumnNames(columnName); } for (String partitionValue : node.getPartitionValues()) { partitionBuilder.addPartitionValues(partitionValue); } if (node.getLocation() != null) { partitionBuilder.setLocation(node.getLocation()); } alterTableBuilder.setAlterPartition(partitionBuilder); break; case DROP_PARTITION: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.DROP_PARTITION); partitionBuilder = PlanProto.AlterTableNode.AlterPartition.newBuilder(); for (String columnName : node.getPartitionColumns()) { partitionBuilder.addColumnNames(columnName); } for (String partitionValue : node.getPartitionValues()) { partitionBuilder.addPartitionValues(partitionValue); } partitionBuilder.setPurge(node.isPurge()); alterTableBuilder.setAlterPartition(partitionBuilder); break; case REPAIR_PARTITION: alterTableBuilder.setSetType(PlanProto.AlterTableNode.Type.REPAIR_PARTITION); alterTableBuilder.setTableName(node.getTableName()); break; default: throw new TajoRuntimeException( new NotImplementedException("Unknown SET type in ALTER TABLE: " + node.getAlterTableOpType().name())); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setAlterTable(alterTableBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitTruncateTable(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, TruncateTableNode node, Stack<LogicalNode> stack) throws TajoException { PlanProto.TruncateTableNode.Builder truncateTableBuilder = PlanProto.TruncateTableNode.newBuilder(); truncateTableBuilder.addAllTableNames(node.getTableNames()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setTruncateTableNode(truncateTableBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitInsert(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, InsertNode node, Stack<LogicalNode> stack) throws TajoException { super.visitInsert(context, plan, block, node, stack); int [] childIds = registerGetChildIds(context, node); PlanProto.PersistentStoreNode.Builder persistentStoreBuilder = buildPersistentStoreBuilder(node, childIds); PlanProto.StoreTableNodeSpec.Builder storeTableBuilder = buildStoreTableNodeSpec(node); PlanProto.InsertNodeSpec.Builder insertNodeSpec = PlanProto.InsertNodeSpec.newBuilder(); insertNodeSpec.setOverwrite(node.isOverwrite()); if (node.hasProjectedSchema()) { insertNodeSpec.setProjectedSchema(node.getProjectedSchema().getProto()); } if (node.hasTargetSchema()) { insertNodeSpec.setTargetSchema(node.getTargetSchema().getProto()); } PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setPersistentStore(persistentStoreBuilder); nodeBuilder.setStoreTable(storeTableBuilder); nodeBuilder.setInsert(insertNodeSpec); context.treeBuilder.addNodes(nodeBuilder); return node; } private static PlanProto.PersistentStoreNode.Builder buildPersistentStoreBuilder(PersistentStoreNode node, int [] childIds) { PlanProto.PersistentStoreNode.Builder persistentStoreBuilder = PlanProto.PersistentStoreNode.newBuilder(); if (childIds.length > 0) { // Simple create table may not have any children. This should be improved at TAJO-1589. persistentStoreBuilder.setChildSeq(childIds[0]); } persistentStoreBuilder.setStorageType(node.getStorageType()); if (node.hasOptions()) { persistentStoreBuilder.setTableProperties(node.getOptions().getProto()); } return persistentStoreBuilder; } private static PlanProto.StoreTableNodeSpec.Builder buildStoreTableNodeSpec(StoreTableNode node) { PlanProto.StoreTableNodeSpec.Builder storeTableBuilder = PlanProto.StoreTableNodeSpec.newBuilder(); if (node.hasTableName()) { // It will be false if node is for INSERT INTO LOCATION '...' storeTableBuilder.setTableName(node.getTableName()); } if (node.hasUri()) { storeTableBuilder.setUri(node.getUri().toString()); } if (node.hasTableSchema()) { storeTableBuilder.setTableSchema(node.getTableSchema().getProto()); } if (node.hasPartition()) { storeTableBuilder.setPartitionMethod(node.getPartitionMethod().getProto()); } return storeTableBuilder; } @Override public LogicalNode visitCreateDatabase(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, CreateDatabaseNode node, Stack<LogicalNode> stack) throws TajoException { PlanProto.CreateDatabaseNode.Builder createDatabaseBuilder = PlanProto.CreateDatabaseNode.newBuilder(); createDatabaseBuilder.setDbName(node.getDatabaseName()); createDatabaseBuilder.setIfNotExists(node.isIfNotExists()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setCreateDatabase(createDatabaseBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitDropDatabase(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, DropDatabaseNode node, Stack<LogicalNode> stack) throws TajoException { PlanProto.DropDatabaseNode.Builder dropDatabaseBuilder = PlanProto.DropDatabaseNode.newBuilder(); dropDatabaseBuilder.setDbName(node.getDatabaseName()); dropDatabaseBuilder.setIfExists(node.isIfExists()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setDropDatabase(dropDatabaseBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitCreateIndex(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, CreateIndexNode node, Stack<LogicalNode> stack) throws TajoException { super.visitCreateIndex(context, plan, block, node, new Stack<>()); PlanProto.CreateIndexNode.Builder createIndexBuilder = PlanProto.CreateIndexNode.newBuilder(); int [] childIds = registerGetChildIds(context, node); createIndexBuilder.setChildSeq(childIds[0]); createIndexBuilder.setIndexName(node.getIndexName()); createIndexBuilder.setIndexMethod(node.getIndexMethod()); createIndexBuilder.setIndexPath(node.getIndexPath().toString()); for (SortSpec sortSpec : node.getKeySortSpecs()) { createIndexBuilder.addKeySortSpecs(sortSpec.getProto()); } createIndexBuilder.setTargetRelationSchema(node.getTargetRelationSchema().getProto()); createIndexBuilder.setIsUnique(node.isUnique()); createIndexBuilder.setIsClustered(node.isClustered()); if (node.hasOptions()) { createIndexBuilder.setIndexProperties(node.getOptions().getProto()); } createIndexBuilder.setIsExternal(node.isExternal()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setCreateIndex(createIndexBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } @Override public LogicalNode visitDropIndex(SerializeContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, DropIndexNode node, Stack<LogicalNode> stack) { PlanProto.DropIndexNode.Builder dropIndexBuilder = PlanProto.DropIndexNode.newBuilder(); dropIndexBuilder.setIndexName(node.getIndexName()); PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node); nodeBuilder.setDropIndex(dropIndexBuilder); context.treeBuilder.addNodes(nodeBuilder); return node; } public static PlanProto.NodeType convertType(NodeType type) { return PlanProto.NodeType.valueOf(type.name()); } public static PlanProto.JoinType convertJoinType(JoinType type) { switch (type) { case CROSS: return PlanProto.JoinType.CROSS_JOIN; case INNER: return PlanProto.JoinType.INNER_JOIN; case LEFT_OUTER: return PlanProto.JoinType.LEFT_OUTER_JOIN; case RIGHT_OUTER: return PlanProto.JoinType.RIGHT_OUTER_JOIN; case FULL_OUTER: return PlanProto.JoinType.FULL_OUTER_JOIN; case LEFT_SEMI: return PlanProto.JoinType.LEFT_SEMI_JOIN; case RIGHT_SEMI: return PlanProto.JoinType.RIGHT_SEMI_JOIN; case LEFT_ANTI: return PlanProto.JoinType.LEFT_ANTI_JOIN; case RIGHT_ANTI: return PlanProto.JoinType.RIGHT_ANTI_JOIN; case UNION: return PlanProto.JoinType.UNION_JOIN; default: throw new RuntimeException("Unknown JoinType: " + type.name()); } } public static PlanProto.Target convertTarget(Target target) { PlanProto.Target.Builder targetBuilder = PlanProto.Target.newBuilder(); targetBuilder.setExpr(EvalNodeSerializer.serialize((EvalNode) target.getEvalTree())); if (target.hasAlias()) { targetBuilder.setAlias(target.getAlias()); } return targetBuilder.build(); } private int [] registerGetChildIds(SerializeContext context, LogicalNode node) { int [] childIds = new int[node.childNum()]; for (int i = 0; i < node.childNum(); i++) { if (node.getChild(i) != null && context.idMap.containsKey(node.getChild(i).getPID())) { childIds[i] = context.idMap.get(node.getChild(i).getPID()); } else { childIds[i] = context.seqId++; } } return childIds; } }