com.alibaba.wasp.plan.parser.druid.DruidDDLParser.java Source code

Java tutorial

Introduction

Here is the source code for com.alibaba.wasp.plan.parser.druid.DruidDDLParser.java

Source

/**
 * Copyright 2010 The Apache Software Foundation
 *
 * 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 com.alibaba.wasp.plan.parser.druid;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLOrderingSpecification;
import com.alibaba.druid.sql.ast.SQLPartitioningClause;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropColumnItem;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableItem;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLDropIndexStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableAddColumn;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatement;
import com.alibaba.wasp.DataType;
import com.alibaba.wasp.FConstants;
import com.alibaba.wasp.FieldKeyWord;
import com.alibaba.wasp.TableExistsException;
import com.alibaba.wasp.TableNotFoundException;
import com.alibaba.wasp.meta.FTable;
import com.alibaba.wasp.meta.Field;
import com.alibaba.wasp.meta.Index;
import com.alibaba.wasp.meta.TableSchemaCacheReader;
import com.alibaba.wasp.plan.AlterTablePlan;
import com.alibaba.wasp.plan.CreateIndexPlan;
import com.alibaba.wasp.plan.CreateTablePlan;
import com.alibaba.wasp.plan.DescTablePlan;
import com.alibaba.wasp.plan.DropIndexPlan;
import com.alibaba.wasp.plan.DropTablePlan;
import com.alibaba.wasp.plan.NotingTodoPlan;
import com.alibaba.wasp.plan.ShowIndexesPlan;
import com.alibaba.wasp.plan.ShowTablesPlan;
import com.alibaba.wasp.plan.TruncateTablePlan;
import com.alibaba.wasp.plan.parser.ParseContext;
import com.alibaba.wasp.plan.parser.UnsupportedException;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlAlterTableChangeColumn;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlColumnDefinition;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlCreateIndexStatement;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlCreateTableStatement;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlDescribeStatement;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlDropTableStatement;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlPartitionByKey;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlShowCreateTableStatement;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlShowIndexesStatement;
import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlShowTablesStatement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;

/**
 * Use Druid (https://github.com/AlibabaTech/druid) to parse the sql and
 * generate Execute Plan
 * 
 */
public class DruidDDLParser extends DruidParser {

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

    /**
     * @param conf
     */
    public DruidDDLParser(Configuration conf) {
        super();
        this.setConf(conf);
    }

    /**
     * Parse sql and generate Execute Plan:CREATE,DROP,ALTER
     * 
     */
    @Override
    public void generatePlan(ParseContext context) throws IOException {
        SQLStatement stmt = context.getStmt();
        MetaEventOperation metaEventOperation = new FMetaEventOperation(context.getTsr());
        if (stmt instanceof WaspSqlCreateTableStatement) {
            // This is a Create Table SQL
            getCreateTablePlan(context, (WaspSqlCreateTableStatement) stmt, metaEventOperation);
        } else if (stmt instanceof WaspSqlCreateIndexStatement) {
            // This is a Create Index SQL
            getCreateIndexPlan(context, (WaspSqlCreateIndexStatement) stmt, metaEventOperation);
        } else if (stmt instanceof SQLDropTableStatement) {
            // This is a Drop Table SQL
            getDropTablePlan(context, (SQLDropTableStatement) stmt, metaEventOperation);
        } else if (stmt instanceof SQLDropIndexStatement) {
            // This is a Drop Index SQL
            getDropIndexPlan(context, (SQLDropIndexStatement) stmt, metaEventOperation);
        } else if (stmt instanceof MySqlAlterTableStatement) {
            // This is a Alter Table SQL
            getAlterTablePlan(context, (MySqlAlterTableStatement) stmt, metaEventOperation);
        } else if (stmt instanceof WaspSqlShowTablesStatement) {
            // This is a Show Tables SQL
            getShowTablesPlan(context, (WaspSqlShowTablesStatement) stmt, metaEventOperation);
        } else if (stmt instanceof WaspSqlShowCreateTableStatement) {
            // This is a Show Create Table SQL
            getShowCreateTablePlan(context, (WaspSqlShowCreateTableStatement) stmt, metaEventOperation);
        } else if (stmt instanceof WaspSqlDescribeStatement) {
            // This is a DESCRIBE SQL
            getDescribePlan(context, (WaspSqlDescribeStatement) stmt, metaEventOperation);
        } else if (stmt instanceof WaspSqlShowIndexesStatement) {
            // This is a SHOW INDEXES SQL
            getShowIndexesPlan(context, (WaspSqlShowIndexesStatement) stmt, metaEventOperation);
        } else if (stmt instanceof SQLTruncateStatement) {
            // This is a TRUNCATE TABLE SQL
            getTruncatePlan(context, (SQLTruncateStatement) stmt, metaEventOperation);
        } else {
            throw new UnsupportedException("Unsupported SQLStatement " + SQLUtils.toSQLString(stmt));
        }
    }

