package util.sqlparse;

import bean.Column;
import bean.DataBase;
import bean.Schema;
import bean.Table;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;
import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLCaseExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddColumn;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropColumnItem;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableItem;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCreateStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateViewStatement;
import com.alibaba.druid.sql.ast.statement.SQLDDLStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLGrantStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;
import com.alibaba.druid.sql.ast.statement.SQLRevokeStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.ast.statement.SQLTableSource;
import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement;
import com.alibaba.druid.sql.ast.statement.SQLUnionQuery;
import com.alibaba.druid.sql.ast.statement.SQLUnionQueryTableSource;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.dialect.hive.ast.stmt.HiveAlterDatabaseStatement;
import com.alibaba.druid.sql.dialect.hive.ast.stmt.HiveCreateTableStatement;
import com.alibaba.druid.sql.visitor.SchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.util.StringUtils;
import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import util.DataUtil;
import util.JdbcUtil;
import util.SqlUtil;
import util.StringJoin;
import util.log.SQLLogger;
import util.sqlparse.visitor.common.Context;
import util.sqlparse.visitor.common.bean.FieldInfo;
import util.sqlparse.visitor.common.bean.SQLResult;
import util.sqlparse.visitor.common.bean.TableInfo;
import util.sqlparse.visitor.common.bean.ValueInfo;
import util.sqlparse.visitor.common.memo.FieldMemo;
import util.sqlparse.visitor.common.memo.TableMemo;
import util.sqlparse.visitor.common.scope.Scope;
import util.sqlparse.visitor.hive.HiveNameWrapper;
import util.sqlparse.visitor.hive.SQLParser;
import util.sqlparse.visitor.hive.visitor.FieldVisitor;
import util.sqlparse.visitor.hive.visitor.HiveScopeDialector;
import util.sqlparse.visitor.hive.visitor.ScopeVisitor;

public class HiveParse {
   public HashMap<String, List<Column>> tableList = new HashMap();
   public List<Column> columnList = new ArrayList();
   public Map<String, String> tableMap = new HashMap();
   public Map<String, String> unionTableMap = new HashMap();
   public Map<String, String> schemaMap = new HashMap();
   public Map<String, String> whereMap = new HashMap();
   public Map<String, String> params;
   public List<Map<String, Object>> whereList = new ArrayList();
   public List<Map<String, Object>> whereValueList = new ArrayList();
   public String dbType = "hive";

   public HiveParse(Map<String, String> params) {
      this.params = params;
   }

   public HiveParse() {
   }

