org.apache.tajo.master.exec.NonForwardQueryResultSystemScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.tajo.master.exec.NonForwardQueryResultSystemScanner.java

Source

/**
 * 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.master.exec;

import com.google.common.collect.Lists;
import com.google.protobuf.ByteString;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tajo.QueryId;
import org.apache.tajo.TaskAttemptId;
import org.apache.tajo.TaskId;
import org.apache.tajo.catalog.*;
import org.apache.tajo.catalog.proto.CatalogProtos.*;
import org.apache.tajo.catalog.statistics.TableStats;
import org.apache.tajo.conf.TajoConf;
import org.apache.tajo.datum.DatumFactory;
import org.apache.tajo.engine.codegen.CompilationError;
import org.apache.tajo.engine.planner.PhysicalPlannerImpl;
import org.apache.tajo.engine.planner.Projector;
import org.apache.tajo.engine.planner.global.ExecutionBlock;
import org.apache.tajo.engine.planner.global.ExecutionBlockCursor;
import org.apache.tajo.engine.planner.global.GlobalPlanner;
import org.apache.tajo.engine.planner.global.MasterPlan;
import org.apache.tajo.engine.planner.physical.PhysicalExec;
import org.apache.tajo.engine.query.QueryContext;
import org.apache.tajo.exception.InvalidSessionException;
import org.apache.tajo.exception.TajoException;
import org.apache.tajo.exception.TajoInternalError;
import org.apache.tajo.ipc.ClientProtos.SerializedResultSet;
import org.apache.tajo.master.TajoMaster.MasterContext;
import org.apache.tajo.master.rm.NodeStatus;
import org.apache.tajo.plan.LogicalPlan;
import org.apache.tajo.plan.expr.EvalNode;
import org.apache.tajo.plan.logical.IndexScanNode;
import org.apache.tajo.plan.logical.LogicalNode;
import org.apache.tajo.plan.logical.ScanNode;
import org.apache.tajo.resource.NodeResource;
import org.apache.tajo.resource.NodeResources;
import org.apache.tajo.schema.IdentifierUtil;
import org.apache.tajo.storage.RowStoreUtil;
import org.apache.tajo.storage.RowStoreUtil.RowStoreEncoder;
import org.apache.tajo.storage.Tuple;
import org.apache.tajo.storage.VTuple;
import org.apache.tajo.tuple.memory.MemoryBlock;
import org.apache.tajo.tuple.memory.MemoryRowBlock;
import org.apache.tajo.type.Type;
import org.apache.tajo.type.TypeProtobufEncoder;
import org.apache.tajo.util.KeyValueSet;
import org.apache.tajo.worker.TaskAttemptContext;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class NonForwardQueryResultSystemScanner implements NonForwardQueryResultScanner {

    private static final Log LOG = LogFactory.getLog(NonForwardQueryResultSystemScanner.class);

    private MasterContext masterContext;
    private LogicalPlan logicalPlan;
    private final QueryId queryId;
    private final String sessionId;
    private TaskAttemptContext taskContext;
    private int currentRow;
    private long maxRow;
    private TableDesc tableDesc;
    private Schema outSchema;
    private RowStoreEncoder encoder;
    private PhysicalExec physicalExec;
    private MemoryRowBlock rowBlock;
    private boolean eof;

    public NonForwardQueryResultSystemScanner(MasterContext context, LogicalPlan plan, QueryId queryId,
            String sessionId, int maxRow) {
        masterContext = context;
        logicalPlan = plan;
        this.queryId = queryId;
        this.sessionId = sessionId;
        this.maxRow = maxRow;

    }

    @Override
    public void init() throws IOException {
        QueryContext queryContext = new QueryContext(masterContext.getConf());
        currentRow = 0;

        MasterPlan masterPlan = new MasterPlan(queryId, queryContext, logicalPlan);
        GlobalPlanner globalPlanner = new GlobalPlanner(masterContext.getConf(), masterContext.getCatalog());
        try {
            globalPlanner.build(queryContext, masterPlan);
        } catch (TajoException e) {
            throw new TajoInternalError(e);
        }

        ExecutionBlockCursor cursor = new ExecutionBlockCursor(masterPlan);
        ExecutionBlock leafBlock = null;
        for (ExecutionBlock block : cursor) {
            if (masterPlan.isLeaf(block)) {
                leafBlock = block;
                break;
            }
        }

        if (leafBlock == null) {
            throw new TajoInternalError("global planner could not find any leaf block.");
        }

        taskContext = new TaskAttemptContext(queryContext, null,
                new TaskAttemptId(new TaskId(leafBlock.getId(), 0), 0), null, null);
        physicalExec = new SimplePhysicalPlannerImpl(masterContext.getConf()).createPlan(taskContext,
                leafBlock.getPlan());

        tableDesc = new TableDesc("table_" + System.currentTimeMillis(), physicalExec.getSchema(),
                new TableMeta("SYSTEM", new KeyValueSet()), null);
        outSchema = physicalExec.getSchema();
        encoder = RowStoreUtil.createEncoder(getLogicalSchema());

        physicalExec.init();
        eof = false;
    }

    @Override
    public void close() {
        if (rowBlock != null) {
            rowBlock.release();
            rowBlock = null;
        }

        if (physicalExec != null) {
            try {
                physicalExec.close();
            } catch (Exception ignored) {
            }
        }
        physicalExec = null;
        currentRow = -1;
    }

    private List<Tuple> getTablespaces(Schema outSchema) {
        List<TablespaceProto> tablespaces = masterContext.getCatalog().getAllTablespaces();
        List<Tuple> tuples = new ArrayList<>(tablespaces.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (TablespaceProto tablespace : tablespaces) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);
                if ("space_id".equalsIgnoreCase(column.getSimpleName())) {
                    if (tablespace.hasId()) {
                        aTuple.put(fieldId, DatumFactory.createInt4(tablespace.getId()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                } else if ("space_name".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(tablespace.getSpaceName()));
                } else if ("space_handler".equalsIgnoreCase(column.getSimpleName())) {
                    if (tablespace.hasHandler()) {
                        aTuple.put(fieldId, DatumFactory.createText(tablespace.getHandler()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                } else if ("space_uri".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(tablespace.getUri()));
                }
            }
            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getDatabases(Schema outSchema) {
        List<DatabaseProto> databases = masterContext.getCatalog().getAllDatabases();
        List<Tuple> tuples = new ArrayList<>(databases.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (DatabaseProto database : databases) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);
                if ("db_id".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(database.getId()));
                } else if ("db_name".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(database.getName()));
                } else if ("space_id".equalsIgnoreCase(column.getSimpleName())) {
                    if (database.hasSpaceId()) {
                        aTuple.put(fieldId, DatumFactory.createInt4(database.getSpaceId()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                }
            }

            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getTables(Schema outSchema) {
        List<TableDescriptorProto> tables = masterContext.getCatalog().getAllTables();
        List<Tuple> tuples = new ArrayList<>(tables.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (TableDescriptorProto table : tables) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);
                if ("tid".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(table.getTid()));
                } else if ("db_id".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(table.getDbId()));
                } else if ("table_name".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(table.getName()));
                } else if ("table_type".equalsIgnoreCase(column.getSimpleName())) {
                    if (table.hasTableType()) {
                        aTuple.put(fieldId, DatumFactory.createText(table.getTableType()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                } else if ("path".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(table.getPath()));
                } else if ("data_format".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(table.getDataFormat()));
                }
            }

            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getColumns(Schema outSchema) {
        List<ColumnProto> columnsList = masterContext.getCatalog().getAllColumns();
        List<Tuple> tuples = new ArrayList<>(columnsList.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;
        int columnId = 1, prevtid = -1, tid = 0;

        for (ColumnProto column : columnsList) {
            aTuple = new VTuple(outSchema.size());

            tid = column.getTid();
            if (prevtid != tid) {
                columnId = 1;
                prevtid = tid;
            }

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column colObj = columns.get(fieldId);

                if ("tid".equalsIgnoreCase(colObj.getSimpleName())) {
                    if (column.hasTid()) {
                        aTuple.put(fieldId, DatumFactory.createInt4(tid));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                } else if ("column_name".equalsIgnoreCase(colObj.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(column.getName()));
                } else if ("ordinal_position".equalsIgnoreCase(colObj.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(columnId));
                } else if ("data_type".equalsIgnoreCase(colObj.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(column.getType().toString()));
                } else if ("type_length".equalsIgnoreCase(colObj.getSimpleName())) {
                    Type type = TypeProtobufEncoder.decode(column.getType());
                    if (type.isValueParameterized()) {
                        aTuple.put(fieldId, DatumFactory.createInt4(type.getValueParameters().get(0)));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                }
            }

            columnId++;
            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getIndexes(Schema outSchema) {
        List<IndexDescProto> indexList = masterContext.getCatalog().getAllIndexes();
        List<Tuple> tuples = new ArrayList<>(indexList.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (IndexDescProto index : indexList) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);

                if ("db_id".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(index.getTableIdentifier().getDbId()));
                } else if ("tid".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(index.getTableIdentifier().getTid()));
                } else if ("index_name".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(index.getIndexName()));
                } else if ("index_method".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(index.getIndexMethod().name()));
                } else if ("index_path".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(index.getIndexPath()));
                }
            }

            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getAllTableOptions(Schema outSchema) {
        List<TableOptionProto> optionList = masterContext.getCatalog().getAllTableOptions();
        List<Tuple> tuples = new ArrayList<>(optionList.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (TableOptionProto option : optionList) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);

                if ("tid".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(option.getTid()));
                } else if ("key_".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(option.getKeyval().getKey()));
                } else if ("value_".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(option.getKeyval().getValue()));
                }
            }

            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getAllTableStats(Schema outSchema) {
        List<TableStatsProto> statList = masterContext.getCatalog().getAllTableStats();
        List<Tuple> tuples = new ArrayList<>(statList.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (TableStatsProto stat : statList) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);

                if ("tid".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(stat.getTid()));
                } else if ("num_rows".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt8(stat.getNumRows()));
                } else if ("num_bytes".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt8(stat.getNumBytes()));
                }
            }

            tuples.add(aTuple);
        }

        return tuples;
    }

    private List<Tuple> getAllPartitions(Schema outSchema) {
        List<TablePartitionProto> partitionList = masterContext.getCatalog().getAllPartitions();
        List<Tuple> tuples = new ArrayList<>(partitionList.size());
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple;

        for (TablePartitionProto partition : partitionList) {
            aTuple = new VTuple(outSchema.size());

            for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
                Column column = columns.get(fieldId);

                if ("partition_id".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(partition.getPartitionId()));
                } else if ("tid".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(partition.getTid()));
                } else if ("partition_name".equalsIgnoreCase(column.getSimpleName())) {
                    if (partition.hasPartitionName()) {
                        aTuple.put(fieldId, DatumFactory.createText(partition.getPartitionName()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                } else if ("path".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createText(partition.getPath()));
                }
            }

            tuples.add(aTuple);
        }

        return tuples;
    }

    private Tuple getQueryMasterTuple(Schema outSchema, NodeStatus aNodeStatus) {
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple = new VTuple(outSchema.size());

        for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
            Column column = columns.get(fieldId);

            if ("host".equalsIgnoreCase(column.getSimpleName())) {
                if (aNodeStatus.getConnectionInfo() != null && aNodeStatus.getConnectionInfo().getHost() != null) {
                    aTuple.put(fieldId, DatumFactory.createText(aNodeStatus.getConnectionInfo().getHost()));
                } else {
                    aTuple.put(fieldId, DatumFactory.createNullDatum());
                }
            } else if ("port".equalsIgnoreCase(column.getSimpleName())) {
                if (aNodeStatus.getConnectionInfo() != null) {
                    aTuple.put(fieldId,
                            DatumFactory.createInt4(aNodeStatus.getConnectionInfo().getQueryMasterPort()));
                } else {
                    aTuple.put(fieldId, DatumFactory.createNullDatum());
                }
            } else if ("type".equalsIgnoreCase(column.getSimpleName())) {
                aTuple.put(fieldId, DatumFactory.createText("QueryMaster"));
            } else if ("status".equalsIgnoreCase(column.getSimpleName())) {
                aTuple.put(fieldId, DatumFactory.createText(aNodeStatus.getState().toString()));
            } else if ("RUNNING".equalsIgnoreCase(aNodeStatus.getState().toString())) {
                if ("running_tasks".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(aNodeStatus.getNumRunningQueryMaster()));
                } else if ("last_heartbeat_ts".equalsIgnoreCase(column.getSimpleName())) {
                    if (aNodeStatus.getLastHeartbeatTime() > 0) {
                        aTuple.put(fieldId, DatumFactory
                                .createTimestampDatumWithJavaMillis(aNodeStatus.getLastHeartbeatTime()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                }
            } else {
                aTuple.put(fieldId, DatumFactory.createNullDatum());
            }
        }

        return aTuple;
    }

    private Tuple getWorkerTuple(Schema outSchema, NodeStatus aNodeStatus) {
        List<Column> columns = outSchema.getRootColumns();
        Tuple aTuple = new VTuple(outSchema.size());

        NodeResource total = aNodeStatus.getTotalResourceCapability();
        NodeResource used = NodeResources.subtract(total, aNodeStatus.getAvailableResource());

        for (int fieldId = 0; fieldId < columns.size(); fieldId++) {
            Column column = columns.get(fieldId);

            if ("host".equalsIgnoreCase(column.getSimpleName())) {
                if (aNodeStatus.getConnectionInfo() != null && aNodeStatus.getConnectionInfo().getHost() != null) {
                    aTuple.put(fieldId, DatumFactory.createText(aNodeStatus.getConnectionInfo().getHost()));
                } else {
                    aTuple.put(fieldId, DatumFactory.createNullDatum());
                }
            } else if ("port".equalsIgnoreCase(column.getSimpleName())) {
                if (aNodeStatus.getConnectionInfo() != null) {
                    aTuple.put(fieldId, DatumFactory.createInt4(aNodeStatus.getConnectionInfo().getPeerRpcPort()));
                } else {
                    aTuple.put(fieldId, DatumFactory.createNullDatum());
                }
            } else if ("type".equalsIgnoreCase(column.getSimpleName())) {
                aTuple.put(fieldId, DatumFactory.createText("Worker"));
            } else if ("status".equalsIgnoreCase(column.getSimpleName())) {
                aTuple.put(fieldId, DatumFactory.createText(aNodeStatus.getState().toString()));
            } else if ("RUNNING".equalsIgnoreCase(aNodeStatus.getState().toString())) {
                if ("total_cpu".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(total.getVirtualCores()));
                } else if ("used_mem".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt8(used.getMemory() * 1048576l));
                } else if ("total_mem".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt8(total.getMemory() * 1048576l));
                } else if ("running_tasks".equalsIgnoreCase(column.getSimpleName())) {
                    aTuple.put(fieldId, DatumFactory.createInt4(aNodeStatus.getNumRunningTasks()));
                } else if ("last_heartbeat_ts".equalsIgnoreCase(column.getSimpleName())) {
                    if (aNodeStatus.getLastHeartbeatTime() > 0) {
                        aTuple.put(fieldId, DatumFactory
                                .createTimestampDatumWithJavaMillis(aNodeStatus.getLastHeartbeatTime()));
                    } else {
                        aTuple.put(fieldId, DatumFactory.createNullDatum());
                    }
                }
            } else {
                aTuple.put(fieldId, DatumFactory.createNullDatum());
            }
        }

        return aTuple;
    }

    private List<Tuple> getClusterInfo(Schema outSchema) {
        Map<Integer, NodeStatus> workerMap = masterContext.getResourceManager().getNodes();
        List<Tuple> tuples;
        List<NodeStatus> queryMasterList = new ArrayList<>();
        List<NodeStatus> nodeStatusList = new ArrayList<>();

        for (NodeStatus aNodeStatus : workerMap.values()) {
            queryMasterList.add(aNodeStatus);
            nodeStatusList.add(aNodeStatus);
        }

        tuples = new ArrayList<>(queryMasterList.size() + nodeStatusList.size());
        for (NodeStatus queryMaster : queryMasterList) {
            tuples.add(getQueryMasterTuple(outSchema, queryMaster));
        }

        for (NodeStatus nodeStatus : nodeStatusList) {
            tuples.add(getWorkerTuple(outSchema, nodeStatus));
        }

        return tuples;
    }

    private List<Tuple> getSessionInfo(Schema outSchema) {
        List<Tuple> outputs = Lists.newArrayList();
        Tuple eachVariable;

        try {
            for (Map.Entry<String, String> var : masterContext.getSessionManager().getAllVariables(sessionId)
                    .entrySet()) {
                eachVariable = new VTuple(outSchema.size());
                eachVariable.put(0, DatumFactory.createText(var.getKey()));
                eachVariable.put(1, DatumFactory.createText(var.getValue()));

                outputs.add(eachVariable);
            }
        } catch (InvalidSessionException e) {
            LOG.error(e);
        }

        return outputs;
    }

    private List<Tuple> fetchSystemTable(TableDesc tableDesc, Schema inSchema) {
        List<Tuple> tuples = null;
        String tableName = IdentifierUtil.extractSimpleName(tableDesc.getName());

        if ("tablespace".equalsIgnoreCase(tableName)) {
            tuples = getTablespaces(inSchema);
        } else if ("databases".equalsIgnoreCase(tableName)) {
            tuples = getDatabases(inSchema);
        } else if ("tables".equalsIgnoreCase(tableName)) {
            tuples = getTables(inSchema);
        } else if ("columns".equalsIgnoreCase(tableName)) {
            tuples = getColumns(inSchema);
        } else if ("indexes".equalsIgnoreCase(tableName)) {
            tuples = getIndexes(inSchema);
        } else if ("table_options".equalsIgnoreCase(tableName)) {
            tuples = getAllTableOptions(inSchema);
        } else if ("table_stats".equalsIgnoreCase(tableName)) {
            tuples = getAllTableStats(inSchema);
        } else if ("partitions".equalsIgnoreCase(tableName)) {
            tuples = getAllPartitions(inSchema);
        } else if ("cluster".equalsIgnoreCase(tableName)) {
            tuples = getClusterInfo(inSchema);
        } else if ("session".equalsIgnoreCase(tableName)) {
            tuples = getSessionInfo(inSchema);
        }

        return tuples;
    }

    @Override
    public List<ByteString> getNextRows(int fetchRowNum) throws IOException {
        List<ByteString> rows = new ArrayList<>();
        int startRow = currentRow;
        int endRow = startRow + fetchRowNum;

        if (physicalExec == null) {
            return rows;
        }

        while (currentRow < endRow) {
            Tuple currentTuple = physicalExec.next();

            if (currentTuple == null) {
                physicalExec.close();
                physicalExec = null;
                break;
            }

            currentRow++;
            rows.add(ByteString.copyFrom(encoder.toBytes(currentTuple)));

            if (currentRow >= maxRow) {
                physicalExec.close();
                physicalExec = null;
                break;
            }
        }

        return rows;
    }

    @Override
    public List<Tuple> getNextTupleRows(int fetchRowNum) throws IOException {
        List<Tuple> rows = new ArrayList<>();
        int startRow = currentRow;
        int endRow = startRow + fetchRowNum;

        if (physicalExec == null) {
            return rows;
        }

        while (currentRow < endRow) {
            Tuple currentTuple = physicalExec.next();

            if (currentTuple == null) {
                physicalExec.close();
                physicalExec = null;
                break;
            }

            currentRow++;
            rows.add(currentTuple);

            if (currentRow >= maxRow) {
                physicalExec.close();
                physicalExec = null;
                break;
            }
        }

        return rows;
    }

    @Override
    public SerializedResultSet nextRowBlock(int fetchRowNum) throws IOException {
        int rowCount = 0;

        SerializedResultSet.Builder resultSetBuilder = SerializedResultSet.newBuilder();
        resultSetBuilder.setSchema(getLogicalSchema().getProto());
        resultSetBuilder.setRows(rowCount);
        int startRow = currentRow;
        int endRow = startRow + fetchRowNum;

        if (physicalExec == null) {
            return resultSetBuilder.build();
        }

        while (currentRow < endRow) {
            Tuple currentTuple = physicalExec.next();

            if (currentTuple == null) {
                eof = true;
                break;
            } else {
                if (rowBlock == null) {
                    rowBlock = new MemoryRowBlock(SchemaUtil.toDataTypes(tableDesc.getLogicalSchema()));
                }

                rowBlock.getWriter().addTuple(currentTuple);
                currentRow++;
                rowCount++;

                if (currentRow >= maxRow) {
                    eof = true;
                    break;
                }
            }
        }

        if (rowCount > 0) {
            resultSetBuilder.setRows(rowCount);
            MemoryBlock memoryBlock = rowBlock.getMemory();
            ByteBuffer rows = memoryBlock.getBuffer().nioBuffer(0, memoryBlock.readableBytes());

            resultSetBuilder.setDecompressedLength(rows.remaining());
            resultSetBuilder.setSerializedTuples(ByteString.copyFrom(rows));
            rowBlock.clear();
        }

        if (eof) {
            close();
        }
        return resultSetBuilder.build();
    }

    @Override
    public QueryId getQueryId() {
        return queryId;
    }

    @Override
    public String getSessionId() {
        return sessionId;
    }

    @Override
    public TableDesc getTableDesc() {
        return tableDesc;
    }

    @Override
    public Schema getLogicalSchema() {
        return outSchema;
    }

    @Override
    public int getCurrentRowNumber() {
        return currentRow;
    }

    class SimplePhysicalPlannerImpl extends PhysicalPlannerImpl {

        public SimplePhysicalPlannerImpl(TajoConf conf) {
            super(conf);
        }

        @Override
        public PhysicalExec createScanPlan(TaskAttemptContext ctx, ScanNode scanNode, Stack<LogicalNode> node)
                throws IOException {
            return new SystemPhysicalExec(ctx, scanNode);
        }

        @Override
        public PhysicalExec createIndexScanExec(TaskAttemptContext ctx, IndexScanNode annotation)
                throws IOException {
            return new SystemPhysicalExec(ctx, annotation);
        }
    }

    class SystemPhysicalExec extends PhysicalExec {

        private ScanNode scanNode;
        private EvalNode qual;
        private Projector projector;
        private TableStats tableStats;
        private final List<Tuple> cachedData;
        private int currentRow;
        private boolean isClosed;

        public SystemPhysicalExec(TaskAttemptContext context, ScanNode scanNode) {
            super(context, scanNode.getInSchema(), scanNode.getOutSchema());
            this.scanNode = scanNode;

            if (this.scanNode.hasQual()) {
                this.qual = this.scanNode.getQual();
                this.qual.bind(null, inSchema);
            }

            cachedData = new ArrayList<>();
            currentRow = 0;
            isClosed = false;

            projector = new Projector(context, inSchema, outSchema, scanNode.getTargets());
        }

        @Override
        public Tuple next() throws IOException {
            Tuple aTuple;

            if (isClosed) {
                return null;
            }

            if (cachedData.size() == 0) {
                rescan();
            }

            if (!scanNode.hasQual()) {
                if (currentRow < cachedData.size()) {
                    aTuple = cachedData.get(currentRow++);
                    Tuple outTuple = projector.eval(aTuple);
                    outTuple.setOffset(aTuple.getOffset());
                    return outTuple;
                }
                return null;
            } else {
                while (currentRow < cachedData.size()) {
                    aTuple = cachedData.get(currentRow++);
                    if (qual.eval(aTuple).isTrue()) {
                        Tuple outTuple = projector.eval(aTuple);
                        outTuple.setOffset(aTuple.getOffset());
                        return outTuple;
                    }
                }
                return null;
            }
        }

        @Override
        public void rescan() throws IOException {
            cachedData.clear();
            cachedData.addAll(fetchSystemTable(scanNode.getTableDesc(), inSchema));

            tableStats = new TableStats();
            tableStats.setNumRows(cachedData.size());
        }

        @Override
        public void close() throws IOException {
            scanNode = null;
            qual = null;
            projector = null;
            cachedData.clear();
            currentRow = -1;
            isClosed = true;
        }

        @Override
        public float getProgress() {
            return 1.0f;
        }

        @Override
        protected void compile() throws CompilationError {
            if (scanNode.hasQual()) {
                qual = context.getPrecompiledEval(inSchema, qual);
            }
        }

        @Override
        public TableStats getInputStats() {
            return tableStats;
        }

    }

}