    /**
     * Process TRUNCATE Table Statement and generate Execute Plan
     * 
     */
    private void getTruncatePlan(ParseContext context, SQLTruncateStatement sqlTruncateStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        List<SQLExprTableSource> tableSources = sqlTruncateStatement.getTableSources();
        List<String> tableNames = new ArrayList<String>();
        for (SQLExprTableSource tableSource : tableSources) {
            String tablename = parseFromClause(tableSource);
            // check if table exists and get Table info
            metaEventOperation.checkAndGetTable(tablename, true);
            tableNames.add(tablename);
        }
        TruncateTablePlan truncateTable = new TruncateTablePlan(tableNames);
        context.setPlan(truncateTable);
        LOG.debug("TruncateTablePlan " + truncateTable.toString());
    }

    /**
     * Process SHOW INDEXES Statement and generate Execute Plan
     * 
     */
    private void getShowIndexesPlan(ParseContext context, WaspSqlShowIndexesStatement sqlShowIndexesStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        SQLName object = sqlShowIndexesStatement.getTable();
        String tablename = parseName(object);
        // check if table exists and get Table info
        metaEventOperation.checkAndGetTable(tablename, true);

        ShowIndexesPlan showIndexesTable = new ShowIndexesPlan(tablename);
        context.setPlan(showIndexesTable);
        LOG.debug("ShowIndexesPlan " + showIndexesTable.toString());
    }

    /**
     * Process Describe Table Statement and generate Execute Plan
     * 
     */
    private void getDescribePlan(ParseContext context, WaspSqlDescribeStatement sqlDescribeStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        SQLName object = sqlDescribeStatement.getObject();
        String tablename = parseName(object);
        // check if table exists and get Table info
        metaEventOperation.checkAndGetTable(tablename, true);

        DescTablePlan descTable = new DescTablePlan(tablename);
        context.setPlan(descTable);
        LOG.debug("DescTablePlan " + descTable.toString());
    }

    /**
     * Process Show Tables Statement and generate Execute Plan
     * 
     */
    private void getShowTablesPlan(ParseContext context, WaspSqlShowTablesStatement sqlShowTablesStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        ShowTablesPlan showTables = null;
        SQLExpr like = sqlShowTablesStatement.getLike();
        SQLExpr where = sqlShowTablesStatement.getWhere();
        if (like == null && where == null) {
            showTables = new ShowTablesPlan(ShowTablesPlan.ShowTablesType.ALL);
        } else if (like != null) {
            showTables = new ShowTablesPlan(ShowTablesPlan.ShowTablesType.LIKE);
            String likePattern = parseString(like);
            showTables.setLikePattern(likePattern);
        } else if (where != null) {
            throw new UnsupportedException("Show tables where not Unsupported!");
        }
        context.setPlan(showTables);
        LOG.debug("ShowTablesPlan " + showTables.toString());
    }

    /**
     * Process Show Create Table Statement and generate Execute Plan
     * 
     */
    private void getShowCreateTablePlan(ParseContext context,
            WaspSqlShowCreateTableStatement sqlShowCreateTableStatement, MetaEventOperation metaEventOperation)
            throws IOException {
        ShowTablesPlan showTables = new ShowTablesPlan(ShowTablesPlan.ShowTablesType.CREATE);
        SQLExpr name = sqlShowCreateTableStatement.getName();
        String tablename = parseName(name);
        // check if table exists and get Table info
        metaEventOperation.checkAndGetTable(tablename, true);

        showTables.setTablename(tablename);
        context.setPlan(showTables);
        LOG.debug("ShowTablesPlan " + showTables.toString());
    }