   public String rowVisitControl(Map<String, Object> param) throws SQLException {
      String sql = (String)param.get("sql");
      if ("".equalsIgnoreCase(sql)) {
         return sql;
      } else {
         Map<String, String> tableKeyWord = (Map)param.get("tableKeyWord");
         Map<String, List<String>> modifyTable = (Map)param.get("modifyTable");
         String operateType = (String)param.get("operateType");
         List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, this.dbType);
         SQLStatement sqlStatement = (SQLStatement)stmtList.get(0);
         SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(DbType.of(this.dbType));
         sqlStatement.accept(visitor);
         Map<TableStat.Name, TableStat> tables = visitor.getTables();

         for(TableStat.Name tableName : tables.keySet()) {
            String name = tableName.getName();
            if (name.split("\\.").length > 1) {
               name = name.split("\\.")[1];
            }

            if (tableKeyWord.containsKey(name)) {
               TableStat tableStat = (TableStat)tables.get(tableName);
               String tableType = tableStat.toString();
               if (!tableType.equalsIgnoreCase(operateType)) {
                  modifyTable.remove(name);
                  tableKeyWord.remove(name);
               }
            }
         }

         sql = this.addWhereCondition(param);
         return sql;
      }
   }

   public String addWhereCondition(Map<String, Object> param) throws SQLException {
      String sql = (String)param.get("sql");
      String sqlType = getSqlType(sql, this.dbType);
      if ("select".equals(sqlType)) {
         SQLSelectQueryBlock query = this.aliParseSelect(this.dbType, sql, false);
         this.parseFromAndAddWhereCondition(query.getFrom(), param);
         sql = query.toString();
      } else if ("update".equals(sqlType)) {
         SQLUpdateStatement update = this.aliParseUpdate(this.dbType, sql);
         this.parseUpdateAndAddWhereCondition(update, param);
         sql = update.toString();
      } else if ("delete".equals(sqlType)) {
         SQLDeleteStatement delete = this.aliParseDelete(this.dbType, sql);
         this.parseDeleteAndAddWhereCondition(delete, param);
         sql = delete.toString();
      }

      return sql;
   }

   public void parseFromAndAddWhereCondition(SQLTableSource from, Map<String, Object> param) {
      if (from instanceof SQLExprTableSource) {
         Map<String, List<String>> modifyTable = (Map)param.get("modifyTable");
         Map<String, String> tableKeyWord = (Map)param.get("tableKeyWord");
         SQLExprTableSource fromContent = (SQLExprTableSource)from;
         String[] split = fromContent.getExpr().toString().split("\\.");
         String tableName = "";
         if (split.length > 1) {
            tableName = split[1];
         } else {
            tableName = split[0];
         }

         tableName = tableName.replaceAll("`", "");
         String tableAlisa = fromContent.getAlias();
         tableAlisa = tableAlisa == null ? tableName : tableAlisa;

         SQLObject parent;
         for(parent = fromContent.getParent(); !(parent instanceof SQLSelectQueryBlock) && !(parent instanceof SQLDeleteStatement); parent = parent.getParent()) {
         }

         if (parent instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock p = (SQLSelectQueryBlock)parent;

            for(String key : modifyTable.keySet()) {
               if (key.equalsIgnoreCase(tableName)) {
                  String id = (String)tableKeyWord.get(tableName);
                  String condition = "(" + tableAlisa + "." + id + " not in ('";
                  condition = condition + StringJoin.join((Iterable)modifyTable.get(tableName), "','") + "')";
                  condition = condition + " or " + tableAlisa + "." + id + " is null)";
                  p.addCondition(condition);
               }
            }
         } else if (parent instanceof SQLDeleteStatement) {
            SQLDeleteStatement p = (SQLDeleteStatement)parent;

            for(String key : modifyTable.keySet()) {
               if (key.equalsIgnoreCase(tableName)) {
                  String id = (String)tableKeyWord.get(tableName);
                  String condition = tableAlisa + "." + id + " not in ('";
                  condition = condition + StringJoin.join((Iterable)modifyTable.get(tableName), "','") + "')";
                  p.addCondition(condition);
               }
            }
         }
      } else if (from instanceof SQLJoinTableSource) {
         SQLJoinTableSource fromContent = (SQLJoinTableSource)from;
         SQLTableSource left = fromContent.getLeft();
         this.parseFromAndAddWhereCondition(left, param);
         SQLTableSource right = fromContent.getRight();
         this.parseFromAndAddWhereCondition(right, param);
      } else if (from instanceof SQLUnionQueryTableSource) {
         SQLUnionQueryTableSource fromContent = (SQLUnionQueryTableSource)from;
         String alias = fromContent.getAlias();
         SQLUnionQuery union = fromContent.getUnion();
         List<SQLSelectQueryBlock> unionList = new ArrayList();
         this.parseUnionFrom(union, unionList);
      } else if (from instanceof SQLSubqueryTableSource) {
         SQLSubqueryTableSource fromContent = (SQLSubqueryTableSource)from;
         SQLSelect select = fromContent.getSelect();
         SQLSelectQueryBlock query = (SQLSelectQueryBlock)select.getQuery();
         this.parseFromAndAddWhereCondition(query.getFrom(), param);
      }

   }

   public void parseUpdateAndAddWhereCondition(SQLUpdateStatement update, Map<String, Object> param) {
      Map<String, List<String>> modifyTable = (Map)param.get("modifyTable");
      Map<String, String> tableKeyWord = (Map)param.get("tableKeyWord");
      SQLName table = update.getTableName();
      String tableName = table.getSimpleName();
      String tableAlisa = tableName;
      String[] tableInfo = tableName.split("\\.");
      if (tableInfo.length > 1) {
         tableName = tableInfo[1];
         tableAlisa = tableInfo[0];
      }

      tableName = tableName;

      for(String key : modifyTable.keySet()) {
         if (key.equalsIgnoreCase(tableName)) {
            String id = (String)tableKeyWord.get(tableName);
            String condition = "(" + tableAlisa + "." + id + " not in ('";
            condition = condition + StringJoin.join((Iterable)modifyTable.get(tableName), "','") + "')";
            condition = condition + " or " + tableAlisa + "." + id + " is null)";
            update.addCondition(condition);
         }
      }

   }

   public void parseDeleteAndAddWhereCondition(SQLDeleteStatement delete, Map<String, Object> param) {
      Map<String, List<String>> modifyTable = (Map)param.get("modifyTable");
      Map<String, String> tableKeyWord = (Map)param.get("tableKeyWord");
      SQLName table = delete.getTableName();
      String tableName = table.getSimpleName();
      String tableAlisa = tableName;
      String[] tableInfo = tableName.split("\\.");
      if (tableInfo.length > 1) {
         tableName = tableInfo[1];
         tableAlisa = tableInfo[0];
      }

      tableName = tableName;

      for(String key : modifyTable.keySet()) {
         if (key.equalsIgnoreCase(tableName)) {
            String id = (String)tableKeyWord.get(tableName);
            String condition = "(" + tableAlisa + "." + id + " not in ('";
            condition = condition + StringJoin.join((Iterable)modifyTable.get(tableName), "','") + "')";
            condition = condition + " or " + tableAlisa + "." + id + " is null)";
            delete.addCondition(condition);
         }
      }

   }

   public String parseSql() {
      String sql = ((String)this.params.get("sql")).replaceAll("`", "");
      String schema = (String)this.params.get("schema");
      String parseSql = sql;
      Map<String, Object> paramMap = new HashMap();
      paramMap.put("schema", schema);
      paramMap.put("sql", sql);
      paramMap.put("params", this.params);

      try {
         this.parseSqlStructure(paramMap);
         parseSql = DataUtil.dataDesensitization(paramMap);
      } catch (Exception throwables) {
         throwables.printStackTrace();
         SqlUtil.getAnalysisSqlList().add(sql);
      }

      return parseSql;
   }

   public String parseSql_column() {
      String sql = (String)this.params.get("sql");
      String schema = (String)this.params.get("schema");
      String parseSql = sql;
      Map<String, Object> paramMap = new HashMap();
      paramMap.put("schema", schema);
      paramMap.put("sql", sql);
      paramMap.put("params", this.params);

      try {
         this.parseSqlStructure(paramMap);
         parseSql = DataUtil.columnDesensitization(paramMap);
      } catch (Exception throwables) {
         throwables.printStackTrace();
      }

      return parseSql;
   }

   public String parseSql_table() {
      String sql = (String)this.params.get("sql");
      String schema = (String)this.params.get("schema");
      String parseSql = sql;
      Map<String, Object> paramMap = new HashMap();
      paramMap.put("schema", schema);
      paramMap.put("sql", sql);
      paramMap.put("params", this.params);

      try {
         this.parseSqlStructure(paramMap);
         parseSql = DataUtil.tableDesensitization(paramMap);
      } catch (Exception throwables) {
         throwables.printStackTrace();
      }

      return parseSql;
   }

   public String parseSql_data() {
      String sql = (String)this.params.get("sql");
      String schema = (String)this.params.get("schema");
      String parseSql = sql;
      Map<String, Object> paramMap = new HashMap();
      paramMap.put("schema", schema);
      paramMap.put("sql", sql);
      paramMap.put("params", this.params);

      try {
         this.parseSqlStructure(paramMap);
         parseSql = DataUtil.desensitization(paramMap);
      } catch (Exception throwables) {
         throwables.printStackTrace();
      }

      return parseSql;
   }

   public static String getIpPort(String url) {
      String[] urlSplit = url.split("/");
      String ip_port = urlSplit[2];
      return ip_port;
   }

   public void parseSqlStructure(Map<String, Object> paramMap) throws SQLException {
      String sql = (String)this.params.get("sql");
      boolean isEncryption = this.params.containsKey("encryption");
      String sqlType = getSqlType(sql, this.dbType);
      List<SQLStatement> statements = SQLUtils.parseStatements(sql, "hive");
      if (!"union".equals(sqlType) && !"select".equals(sqlType) && !"with_select".equals(sqlType) && !"insert".equals(sqlType) && !"update".equals(sqlType) && !"delete".equals(sqlType) && !"insert_select".equals(sqlType)) {
         if ("create".equals(sqlType)) {
            SQLCreateStatement drop = this.parseCreate(this.dbType, sql);
            paramMap.put("type", "create");
            paramMap.put("statement", drop);
            paramMap.put("sql", drop.toString());
         } else if ("alter".equals(sqlType)) {
            HiveAlterDatabaseStatement alter = this.parseAlter(this.dbType, sql);
            paramMap.put("type", "alter");
            paramMap.put("statement", alter);
            paramMap.put("sql", alter.toString());
         } else if ("drop".equals(sqlType)) {
            SQLDropTableStatement drop = this.parseDrop(this.dbType, sql);
            paramMap.put("type", "drop");
            paramMap.put("statement", drop);
            paramMap.put("sql", drop.toString());
         } else if ("grant".equals(sqlType)) {
            SQLGrantStatement grant = this.parseGrant(this.dbType, sql);
            paramMap.put("type", "grant");
            paramMap.put("statement", grant);
            paramMap.put("sql", grant.toString());
         } else if ("revoke".equals(sqlType)) {
            SQLRevokeStatement revoke = this.parseRevoke(this.dbType, sql);
            paramMap.put("type", "revoke");
            paramMap.put("statement", revoke);
            paramMap.put("sql", revoke.toString());
         } else if ("drop_other".equals(sqlType)) {
            SQLDDLStatement drop = this.parseDropOther(this.dbType, sql);
            paramMap.put("type", "drop");
            paramMap.put("statement", drop);
            paramMap.put("sql", drop.toString());
         } else {
            List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, this.dbType);
            SQLStatement statement = (SQLStatement)stmtList.get(0);
            paramMap.put("type", sqlType);
            paramMap.put("statement", statement);
            paramMap.put("sql", statement.toString());
         }
      } else {
         String permission = (String)this.params.get("permission");
         String url = (String)this.params.get("url");
         String schema = (String)this.params.get("schema");
         String ip_port = getIpPort(url);
         DataBase dataBase = new DataBase();

         for(DataBase db : JdbcUtil.dataBaseList) {
            if (ip_port.equalsIgnoreCase(db.getIp_port())) {
               dataBase = db;
               break;
            }
         }

         SQLParser sqlParser = new SQLParser();
         SQLResult parse = sqlParser.parse(sql, dataBase, schema);
         if ("alter".equals(sqlType)) {
            parse.outputs = parse.fields;
         }

         List<FieldInfo> columnlist;
         if ("masking".equalsIgnoreCase(permission)) {
            columnlist = parse.outputs;
         } else if ("full".equalsIgnoreCase(permission)) {
            columnlist = parse.fields;
         } else if (isEncryption) {
            if (!"insert".equalsIgnoreCase(sqlType) && !"update".equalsIgnoreCase(sqlType)) {
               columnlist = parse.outputs;
            } else {
               columnlist = parse.fields;
            }
         } else {
            columnlist = parse.outputs;
         }

         HiveNameWrapper wrapper = new HiveNameWrapper(dataBase.simpleCache);
         Set<String> tabSet = new HashSet();

         for(FieldInfo fieldInfo : columnlist) {
            List<FieldInfo> fieldInfoList = new ArrayList();
            if (fieldInfo.getRelations().size() > 0) {
               fieldInfoList = fieldInfo.getRelations();
            } else {
               fieldInfoList.add(fieldInfo);
            }

            for(FieldInfo info : fieldInfoList) {
               String name = info.getName();
               if (!StringUtils.isEmpty(name)) {
                  Map<String, TableInfo> tables = info.getTables();
                  String tableName = "";
                  String schemaName = "";

                  for(String s : tables.keySet()) {
                     String[] segment = s.split("\\.");
                     if (!StringUtils.isEmpty(segment[0]) && !StringUtils.isEmpty(segment[1])) {
                        schemaName = segment[0];
                        tableName = segment[1];
                        break;
                     }
                  }

                  if (tableName.length() == 0 && info.getTables().size() > 0) {
                     for(Map.Entry<String, TableInfo> entry : info.getTables().entrySet()) {
                        TableInfo tbInfo = (TableInfo)entry.getValue();
                        if (tbInfo != null && tbInfo.getTable() != null && !StringUtils.isEmpty(tbInfo.getTable().schema) && !StringUtils.isEmpty(tbInfo.getTable().name)) {
                           tableName = tbInfo.getTable().name;
                           schemaName = tbInfo.getTable().schema;
                           break;
                        }
                     }
                  }

                  schemaName = wrapper.unwrapSchema(schemaName);
                  tableName = wrapper.unwrapTable(schemaName, tableName);
                  tabSet.add(tableName);
                  if (!name.equalsIgnoreCase("ROWID") && !name.equalsIgnoreCase("ORA_ROWSCN")) {
                     Column column = new Column();
                     column.setColumnName(tableName + "." + name);
                     column.setTable(tableName);
                     column.setSchema(schemaName);
                     column.setColumnAlisa(info.getAlias());
                     column.setSubQuery(false);
                     this.columnList.add(column);
                  }
               }
            }
         }

         for(TableInfo table : parse.tables) {
            TableMemo tb = table.getTable();
            String schName = wrapper.unwrapSchema(tb.schema);
            String tbName = wrapper.unwrapTable(schName, tb.name);
            this.tableMap.put(tbName, tbName);
            this.schemaMap.put(tbName, schName);
         }

         for(ValueInfo value : parse.values) {
            FieldMemo field = value.field;
            this.whereMap.put(field.name, value.value.toString());
            Map<String, Object> map = new HashMap();
            String str = value.getValueNames();
            map.put("column", str.substring(0, str.length() - 1));
            map.put("columnValue", value.value.toString());
            map.put("type", value.operator);
            this.whereList.add(map);
         }

         paramMap.put("whereList", this.whereList);
         paramMap.put("statement", parse.statement);
         paramMap.put("type", sqlType);
         paramMap.put("sql", parse.statement.toString());
         wrapper.unwrapFullColumns(this.columnList);
      }

      paramMap.put("whereMap", this.whereMap);
      paramMap.put("tableMap", this.tableMap);
      paramMap.put("columnList", this.columnList);
      paramMap.put("schemaMap", this.schemaMap);
   }

   public String parseSelect(String dbType, String sql) throws SQLException {
      SQLSelectQueryBlock query = this.aliParseSelect(dbType, sql, true);
      this.parseFrom(query.getFrom(), (Map)null);
      sql = this.parseSelectStar(query);
      query = this.aliParseSelect(dbType, sql, false);
      this.parseQueryColumns(query, "");
      sql = this.parseSelectStar(query);
      return sql;
   }

   public SQLSelectQueryBlock aliParseSelect(String dbType, String sql, boolean first) throws SQLException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLSelectStatement stmt = (SQLSelectStatement)stmtList.get(0);
         SQLSelect select = stmt.getSelect();
         SQLSelectQueryBlock query = (SQLSelectQueryBlock)select.getQuery();
         if (first) {
            query = JdbcUtil.hiveJudgeViewAndModify(query, this.params, this.tableList);
         }

         return query;
      }
   }

   public Map<String, String> parseFrom(SQLTableSource from, Map<String, String> map) throws SQLException {
      String className = from.getClass().getName();
      if (className.contains("SQLExprTableSource")) {
         SQLExprTableSource fromContent = (SQLExprTableSource)from;
         String tableName = fromContent.getExpr().toString();
         String tableAlisa = fromContent.getAlias();
         String exprClass = fromContent.getExpr().getClass().getName();
         if (exprClass.contains("SQLPropertyExpr")) {
            SQLPropertyExpr expr = (SQLPropertyExpr)fromContent.getExpr();
            tableName = expr.getName();
            String owner = expr.getOwner().toString();
            this.schemaMap.put(tableName, owner);
         } else {
            String owner = (String)this.params.get("schema");
            SQLIdentifierExpr expr = (SQLIdentifierExpr)fromContent.getExpr();
            SQLPropertyExpr pExpr = new SQLPropertyExpr();
            pExpr.setName(expr.getName());
            pExpr.setOwner(owner);
            fromContent.setExpr((SQLExpr)pExpr);
            this.schemaMap.put(tableName, owner);
         }

         if (map == null) {
            this.tableMap.put(tableAlisa == null ? tableName : tableAlisa, tableName);
         } else {
            map.put(tableAlisa == null ? tableName : tableAlisa, tableName);
         }
      } else if (className.contains("SQLJoinTableSource")) {
         SQLJoinTableSource fromContent = (SQLJoinTableSource)from;
         SQLTableSource left = fromContent.getLeft();
         this.parseFrom(left, map);
         SQLTableSource right = fromContent.getRight();
         this.parseFrom(right, map);
      } else if (className.contains("SQLUnionQueryTableSource")) {
         SQLUnionQueryTableSource fromContent = (SQLUnionQueryTableSource)from;
         String alias = fromContent.getAlias();
         SQLUnionQuery union = fromContent.getUnion();
         List<SQLSelectQueryBlock> unionList = new ArrayList();
         this.parseUnionFrom(union, unionList);
         this.paresUnionFromSql(unionList, alias);
         if (map == null) {
            this.tableMap.put(alias, alias);
         } else {
            map.put(alias, alias);
         }
      } else if (className.contains("SQLSubqueryTableSource")) {
         SQLSubqueryTableSource fromContent = (SQLSubqueryTableSource)from;
         SQLSelect select = fromContent.getSelect();
         if (map == null) {
            SQLSelectQueryBlock query = (SQLSelectQueryBlock)select.getQuery();
            this.parseFrom(query.getFrom(), (Map)null);
            String sql = this.parseSelectStar(query);
            query = this.aliParseSelect((String)this.params.get("dbType"), sql, false);
            this.parseQueryColumns(query, fromContent.getAlias());
            this.tableMap.put(fromContent.getAlias(), fromContent.getAlias());
         } else {
            map.put(fromContent.getAlias(), fromContent.getAlias());
         }
      }

      return this.tableMap;
   }

   public void parseUnionFrom(SQLUnionQuery union, List<SQLSelectQueryBlock> unionList) {
      List<SQLSelectQuery> queryList = union.getRelations();

      for(int i = 0; i < queryList.size(); ++i) {
         SQLSelectQuery selectQuery = (SQLSelectQuery)queryList.get(i);
         String className = selectQuery.getClass().getName();
         if (className.contains("SQLUnionQuery")) {
            SQLUnionQuery unQuery = (SQLUnionQuery)selectQuery;
            this.parseUnionFrom(unQuery, unionList);
         } else {
            SQLSelectQueryBlock query = (SQLSelectQueryBlock)selectQuery;
            unionList.add(query);
         }
      }

   }

   public void paresUnionFromSql(List<SQLSelectQueryBlock> unionList, String alias) throws SQLException {
      for(int i = 0; i < unionList.size(); ++i) {
         this.unionTableMap.clear();
         SQLSelectQueryBlock query = (SQLSelectQueryBlock)unionList.get(i);
         SQLExprTableSource fromContent = (SQLExprTableSource)query.getFrom();
         String tableName = fromContent.getExpr().toString();
         String tableAlisa = fromContent.getAlias();
         this.unionTableMap.put(tableAlisa == null ? tableName : tableAlisa, tableName);
         this.tableMap.put(tableAlisa == null ? tableName : tableAlisa, tableName);
         String sql = this.parseSelectStar(query);
         if (i == unionList.size() - 1) {
            query = this.aliParseSelect(this.dbType, sql, false);
            this.parseQueryColumns(query, alias);
         }
      }

   }

   public String parseSelectStar(SQLSelectQueryBlock query) throws SQLException {
      String sql = query.toString();

      for(SQLSelectItem field : query.getSelectList()) {
         String[] columns = field.getExpr().toString().trim().split("\\.");
         String alias = columns[0];
         String column = columns.length == 1 ? columns[0] : columns[1];
         if ("*".equalsIgnoreCase(column)) {
            Map<String, String> map = new HashMap();
            this.parseFrom(query.getFrom(), map);
            sql = this.columnReplaceStar(sql, alias, map);
         } else if (columns.length == 1) {
            List<Column> sqlExprList = new ArrayList();
            SQLExpr fieldExpr = field.getExpr();
            this.parseSQLExpr(fieldExpr, fieldExpr, sqlExprList, false);

            for(Column col : sqlExprList) {
               this.columnAddTableAlias(col.getExpr());
            }

            sql = query.toString();
         }
      }

      return sql;
   }

   public void columnAddTableAlias(SQLExpr fieldExpr) throws SQLException {
      String column = fieldExpr.toString();
      if (!"*".equalsIgnoreCase(column)) {
         String url = (String)this.params.get("url");
         String schema = (String)this.params.get("schema");
         String ip_port = url.split("/")[2];
         SQLIdentifierExpr expr = (SQLIdentifierExpr)fieldExpr;
         String sql = fieldExpr.toString();

         for(String s : this.tableList.keySet()) {
            for(Column tableColumn : this.tableList.get(s)) {
               String[] split = tableColumn.getColumnName().split("\\.");
               if (split.length >= 2) {
                  String columnName = split[1];
                  if (columnName.equalsIgnoreCase(column.replaceAll("[\"`]", ""))) {
                     sql = sql.replaceAll(column, s + "." + column);
                     expr.setName(sql);
                     return;
                  }
               }
            }
         }

         for(String key : this.unionTableMap.keySet()) {
            String schema_temp = (String)this.schemaMap.get(this.unionTableMap.get(key));
            String schema_real = schema_temp == null ? schema : schema_temp;

            for(Column tableColumn : JdbcUtil.getTableColumns(ip_port, schema_real, (String)this.unionTableMap.get(key), key, this.tableList)) {
               String[] split = tableColumn.getColumnName().split("\\.");
               if (split.length >= 2) {
                  String columnName = split[1];
                  if (columnName.equalsIgnoreCase(column.replaceAll("[\"`]", ""))) {
                     sql = sql.replaceAll(column, key + "." + column);
                     expr.setName(sql);
                     return;
                  }
               }
            }
         }

         for(String key : this.tableMap.keySet()) {
            String schema_temp = (String)this.schemaMap.get(this.tableMap.get(key));
            String schema_real = schema_temp == null ? schema : schema_temp;

            for(Column tableColumn : JdbcUtil.getTableColumns(ip_port, schema_real, (String)this.tableMap.get(key), key, this.tableList)) {
               String[] split = tableColumn.getColumnName().split("\\.");
               if (split.length >= 2) {
                  String columnName = split[1];
                  if (columnName.equalsIgnoreCase(column.replaceAll("[\"`]", ""))) {
                     sql = sql.replaceAll(column, key + "." + column);
                     expr.setName(sql);
                     return;
                  }
               }
            }
         }

      }
   }

   public String columnReplaceStar(String sql, String alias, Map<String, String> tableMap) throws SQLException {
      String url = (String)this.params.get("url");
      String schema = (String)this.params.get("schema");
      String ip_port = url.split("/")[2];
      List<String> columns = new ArrayList();
      if ("*".equalsIgnoreCase(alias)) {
         for(String key : tableMap.keySet()) {
            String schema_temp = (String)this.schemaMap.get(tableMap.get(key));
            String schema_real = schema_temp == null ? schema : schema_temp;
            List<Column> tableColumns = JdbcUtil.getTableColumns(ip_port, schema_real, (String)tableMap.get(key), key, this.tableList);
            List<String> colList = new ArrayList();

            for(int i = 0; i < tableColumns.size(); ++i) {
               Column col = (Column)tableColumns.get(i);
               String column = "";
               if (col.getColumnAlisa() != null) {
                  column = key + "." + col.getColumnAlisa();
               } else {
                  column = key + "." + col.getColumnName().split("\\.")[1];
               }

               colList.add(column);
            }

            columns.addAll(colList);
         }

         if (columns.size() > 0) {
            sql = sql.replaceFirst("\\*", StringJoin.join(columns, ","));
         }
      } else if (tableMap.get(alias) != null) {
         List<Column> tableColumns = JdbcUtil.getTableColumns(ip_port, schema, (String)tableMap.get(alias), alias, this.tableList);

         for(int i = 0; i < tableColumns.size(); ++i) {
            Column col = (Column)tableColumns.get(i);
            String column = "";
            if (col.getColumnAlisa() != null) {
               column = alias + "." + col.getColumnAlisa();
            } else {
               column = alias + "." + col.getColumnName().split("\\.")[1];
            }

            columns.add(column);
         }

         if (columns.size() > 0) {
            sql = sql.replaceFirst(alias + "\\.\\*", StringJoin.join(columns, ","));
         }
      }

      return sql;
   }

   public void parseQueryColumns(SQLSelectQueryBlock query, String alias) throws SQLException {
      List<SQLSelectItem> fieldList = query.getSelectList();
      boolean isSub = !"".equalsIgnoreCase(alias);
      List<Column> temp = new ArrayList();

      for(SQLSelectItem field : fieldList) {
         SQLExpr fileExpr = field.getExpr();
         String className = fileExpr.getClass().toString();
         if (className.contains("SQLQueryExpr")) {
            String subQuery = this.parseSelect(this.dbType, field.getExpr().toString().replaceAll("\\(|\\)", ""));
            SQLSelectStatement stmt = (SQLSelectStatement)SQLUtils.parseStatements(subQuery, (String)this.params.get("dbType")).get(0);
            SQLSelect select = stmt.getSelect();
            SQLQueryExpr sqlQueryExpr = new SQLQueryExpr(select);
            field.setExpr(sqlQueryExpr);
         } else {
            List<Column> sqlExprList = new ArrayList();
            this.parseSQLExpr(fileExpr, fileExpr, sqlExprList, false);

            for(Column col_obj : sqlExprList) {
               SQLExpr sqlExpr = col_obj.getExpr();
               String columnName = sqlExpr.toString();
               Column column = new Column(columnName, field.getAlias(), isSub, alias);
               column.setMethod(col_obj.isMethod());
               column.setFullName(col_obj.getFullName());
               String[] columns = column.getColumnName().split("\\.");
               String c1 = columns[0];
               String c2 = columns.length > 1 ? columns[1] : columns[0];
               List<Column> list = (List)this.tableList.get(c1);
               if (list != null) {
                  for(Column col : list) {
                     String s = col.getColumnName();
                     if ((JdbcUtil.sqlRegex(s, c2) || JdbcUtil.sqlRegex(col.getColumnAlisa(), c2)) && !column.getFullName().trim().startsWith("count")) {
                        String colName = column.getColumnName();
                        String realName = "";
                        String own = "";
                        String now = "";
                        if (s.split("\\.").length > 1) {
                           realName = s;
                           own = s.split("\\.")[0];
                           now = s.split("\\.")[0] + "." + colName.split("\\.")[1];
                        } else {
                           realName = s;
                           own = colName.split("\\.")[0];
                           now = colName;
                        }

                        column.setColumnName(realName);
                        column.setNowName(now);
                        column.setSubQuery(true);
                        column.setSubAlisa(c1);
                        String fieldClassName = sqlExpr.getClass().getName();
                        if (fieldClassName.contains("SQLPropertyExpr")) {
                           SQLPropertyExpr expr = (SQLPropertyExpr)sqlExpr;
                           SQLIdentifierExpr owner = (SQLIdentifierExpr)expr.getOwner();
                           owner.setName(own);
                        }
                        break;
                     }
                  }
               }

               if (columns.length > 1) {
                  String url = (String)this.params.get("url");
                  String schema = (String)this.params.get("schema");
                  String schema_temp = (String)this.schemaMap.get(c1);
                  String schema_real = schema_temp == null ? schema : schema_temp;
                  String ip_port = url.split("/")[2];
                  String columnDataType = JdbcUtil.getColumnDataType(ip_port, schema_real, (String)this.tableMap.get(c1), c1, c2);
                  column.setDataType(columnDataType);
               }

               if (!isSub && !column.getFullName().trim().startsWith("count")) {
                  this.columnList.add(column);
               }

               temp.add(column);
            }
         }
      }

      if (isSub) {
         this.tableList.put(alias, temp);
      }

   }

   public void parseSQLExpr(SQLExpr oldFileExpr, SQLExpr fileExpr, List<Column> sqlExprList, boolean isMethod) {
      String className = fileExpr.getClass().toString();
      if (!JdbcUtil.sqlRegex(className, "SQLPropertyExpr|SQLIdentifierExpr")) {
         if (className.contains("SQLAggregateExpr")) {
            SQLAggregateExpr expr = (SQLAggregateExpr)fileExpr;

            for(SQLExpr sqlExpr : expr.getArguments()) {
               String name = sqlExpr.getClass().getName();
               if (!name.contains("SQLCharExpr")) {
                  this.parseSQLExpr(oldFileExpr, sqlExpr, sqlExprList, true);
               }
            }
         } else if (className.contains("SQLMethodInvokeExpr")) {
            SQLMethodInvokeExpr expr = (SQLMethodInvokeExpr)fileExpr;

            for(SQLExpr sqlExpr : expr.getArguments()) {
               String name = sqlExpr.getClass().getName();
               if (!name.contains("SQLCharExpr")) {
                  this.parseSQLExpr(oldFileExpr, sqlExpr, sqlExprList, true);
               }
            }
         } else if (className.contains("SQLBinaryOpExpr")) {
            SQLBinaryOpExpr expr = (SQLBinaryOpExpr)fileExpr;
            SQLExpr left = expr.getLeft();
            SQLExpr right = expr.getRight();
            this.parseSQLExpr(oldFileExpr, left, sqlExprList, true);
            this.parseSQLExpr(oldFileExpr, right, sqlExprList, true);
         } else if (className.contains("SQLCaseExpr")) {
            SQLCaseExpr expr = (SQLCaseExpr)fileExpr;

            for(SQLCaseExpr.Item item : expr.getItems()) {
               this.parseSQLExpr(oldFileExpr, item.getConditionExpr(), sqlExprList, true);
            }
         } else if (className.contains("SQLBetweenExpr")) {
            SQLBetweenExpr expr = (SQLBetweenExpr)fileExpr;
            this.parseSQLExpr(oldFileExpr, expr.getTestExpr(), sqlExprList, true);
         }
      } else {
         Column column = new Column(isMethod, fileExpr);
         column.setFullName(oldFileExpr.toString());
         sqlExprList.add(column);
      }

   }

   public SQLInsertStatement parseInsert(String dbType, String sql) throws SQLException {
      SQLInsertStatement insert = this.aliParseInsert(dbType, sql);
      this.parseInsertColumns(insert);
      return insert;
   }

   public SQLInsertStatement aliParseInsert(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLInsertStatement stmt = (SQLInsertStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseInsertColumns(SQLInsertStatement insert) throws SQLException {
      String tableName = insert.getTableName().getSimpleName();
      String url = (String)this.params.get("url");
      String schema = (String)this.params.get("schema");
      List<SQLExpr> columns = insert.getColumns();
      if (columns.size() == 0) {
         String ip_port = url.split("/")[2];
         String schema_temp = (String)this.schemaMap.get(tableName);
         String schema_real = schema_temp == null ? schema : schema_temp;

         for(Column tableColumn : JdbcUtil.getTableColumnsNormal(ip_port, schema_real, tableName.toLowerCase(), "", this.tableList)) {
            SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();
            sqlIdentifierExpr.setName(tableColumn.getColumnName());
            columns.add(sqlIdentifierExpr);
            Column column = new Column();
            column.setColumnName(tableName + "." + tableColumn.getColumnName());
            this.columnList.add(column);
         }

         this.tableMap.put(tableName, tableName);
      }

      List<SQLInsertStatement.ValuesClause> valuesList = insert.getValuesList();
      if (valuesList.size() == 0) {
         String dbType = (String)this.params.get("dbType");
         SQLSelectQueryBlock query = (SQLSelectQueryBlock)insert.getQuery().getQuery();
         String parseSql = this.parseSelect(dbType, query.toString());
         SQLSelectQueryBlock sqlSelectQueryBlock = this.aliParseSelect(dbType, parseSql, true);
         insert.getQuery().setQuery(sqlSelectQueryBlock);
      }

      this.schemaMap.put(tableName, schema);
   }

   public SQLUpdateStatement parseUpdate(String dbType, String sql) throws SQLException {
      SQLUpdateStatement update = this.aliParseUpdate(dbType, sql);
      this.parseUpdateColumns(update);
      return update;
   }

   public SQLUpdateStatement aliParseUpdate(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLUpdateStatement stmt = (SQLUpdateStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseUpdateColumns(SQLUpdateStatement update) {
      String tableName = update.getTableName().getSimpleName();
      SQLExprTableSource tableExpr = (SQLExprTableSource)update.getTableSource();
      SQLExpr expr1 = tableExpr.getExpr();
      String schema = (String)this.params.get("schema");
      if (expr1 instanceof SQLIdentifierExpr) {
         SQLPropertyExpr expr = new SQLPropertyExpr();
         expr.setName(tableName);
         expr.setOwner(schema);
         tableExpr.setExpr((SQLExpr)expr);
      } else if (expr1 instanceof SQLPropertyExpr) {
         schema = ((SQLPropertyExpr)expr1).getOwnerName();
      }

      this.schemaMap.put(tableName, schema);
      String alias = update.getTableSource().getAlias();
      String name = alias == null ? tableName : alias;

      for(SQLUpdateSetItem item : update.getItems()) {
         String className = item.getColumn().getClass().getName();
         String colName = "";
         String colValue = "";
         if (className.contains("SQLPropertyExpr")) {
            SQLPropertyExpr column = (SQLPropertyExpr)item.getColumn();
            colName = column.getName();
            colValue = ((SQLUpdateSetItem)column.getParent()).getValue().toString();
         } else if (className.contains("SQLIdentifierExpr")) {
            SQLIdentifierExpr column = (SQLIdentifierExpr)item.getColumn();
            colName = column.getName();
            colValue = ((SQLUpdateSetItem)column.getParent()).getValue().toString();
         }

         Column col = new Column();
         col.setColumnName(name + "." + colName);
         col.setValue(colValue);
         this.columnList.add(col);
      }

      this.tableMap.put(name, tableName);
   }

   public SQLDeleteStatement parseDelete(String dbType, String sql) throws SQLException {
      SQLDeleteStatement delete = this.aliParseDelete(dbType, sql);
      this.parseDeleteColumns(delete);
      return delete;
   }

   public SQLDeleteStatement aliParseDelete(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLDeleteStatement stmt = (SQLDeleteStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseDeleteColumns(SQLDeleteStatement delete) throws SQLException {
      String schema = (String)this.params.get("schema");
      String url = (String)this.params.get("url");
      String ip_port = url.split("/")[2];
      String tableName = delete.getTableName().getSimpleName();
      SQLExprTableSource tableExpr = (SQLExprTableSource)delete.getTableSource();
      SQLExpr expr1 = tableExpr.getExpr();
      if (expr1 instanceof SQLIdentifierExpr) {
         SQLPropertyExpr expr = new SQLPropertyExpr();
         expr.setName(tableName);
         expr.setOwner(schema);
         tableExpr.setExpr((SQLExpr)expr);
      } else if (expr1 instanceof SQLPropertyExpr) {
         schema = ((SQLPropertyExpr)expr1).getOwnerName();
      }

      this.tableMap.put(tableName, tableName);
      this.schemaMap.put(tableName, schema);

      for(Column tableColumn : JdbcUtil.getTableColumns(ip_port, schema, tableName, tableName, this.tableList)) {
         Column column = new Column();
         column.setColumnName(tableColumn.getColumnName());
         this.columnList.add(column);
      }

   }

   public SQLTruncateStatement parseTruncate(String dbType, String sql) throws SQLException {
      SQLTruncateStatement truncate = this.aliParseTruncate(dbType, sql);
      this.parseTruncateColumns(truncate);
      return truncate;
   }

   public SQLTruncateStatement aliParseTruncate(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLTruncateStatement stmt = (SQLTruncateStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseTruncateColumns(SQLTruncateStatement truncate) {
      for(SQLExprTableSource sqlExprTableSource : truncate.getTableSources()) {
         String tableName = sqlExprTableSource.getName().getSimpleName();
         this.tableMap.put(tableName, tableName);
      }

   }

   public SQLCreateStatement parseCreate(String dbType, String sql) throws SQLException {
      SQLCreateStatement create = this.aliParseCreate(dbType, sql);
      this.parseCreateColumns(create);
      return create;
   }

   public SQLCreateStatement aliParseCreate(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLCreateStatement stmt = (SQLCreateStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseCreateColumns(SQLCreateStatement create) throws SQLException {
      String schema = (String)this.params.get("schema");
      String name = create.getClass().getName();
      String tableName = "";
      if (name.contains("HiveCreateTableStatement")) {
         HiveCreateTableStatement createTable = (HiveCreateTableStatement)create;
         tableName = createTable.getTableName();
         List<SQLTableElement> tableElementList = createTable.getTableElementList();
         if (tableElementList.size() == 0) {
            SQLSelect subQuery = createTable.getSelect();
            if (subQuery != null) {
               this.params.put("sql", subQuery.toString());
               this.parseSqlStructure(new HashMap());
            }
         } else {
            for(SQLTableElement sqlTableElement : tableElementList) {
               String className = sqlTableElement.getClass().getName();
               if (className.contains("SQLColumnDefinition")) {
                  SQLColumnDefinition col = (SQLColumnDefinition)sqlTableElement;
                  String columnName = col.getColumnName();
                  Column column = new Column();
                  column.setColumnName(tableName + "." + columnName);
                  this.columnList.add(column);
               }
            }
         }

         this.tableMap.put(tableName, tableName);
      } else if (name.contains("SQLCreateViewStatement")) {
         SQLCreateViewStatement createView = (SQLCreateViewStatement)create;
         String simpleName = createView.getName().getSimpleName();
         SQLSelect subQuery = createView.getSubQuery();
         this.params.put("sql", subQuery.toString());
         this.parseSqlStructure(new HashMap());
         this.tableMap.put(simpleName, simpleName);
      }

      this.schemaMap.put(tableName, schema);
   }

   public HiveAlterDatabaseStatement parseAlter(String dbType, String sql) throws SQLException {
      HiveAlterDatabaseStatement alter = this.aliParseAlter(dbType, sql);
      return alter;
   }

   public HiveAlterDatabaseStatement aliParseAlter(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         HiveAlterDatabaseStatement stmt = (HiveAlterDatabaseStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseAlterColumns(SQLAlterTableStatement alter) {
      String tableName = alter.getTableName();

      for(SQLAlterTableItem item : alter.getItems()) {
         String className = item.getClass().getName();
         if (className.contains("SQLAlterTableAddColumn")) {
            SQLAlterTableAddColumn alterColumn = (SQLAlterTableAddColumn)item;

            for(SQLColumnDefinition column : alterColumn.getColumns()) {
               String columnName = column.getColumnName();
               Column col = new Column();
               col.setColumnName(tableName + "." + columnName);
               this.columnList.add(col);
            }
         } else if (className.contains("SQLAlterTableDropColumnItem")) {
            SQLAlterTableDropColumnItem alterColumn = (SQLAlterTableDropColumnItem)item;

            for(SQLName column : alterColumn.getColumns()) {
               String columnName = column.getSimpleName();
               Column col = new Column();
               col.setColumnName(tableName + "." + columnName);
               this.columnList.add(col);
            }
         }
      }

      this.tableMap.put(tableName, tableName);
   }

   public String parseUnion(String dbType, String sql) throws SQLException {
      StringBuilder result = new StringBuilder();
      SQLUnionQuery query = this.aliParseUnion(dbType, sql);
      this.dealUnion(query, result);
      return result.toString();
   }

   public SQLUnionQuery aliParseUnion(String dbType, String sql) throws SQLException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLSelectStatement stmt = (SQLSelectStatement)stmtList.get(0);
         SQLSelect select = stmt.getSelect();
         SQLUnionQuery query = (SQLUnionQuery)select.getQuery();
         return query;
      }
   }

   public void dealUnion(SQLUnionQuery unionQuery, StringBuilder result) throws SQLException {
      List<SQLSelectQuery> queryList = unionQuery.getRelations();

      for(int i = 0; i < queryList.size(); ++i) {
         SQLSelectQuery selectQuery = (SQLSelectQuery)queryList.get(i);
         String sql = "";
         String className = selectQuery.getClass().getName();
         if (className.contains("SQLUnionQuery")) {
            SQLUnionQuery unQuery = (SQLUnionQuery)selectQuery;
            this.dealUnion(unQuery, result);
         } else {
            SQLSelectQueryBlock query = (SQLSelectQueryBlock)selectQuery;
            this.parseFrom(query.getFrom(), (Map)null);
            sql = this.parseSelectStar(query);
            query = this.aliParseSelect(this.dbType, sql, true);
            this.parseQueryColumns(query, "");
            sql = this.parseSelectStar(query);
            result.append(sql);
         }

         if (i == 0) {
            result.append("\n" + unionQuery.getOperator().toString() + "\n");
         }
      }

   }

   public SQLDropTableStatement parseDrop(String dbType, String sql) throws SQLException {
      SQLDropTableStatement drop = this.aliParseDrop(dbType, sql);
      this.parseDropColumns(drop);
      return drop;
   }

   public SQLDropTableStatement aliParseDrop(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLDropTableStatement stmt = (SQLDropTableStatement)stmtList.get(0);
         return stmt;
      }
   }

   public SQLDDLStatement parseDropOther(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLDDLStatement stmt = (SQLDDLStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseDropColumns(SQLDropTableStatement drop) {
      for(SQLExprTableSource tableSource : drop.getTableSources()) {
         String tableName = tableSource.getName().getSimpleName();
         this.tableMap.put(tableName, tableName);
      }

   }

   public SQLGrantStatement parseGrant(String dbType, String sql) throws SQLException {
      SQLGrantStatement grant = this.aliParseGrant(dbType, sql);
      return grant;
   }

   public SQLGrantStatement aliParseGrant(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLGrantStatement stmt = (SQLGrantStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseGrantColumns(SQLGrantStatement grant) {
      String url = (String)this.params.get("url");
      String ip_port = url.split("/")[2];
      SQLExprTableSource resource = (SQLExprTableSource)grant.getResource();
      SQLPropertyExpr expr = (SQLPropertyExpr)resource.getExpr();
      String name = expr.getName();
      String owner = expr.getOwner().toString();
      if ("*".equalsIgnoreCase(owner)) {
         for(DataBase dataBase : JdbcUtil.dataBaseList) {
            if (dataBase.getIp_port().equalsIgnoreCase(ip_port)) {
               for(Schema schema : dataBase.getSchemaList()) {
                  for(Table table : schema.getTableList()) {
                     this.schemaMap.put(table.getName(), schema.getName());
                     this.tableMap.put(table.getName(), table.getName());
                  }
               }
            }
         }
      } else if ("*".equalsIgnoreCase(name)) {
         for(DataBase dataBase : JdbcUtil.dataBaseList) {
            if (dataBase.getIp_port().equalsIgnoreCase(ip_port)) {
               for(Schema schema : dataBase.getSchemaList()) {
                  if (schema.getName().equalsIgnoreCase(owner)) {
                     for(Table table : schema.getTableList()) {
                        this.schemaMap.put(table.getName(), owner);
                        this.tableMap.put(table.getName(), table.getName());
                     }
                  }
               }
            }
         }
      } else {
         this.schemaMap.put(name, owner);
         this.tableMap.put(name, name);
      }

   }

   public SQLRevokeStatement parseRevoke(String dbType, String sql) throws SQLException {
      SQLRevokeStatement revoke = this.aliParseRevoke(dbType, sql);
      return revoke;
   }

   public SQLRevokeStatement aliParseRevoke(String dbType, String sql) throws SQLSyntaxErrorException {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() > 1) {
         throw new SQLSyntaxErrorException("Multi is not supported,use single instead");
      } else {
         SQLRevokeStatement stmt = (SQLRevokeStatement)stmtList.get(0);
         return stmt;
      }
   }

   public void parseRevokeColumns(SQLRevokeStatement revoke) {
      String url = (String)this.params.get("url");
      String ip_port = url.split("/")[2];
      SQLExprTableSource resource = (SQLExprTableSource)revoke.getResource();
      SQLPropertyExpr expr = (SQLPropertyExpr)resource.getExpr();
      String name = expr.getName();
      String owner = expr.getOwner().toString();
      if ("*".equalsIgnoreCase(owner)) {
         for(DataBase dataBase : JdbcUtil.dataBaseList) {
            if (dataBase.getIp_port().equalsIgnoreCase(ip_port)) {
               for(Schema schema : dataBase.getSchemaList()) {
                  for(Table table : schema.getTableList()) {
                     this.schemaMap.put(table.getName(), schema.getName());
                     this.tableMap.put(table.getName(), table.getName());
                  }
               }
            }
         }
      } else if ("*".equalsIgnoreCase(name)) {
         for(DataBase dataBase : JdbcUtil.dataBaseList) {
            if (dataBase.getIp_port().equalsIgnoreCase(ip_port)) {
               for(Schema schema : dataBase.getSchemaList()) {
                  if (schema.getName().equalsIgnoreCase(owner)) {
                     for(Table table : schema.getTableList()) {
                        this.schemaMap.put(table.getName(), owner);
                        this.tableMap.put(table.getName(), table.getName());
                     }
                  }
               }
            }
         }
      } else {
         this.schemaMap.put(name, owner);
         this.tableMap.put(name, name);
      }

   }

   public List<String> parseWheres(String sql) {
      SQLParser parser = new SQLParser();
      return parser.where(sql);
   }

   public String formatSQL(String sql) {
      SQLParser parser = new SQLParser();
      return parser.format(sql);
   }

   public static String getSqlType(String sql, String dbType) {
      SQLLogger.log(sql);
      SQLStatement statement = null;
      sql = sql.trim();
      String sqlType = "";
      if (JdbcUtil.followRules_like(sql, "(?i)^(create)(\\s+?)(TEMPORARY)(\\s+?)(macro)(.*)")) {
         return "macro";
      } else if (JdbcUtil.followRules_like(sql, "(?i)^(create)(\\s+?)((TEMPORARY)(\\s+?))?(function)(.*)")) {
         return "function";
      } else if (JdbcUtil.followRules_like(sql, "(?i)^(drop)(\\s+?)(table)")) {
         return "drop";
      } else if (JdbcUtil.followRules_like(sql, "(?i)^(drop)(\\s+?)(.*)")) {
         return "drop_other";
      } else {
         if (JdbcUtil.followRules_like(sql, "(?i)^(insert)(\\s+?)(into)(\\s+?)(select)(.*)(from)")) {
            sqlType = "insert_select";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(insert)(\\s+?)(into)(.*)")) {
            sqlType = "insert";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(update)(.*)(set)")) {
            sqlType = "update";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(replace)(.*)(into)")) {
            sqlType = "replace";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(delete)(\\s+?)(from)(.*)")) {
            sqlType = "delete";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(truncate)(\\s+?)(table)?")) {
            sqlType = "truncate";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(create)(\\s+?)(table|view|EXTERNAL|TEMPORARY|Materialized)")) {
            sqlType = "create";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(alter)(\\s+?)(table)")) {
            sqlType = "alter";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(grant)(.*)(to)")) {
            sqlType = "grant";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(revoke)(.*)(on)(.*)(from)")) {
            sqlType = "revoke";
         } else if (JdbcUtil.followRules_like(sql, "(?i)(\\s+)(union|union all|INTERSECT|MINUS)(\\s+)")) {
            sqlType = "union";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(with)(.*?)(as)(.*)(select)")) {
            sqlType = "with_select";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(select)(.*?)(from)")) {
            sqlType = "select";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(merge)(\\s+?)(into)(.*)")) {
            sqlType = "merge";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(alter)(\\s+?)(schema|database)(.*)")) {
            sqlType = "alter";
         } else if (JdbcUtil.followRules_like(sql, "(?i)^(show)(\\s+?)(.*)")) {
            sqlType = "show";
         } else {
            sqlType = "general";
         }

         return sqlType;
      }
   }

   public void parseSqlStructureAndReplace2(Map<String, Object> paramMap, List<Map> repalceMap) throws SQLException {
      String sql = (String)this.params.get("sql");
      String sqlType = JdbcUtil.getSqlType(sql, this.dbType);
      if ("select".equals(sqlType) || "union".equals(sqlType) || "update".equals(sqlType) || "insert".equals(sqlType) || "delete".equals(sqlType)) {
         String permission = (String)this.params.get("permission");
         String url = (String)this.params.get("url");
         String schema = (String)this.params.get("schema");
         String ip_port = url.split("/")[2];
         SQLParser sqlParser = new SQLParser();
         DataBase dataBase = new DataBase();

         for(DataBase db : JdbcUtil.dataBaseList) {
            if (ip_port.equalsIgnoreCase(db.getIp_port())) {
               dataBase = db;
               break;
            }
         }

         SQLResult parse = sqlParser.parse(sql, dataBase, schema);
         List<FieldInfo> columnlist = new ArrayList();
         if ("masking".equalsIgnoreCase(permission)) {
            columnlist = parse.outputs;
         } else if ("full".equalsIgnoreCase(permission)) {
            columnlist = parse.fields;
         } else if (!"insert".equalsIgnoreCase(sqlType) && !"update".equalsIgnoreCase(sqlType)) {
            columnlist = parse.outputs;
         }

         for(FieldInfo fieldInfo : columnlist) {
            List<FieldInfo> fieldInfoList = new ArrayList();
            if (fieldInfo.getRelations().size() > 1) {
               fieldInfoList = fieldInfo.getRelations();
            } else {
               fieldInfoList.add(fieldInfo);
            }

            for(FieldInfo info : fieldInfoList) {
               String name = info.getName();
               Map<String, TableInfo> tables = info.getTables();
               String tableName = "";

               for(String s : tables.keySet()) {
                  tableName = s.split("\\.")[1];
               }

               Column column = new Column();
               column.setColumnName(tableName + "." + name);
               column.setColumnAlisa(info.getAlias());
               column.setSubQuery(false);
               this.columnList.add(column);
            }
         }

         for(TableInfo table : parse.tables) {
            TableMemo tb = table.getTable();
            this.tableMap.put(tb.name, tb.name);
            this.schemaMap.put(tb.name, tb.schema);
         }

         for(ValueInfo value : parse.values) {
            FieldMemo field = value.field;
            this.whereMap.put(field.name, value.value.toString());
            Map<String, Object> map = new HashMap();
            map.put("column", field.getAtomName());
            map.put("columnValue", value.value.toString());
            map.put("type", value.operator);
            this.whereList.add(map);
         }

         sqlParser.replace(parse.values, repalceMap);
         paramMap.put("whereList", this.whereList);
         paramMap.put("statement", parse.statement);
         paramMap.put("type", parse.sqlType.name().toLowerCase());
         paramMap.put("sql", parse.statement.toString());
      }

      paramMap.put("tableMap", this.tableMap);
      paramMap.put("columnList", this.columnList);
      paramMap.put("schemaMap", this.schemaMap);
      paramMap.put("whereMap", this.whereMap);
      paramMap.put("whereList", this.flatWhereListValues());
   }

   private List<Map<String, Object>> flatWhereListValues() {
      if (this.whereValueList != null && this.whereValueList.size() != 0) {
         List<Map<String, Object>> flats = new ArrayList();

         for(Map<String, Object> column : this.whereValueList) {
            Object columnValue = column.get("columnValue");
            if (columnValue instanceof List) {
               List values = (List)columnValue;
               if (values != null && values.size() != 0) {
                  for(Object value : values) {
                     Map<String, Object> columnMap = new HashMap(column);
                     columnMap.put("columnValue", value);
                     flats.add(columnMap);
                  }
               } else {
                  column.put("columnValue", "");
                  flats.add(column);
               }
            } else {
               flats.add(column);
            }
         }

         return flats;
      } else {
         return this.whereValueList;
      }
   }

   public Scope getScope(String ip_port, String schema, List<SQLStatement> statements) {
      Context context = new Context();
      DataBase dataBase = new DataBase();

      for(DataBase db : JdbcUtil.dataBaseList) {
         if (ip_port.equalsIgnoreCase(db.getIp_port())) {
            dataBase = db;
            break;
         }
      }

      context.initialize(dataBase, schema, this.dbType);
      Scope scope = new Scope();
      scope.dialector = new HiveScopeDialector(scope);
      scope.context = context;
      scope.isCheckIsolated = true;
      ScopeVisitor scopeVisitor = new ScopeVisitor(scope);
      scopeVisitor.perform(statements);
      FieldVisitor fieldVisitor = new FieldVisitor(scope);
      fieldVisitor.perform(statements);
      return scope;
   }
}