    /**
     * Process Alter Table Statement and generate Execute Plan
     * 
     */
    private void getAlterTablePlan(ParseContext context, MySqlAlterTableStatement sqlAlterTableStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        SQLExprTableSource tableSource = sqlAlterTableStatement.getTableSource();
        String tableName = parseFromClause(tableSource);
        // check if table exists and get Table info
        FTable oldTable = metaEventOperation.checkAndGetTable(tableName, true);

        FTable newTable = FTable.clone(oldTable);

        List<SQLAlterTableItem> items = sqlAlterTableStatement.getItems();
        for (SQLAlterTableItem item : items) {
            if (item instanceof WaspSqlAlterTableChangeColumn) {
                // Alter Table Change Column
                WaspSqlAlterTableChangeColumn changeColumn = (WaspSqlAlterTableChangeColumn) item;

                SQLName columnName = changeColumn.getColumnName();
                LinkedHashMap<String, Field> ftableColumns = newTable.getColumns();
                String oldColumnName = parseName(columnName);
                // Table have this column and this column is not primary key
                metaEventOperation.checkFieldExists(oldTable, oldColumnName);
                metaEventOperation.checkFieldNotInPrimaryKeys(oldTable, oldColumnName);
                // Check column not in a index
                metaEventOperation.checkColumnNotInIndex(oldTable, oldColumnName);

                // Which column(index) to change
                Field field = ftableColumns.get(oldColumnName); // Change this Field
                SQLColumnDefinition newColumnDefinition = changeColumn.getNewColumnDefinition();
                // ColumnFamily specify do not supported.
                if (newColumnDefinition instanceof WaspSqlColumnDefinition) {
                    WaspSqlColumnDefinition waspSqlColumnDefinition = (WaspSqlColumnDefinition) newColumnDefinition;
                    if (waspSqlColumnDefinition.getColumnFamily() != null) {
                        throw new UnsupportedException("Alter Table, columnFamily specify do not supported.");
                    }
                }
                if (newColumnDefinition.getDataType() != null) {
                    field.setType(parse(newColumnDefinition.getDataType()));
                }
                String newColumnName = parseName(newColumnDefinition.getName());
                if (!oldColumnName.equals(newColumnName)) { // Change column name
                    for (Field f : ftableColumns.values()) {
                        if (f.getName().equalsIgnoreCase(newColumnName)) {
                            throw new UnsupportedException(
                                    "Unsupported. Rename one column to a column that already column "
                                            + newColumnName);
                        }
                    }
                    field.setName(newColumnName);
                }
            } else if (item instanceof MySqlAlterTableAddColumn) {
                // Alter Table Add Column
                MySqlAlterTableAddColumn addColumn = (MySqlAlterTableAddColumn) item;
                List<SQLColumnDefinition> columns = addColumn.getColumns();
                boolean first = addColumn.isFirst();
                SQLName afterColumn = addColumn.getAfterColumn();
                LinkedHashMap<String, Field> ftableColumns = newTable.getColumns();

                List<Field> addFields = convertColumnDefForAlterTable(columns);
                // check Duplicate column name
                metaEventOperation.areLegalTableColumns(ftableColumns.values(), addFields);
                // Do not support add ColumnFamily dynamic right now.
                metaEventOperation.checkColumnFamilyName(ftableColumns.values(), addFields);
                if (first) {
                    this.addFieldByPosition(-1, addFields, ftableColumns, newTable);
                } else if (afterColumn != null) {
                    int index = getIndex(parseName(afterColumn), ftableColumns);
                    this.addFieldByPosition(index, addFields, ftableColumns, newTable);
                } else {
                    int index = ftableColumns.size() - 1;
                    this.addFieldByPosition(index, addFields, ftableColumns, newTable);
                }
            } else if (item instanceof SQLAlterTableDropColumnItem) {
                // Alter Table Drop Column
                SQLAlterTableDropColumnItem dropColumn = (SQLAlterTableDropColumnItem) item;
                SQLName columnName = dropColumn.getColumnName();
                String cname = parseName(columnName);
                // This column is not primary key
                metaEventOperation.checkFieldNotInPrimaryKeys(oldTable, cname);
                // Check column not in a index, if you want to drop the column you
                // should drop the index first
                metaEventOperation.checkColumnNotInIndex(oldTable, cname);

                LinkedHashMap<String, Field> ftableColumns = newTable.getColumns();
                Field field = ftableColumns.remove(cname);
                if (field == null) {
                    throw new UnsupportedException("Unsupported Do not find this column "
                            + SQLUtils.toSQLString(((SQLAlterTableDropColumnItem) item).getColumnName()));
                }
                newTable.setColumns(ftableColumns);
            } else {
                throw new UnsupportedException(SQLUtils.toSQLString(item) + " SQLAlterTableItem Unsupported");
            }
        }

        AlterTablePlan alterTable = new AlterTablePlan(oldTable, newTable);
        context.setPlan(alterTable);
        LOG.debug("AlterTablePlan " + alterTable.toString());
    }

    private void addFieldByPosition(int index, List<Field> addFields, LinkedHashMap<String, Field> ftableColumns,
            FTable newTable) {
        LinkedHashMap<String, Field> finalColumns = new LinkedHashMap<String, Field>();
        int i = 0;
        if (index < 0) {
            for (Field addField : addFields) {
                finalColumns.put(addField.getName(), addField);
            }
        }
        for (Field field : ftableColumns.values()) {
            if (index == i) {
                finalColumns.put(field.getName(), field);
                for (Field addField : addFields) {
                    finalColumns.put(addField.getName(), addField);
                }
            } else {
                finalColumns.put(field.getName(), field);
            }
            i++;
        }
        newTable.setColumns(finalColumns);
    }

    /**
     * Process Drop Table Statement and generate Execute Plan
     * 
     */
    private void getDropTablePlan(ParseContext context, SQLDropTableStatement sqlDropTableStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        List<SQLExprTableSource> tableSources = sqlDropTableStatement.getTableSources();
        List<String> tableNames = new ArrayList<String>(tableSources.size());
        boolean ifExists = ((WaspSqlDropTableStatement) sqlDropTableStatement).isIfExists();
        for (SQLExprTableSource tableSource : tableSources) {
            String tableName = parseFromClause(tableSource);
            // check if table exists and get Table info
            try {
                metaEventOperation.checkAndGetTable(tableName, true);
            } catch (TableNotFoundException e) {
                if (!ifExists) {
                    throw e;
                } else {
                    continue;
                }
            }
            tableNames.add(tableName);
        }

        DropTablePlan dropTable = new DropTablePlan(tableNames);
        context.setPlan(dropTable);
        LOG.debug("DropTablePlan " + dropTable.toString());
    }

    /**
     * Process Create Table Statement and generate Execute Plan
     * 
     */
    private void getCreateTablePlan(ParseContext context, WaspSqlCreateTableStatement waspSqlCreateTableStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        /**
         * example String sql3 = "CREATE TABLE User {Required Int64 user_id;
         * Required String name; Optional String phone;} primary key(user_id),ENTITY
         * GROUP ROOT, Entity Group Key(user_id);" ; String sql4 = "CREATE TABLE
         * Photo { Required Int64 user_id columnfamily cf comment 'aaa'; Required
         * Int32 photo_id comment 'child primary key'; Required Int64 time; Required
         * String full_url; Optional String thumbnail_url; Repeated String tag; }
         * primary key(user_id, photo_id) IN TABLE user,ENTITY GROUP KEY(user_id)
         * references User;";
         */

        // Table Info
        SQLExprTableSource tableSource = waspSqlCreateTableStatement.getTableSource();
        String tableName = parseFromClause(tableSource);
        // Check Table Name is legal.
        metaEventOperation.isLegalTableName(tableName);
        // Check if the table exists
        boolean tableNotExit = metaEventOperation.checkTableNotExists(tableName, true);
        if (!tableNotExit) {
            if (waspSqlCreateTableStatement.isIfNotExiists()) {
                context.setPlan(new NotingTodoPlan());
                LOG.debug("table " + tableName + " exits , isIfNotExiists is true, ignore");
                return;
            } else {
                throw new TableExistsException(tableName + " is already exists!");
            }
        }

        // Table category.
        WaspSqlCreateTableStatement.TableCategory category = waspSqlCreateTableStatement.getCategory();
        FTable.TableType tableType = FTable.TableType.CHILD;
        if (category != null && category == WaspSqlCreateTableStatement.TableCategory.ROOT) {
            tableType = FTable.TableType.ROOT;
        }

        // Primary Key.
        List<SQLExpr> primaryKeysSQLExpr = waspSqlCreateTableStatement.getPrimaryKeys();
        // table columns.
        List<SQLTableElement> tableElementList = waspSqlCreateTableStatement.getTableElementList(); // columns info
        LinkedHashMap<String, Field> columns = new LinkedHashMap<String, Field>();
        for (SQLTableElement element : tableElementList) {
            Field field = parse(element);
            columns.put(field.getName(), field);
        }
        // Check if columns are legal.
        metaEventOperation.areLegalTableColumns(null, columns.values());
        checkFamilyLegal(columns.values(), metaEventOperation);

        // Primary keys check will be done in this following method
        LinkedHashMap<String, Field> primaryKeys = parse(primaryKeysSQLExpr, columns);

        long createTime = System.currentTimeMillis();
        long lastAccessTime = createTime;
        String owner = "me";
        FTable table = new FTable(null, tableName, tableType, owner, createTime, lastAccessTime, columns,
                primaryKeys, primaryKeys.entrySet().iterator().next().getValue());
        SQLExpr entityGroupKeySQLExpr = waspSqlCreateTableStatement.getEntityGroupKey();
        Field entityGroupKey = primaryKeys.get(parseName(entityGroupKeySQLExpr));
        if (entityGroupKey == null) {
            throw new UnsupportedException(entityGroupKeySQLExpr + " is ForeignKey, but don't in primaryKeys.");
        }
        table.setEntityGroupKey(entityGroupKey);

        if (tableType == FTable.TableType.CHILD) {
            String parentName = parseFromClause(waspSqlCreateTableStatement.getInTableName());
            table.setParentName(parentName);
            if (!parentName.equals(parseFromClause(waspSqlCreateTableStatement.getReferenceTable()))) {
                throw new UnsupportedException(" in table " + waspSqlCreateTableStatement.getInTableName()
                        + " != references table " + waspSqlCreateTableStatement.getReferenceTable());
            }

            // Check parent's EGK equals child's EGK.
            TableSchemaCacheReader reader = TableSchemaCacheReader.getInstance(configuration);
            FTable parentTable = reader.getSchema(parentName);
            if (parentTable == null) {
                parentTable = TableSchemaCacheReader.getService(reader.getConf()).getTable(tableName);
            }
            if (parentTable == null) {
                throw new TableNotFoundException("Not found parent table:" + parentName);
            }

            if (!parentTable.getEntityGroupKey().getName().equals(table.getEntityGroupKey().getName())) {
                throw new UnsupportedException(
                        "Parent" + parentName + "'s egk doesn't equals Child" + tableName + "'s egk.");
            }

            // Check child's PKS contains parent's PKS.
            for (Field parentPrimaryKey : parentTable.getPrimaryKeys().values()) {
                boolean found = table.getPrimaryKeys().containsKey(parentPrimaryKey.getName());
                if (!found) {
                    throw new UnsupportedException("Child's pks must contains parent's pks.");
                }
            }
        }

        SQLPartitioningClause partitioning = waspSqlCreateTableStatement.getPartitioning();
        byte[][] splitKeys = null;
        if (partitioning != null) {
            if (table.isRootTable()) {
                if (partitioning instanceof WaspSqlPartitionByKey) {
                    WaspSqlPartitionByKey partitionKey = (WaspSqlPartitionByKey) partitioning;
                    byte[] start = convert(null, partitionKey.getStart());
                    byte[] end = convert(null, partitionKey.getEnd());
                    int partitionCount = convertToInt(partitionKey.getPartitionCount());
                    splitKeys = Bytes.split(start, end, partitionCount - 3);
                } else {
                    throw new UnsupportedException("Unsupported SQLPartitioningClause " + partitioning);
                }
            } else {
                throw new UnsupportedException("Partition by only supported for Root Table");
            }
        }
        CreateTablePlan createTable = new CreateTablePlan(table, splitKeys);
        context.setPlan(createTable);
        LOG.debug("CreateTablePlan " + createTable.toString());
    }

    private void checkFamilyLegal(Collection<Field> newColumns, MetaEventOperation metaEventOperation)
            throws IOException {
        for (Field newColumn : newColumns) {
            if (!newColumn.getFamily().equals(FConstants.COLUMNFAMILYNAME_STR)) {
                metaEventOperation.isLegalFamilyName(newColumn.getFamily());
            }
        }
    }

    private LinkedHashMap<String, Field> parse(List<SQLExpr> particularColumns,
            LinkedHashMap<String, Field> columns) throws UnsupportedException {
        LinkedHashMap<String, Field> particularFields = new LinkedHashMap<String, Field>(particularColumns.size());
        for (SQLExpr expr : particularColumns) {
            String name = parseName(expr);
            Field field = columns.get(name);
            if (field == null) {
                throw new UnsupportedException("Unsupported table don't have this primaryKey " + expr);
            }
            particularFields.put(name, field);
        }
        return particularFields;
    }

    private Field parse(SQLTableElement tableElement) throws UnsupportedException {
        Field field = null;
        FieldKeyWord keyWord = null;
        if (tableElement instanceof WaspSqlColumnDefinition) {
            WaspSqlColumnDefinition column = (WaspSqlColumnDefinition) tableElement;
            if (column.getColumnConstraint() == FieldKeyWord.REQUIRED) {
                keyWord = FieldKeyWord.REQUIRED;
            } else if (column.getColumnConstraint() == FieldKeyWord.OPTIONAL) {
                keyWord = FieldKeyWord.OPTIONAL;
            } else if (column.getColumnConstraint() == FieldKeyWord.REPEATED) {
                keyWord = FieldKeyWord.REPEATED;
            } else {
                throw new UnsupportedException("Unsupported ColumnConstraint " + column.getColumnConstraint());
            }
            SQLName name = column.getName();
            SQLDataType dataType = column.getDataType();
            SQLName columnFamilyName = column.getColumnFamily();
            String columnFamily = FConstants.COLUMNFAMILYNAME_STR;
            if (columnFamilyName != null) {
                columnFamily = parseName(columnFamilyName);
            }
            field = new Field(keyWord, columnFamily, parseName(name), parse(dataType), column.getComment());
            return field;
        } else {
            throw new UnsupportedException("Unsupported SQLColumnDefinition " + tableElement);
        }
    }

    /**
     * Process Drop Index Statement and generate Execute Plan
     * 
     */
    private void getDropIndexPlan(ParseContext context, SQLDropIndexStatement sqlDropIndexStatement,
            MetaEventOperation metaEventOperation) throws IOException {
        SQLExpr indexNameExpr = sqlDropIndexStatement.getIndexName();
        SQLExpr table = sqlDropIndexStatement.getTableName();
        String tableName = parseName(table);

        // check if table exists and get Table info
        FTable ftable = metaEventOperation.checkAndGetTable(tableName, true);

        String indexName = parseName(indexNameExpr);
        metaEventOperation.checkIndexExists(ftable, indexName);

        DropIndexPlan dropIndex = new DropIndexPlan(indexName, tableName);
        context.setPlan(dropIndex);
        LOG.debug("DropIndexPlan " + dropIndex.toString());
    }

    /**
     * Process Create Index Statement and generate Execute Plan
     * 
     */
    private void getCreateIndexPlan(ParseContext context, WaspSqlCreateIndexStatement sqlCreateIndexStatement,
            MetaEventOperation metaEventOperation) throws IOException {

        // Index Name
        SQLName name = sqlCreateIndexStatement.getName();
        String indexName = parseName(name);
        metaEventOperation.isLegalIndexName(indexName);
        LOG.debug("Create Index SQL IndexName " + name);

        // Table Name
        SQLName table = sqlCreateIndexStatement.getTable();
        String tableName = parseName(table);
        LOG.debug("Create Index SQL TableName " + table);

        // check if table exists and get Table info
        FTable fTable = metaEventOperation.checkAndGetTable(tableName, true);

        // check the index not exists
        metaEventOperation.checkIndexNotExists(fTable, indexName);

        // Field
        List<SQLSelectOrderByItem> items = sqlCreateIndexStatement.getItems();
        LinkedHashSet<String> columns = new LinkedHashSet<String>(items.size());
        List<String> desc = new ArrayList<String>();
        for (SQLSelectOrderByItem item : items) {
            String columnName = parseName(item.getExpr());
            if (columns.contains(columnName)) {
                throw new UnsupportedException("Index have two same field '" + columnName + "'");
            } else {
                columns.add(columnName);
            }
            if (item.getType() == SQLOrderingSpecification.DESC) {
                desc.add(columnName);
            }
        }

        if (!metaEventOperation.isLegalDescFields(fTable, desc)) {
            throw new UnsupportedException("Currently we only support the ascending and descending time field.");
        }

        List<String> colList = new ArrayList<String>();
        colList.addAll(columns);
        if (metaEventOperation.arePrimaryKeys(fTable, colList)) {
            throw new UnsupportedException("Index keys is Primary Keys.");
        }
        if (metaEventOperation.containPrimaryKeys(fTable, colList)) {
            throw new UnsupportedException("Index keys contain all Primary Keys.");
        }

        LinkedHashMap<String, Field> indexKeys = metaEventOperation.checkAndGetFields(fTable, columns);
        // Check the indexKeys whether have Duplicate column name
        metaEventOperation.areLegalTableColumns(null, indexKeys.values());

        Index index = new Index(indexName, tableName, indexKeys);
        // Check if two index have the same columns and the same columns order
        metaEventOperation.checkTwoIndexWithSameColumn(fTable, index);

        index.setDesc(desc);
        index.setStoring(parse(sqlCreateIndexStatement.getStoringCols(), fTable.getColumns()));
        CreateIndexPlan createIndexPlan = new CreateIndexPlan(index);

        context.setPlan(createIndexPlan);
        LOG.debug("CreateIndexPlan " + createIndexPlan.toString());
    }

    public int getIndex(String name, LinkedHashMap<String, Field> columns) throws UnsupportedException {
        Iterator<Entry<String, Field>> iter = columns.entrySet().iterator();
        int i = 0;
        while (iter.hasNext()) {
            Field field = iter.next().getValue();
            if (field.getName().equals(name)) {
                return i;
            }
            i++;
        }
        throw new UnsupportedException(name + " is not in set");
    }

    public static List<Field> convertColumnDefForAlterTable(List<SQLColumnDefinition> columns)
            throws UnsupportedException {
        List<Field> addFields = new ArrayList<Field>();
        for (SQLColumnDefinition sqlColumnDefinition : columns) {
            addFields.add(convertColumnDefForAlterTable(sqlColumnDefinition));
        }
        return addFields;
    }

    private static Field convertColumnDefForAlterTable(SQLColumnDefinition column) throws UnsupportedException {
        Field field = new Field();
        if (column instanceof WaspSqlColumnDefinition) {
            WaspSqlColumnDefinition waspColumn = (WaspSqlColumnDefinition) column;
            if (waspColumn.getComment() != null) {
                field.setComment(waspColumn.getComment());
            }
            if (waspColumn.getColumnFamily() != null) {
                field.setFamily(parseName(waspColumn.getColumnFamily()));
            } else {
                field.setFamily(FConstants.COLUMNFAMILYNAME_STR);
            }
            field.setKeyWord(FieldKeyWord.OPTIONAL);
            field.setName(parseName(column.getName()));
            field.setType(parse(column.getDataType()));
        } else {
            field.setComment(column.getComment());
            field.setFamily(FConstants.COLUMNFAMILYNAME_STR);
            field.setKeyWord(FieldKeyWord.OPTIONAL);
            field.setName(parseName(column.getName()));
            field.setType(parse(column.getDataType()));
        }
        return field;
    }

    public static DataType parse(SQLDataType dataType) throws UnsupportedException {
        String typeName = dataType.getName();
        if (typeName.equalsIgnoreCase("INT32")) {
            return DataType.INT32;
        } else if (typeName.equalsIgnoreCase("INT64")) {
            return DataType.INT64;
        } else if (typeName.equalsIgnoreCase("STRING")) {
            return DataType.STRING;
        } else if (typeName.equalsIgnoreCase("FLOAT")) {
            return DataType.FLOAT;
        } else if (typeName.equalsIgnoreCase("DOUBLE")) {
            return DataType.DOUBLE;
        } else if (typeName.equalsIgnoreCase("PROTOBUF")) {
            return DataType.PROTOBUF;
        } else if (typeName.equalsIgnoreCase("DATETIME")) {
            return DataType.DATETIME;
        } else {
            throw new UnsupportedException("Unsupported SQLDataType " + dataType);
        }
    }

}