package com.chenyang.druid.sql.ast.statement;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.ClusteringType;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLPartitionBy;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.SQLStatementImpl;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLValuableExpr;
import com.chenyang.druid.sql.dialect.mysql.ast.MySqlKey;
import com.chenyang.druid.sql.dialect.mysql.ast.MySqlUnique;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleCreateSynonymStatement;
import com.chenyang.druid.sql.parser.SQLParserUtils;
import com.chenyang.druid.sql.semantic.SemanticException;
import com.chenyang.druid.sql.visitor.SQLASTVisitor;
import com.chenyang.druid.util.FnvHash;
import com.chenyang.druid.util.ListDG;
import com.chenyang.druid.util.lang.Consumer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLStatement, SQLCreateStatement {
   protected boolean ifNotExists = false;
   protected Type type;
   protected SQLExprTableSource tableSource;
   protected List<SQLTableElement> tableElementList = new ArrayList();
   protected SQLExprTableSource inherits;
   protected SQLSelect select;
   protected SQLExpr comment;
   protected SQLExprTableSource like;
   protected Boolean compress;
   protected Boolean logging;
   protected SQLName tablespace;
   protected SQLPartitionBy partitioning;
   protected SQLExpr storedAs;
   protected SQLExpr location;
   protected boolean onCommitPreserveRows;
   protected boolean onCommitDeleteRows;
   protected boolean external;
   protected SQLExternalRecordFormat rowFormat;
   protected final List<SQLColumnDefinition> partitionColumns = new ArrayList(2);
   protected ClusteringType clusteringType;
   protected final List<SQLSelectOrderByItem> clusteredBy = new ArrayList();
   protected final List<SQLSelectOrderByItem> sortedBy = new ArrayList();
   protected int buckets;
   protected int shards;
   protected final List<SQLAssignItem> tableOptions = new ArrayList();
   protected final List<SQLAssignItem> tblProperties = new ArrayList();
   protected boolean replace = false;
   protected boolean ignore = false;
   protected boolean dimension;
   protected SQLExpr engine;

   public SQLCreateTableStatement() {
   }

   public SQLCreateTableStatement(DbType dbType) {
      super(dbType);
   }

   protected void accept0(SQLASTVisitor v) {
      if (v.visit(this)) {
         this.acceptChild(v);
      }

      v.endVisit(this);
   }

   protected void acceptChild(SQLASTVisitor v) {
      this.acceptChild(v, this.tableSource);
      this.acceptChild(v, this.tableElementList);
      this.acceptChild(v, this.inherits);
      this.acceptChild(v, this.select);
      this.acceptChild(v, this.comment);
      this.acceptChild(v, this.like);
      this.acceptChild(v, this.tablespace);
      this.acceptChild(v, this.partitioning);
      this.acceptChild(v, this.storedAs);
      this.acceptChild(v, this.location);
      this.acceptChild(v, this.partitionColumns);
      this.acceptChild(v, this.clusteredBy);
      this.acceptChild(v, this.sortedBy);
      this.acceptChild(v, this.tableOptions);
      this.acceptChild(v, this.tblProperties);
   }

   public SQLExpr getComment() {
      return this.comment;
   }

   public void setComment(SQLExpr comment) {
      if (comment != null) {
         comment.setParent(this);
      }

      this.comment = comment;
   }

   public SQLName getName() {
      return this.tableSource == null ? null : (SQLName)this.tableSource.getExpr();
   }

   public String getTableName() {
      SQLName name = this.getName();
      return name == null ? null : name.getSimpleName();
   }

   public String getSchema() {
      SQLName name = this.getName();
      if (name == null) {
         return null;
      } else {
         return name instanceof SQLPropertyExpr ? ((SQLPropertyExpr)name).getOwnernName() : null;
      }
   }

   public void setSchema(String name) {
      if (this.tableSource != null) {
         this.tableSource.setSchema(name);
      }
   }

   public void setName(SQLName name) {
      this.setTableSource(new SQLExprTableSource(name));
   }

   public void setName(String name) {
      this.setName((SQLName)(new SQLIdentifierExpr(name)));
   }

   public SQLExprTableSource getTableSource() {
      return this.tableSource;
   }

   public void setTableSource(SQLExprTableSource tableSource) {
      if (tableSource != null) {
         tableSource.setParent(this);
      }

      this.tableSource = tableSource;
   }

   public void setTableName(String tableName) {
      SQLExpr name = SQLUtils.toSQLExpr(tableName, this.dbType);
      this.setTableSource(new SQLExprTableSource(name));
   }

   public Type getType() {
      return this.type;
   }

   public void setType(Type type) {
      this.type = type;
   }

   public List<SQLTableElement> getTableElementList() {
      return this.tableElementList;
   }

   public SQLColumnDefinition getColumn(String columnName) {
      long hashCode64 = FnvHash.hashCode64(columnName);

      for(SQLTableElement e : this.tableElementList) {
         if (e instanceof SQLColumnDefinition) {
            SQLColumnDefinition column = (SQLColumnDefinition)e;
            if (column.nameHashCode64() == hashCode64) {
               return column;
            }
         }
      }

      return null;
   }

   public List<SQLColumnDefinition> getColumnDefinitions() {
      ArrayList<SQLColumnDefinition> column = new ArrayList();

      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLColumnDefinition) {
            column.add((SQLColumnDefinition)element);
         }
      }

      return column;
   }

   public List<String> getColumnNames(boolean normalized) {
      List<String> columnNames = new ArrayList();

      for(SQLColumnDefinition definition : this.getColumnDefinitions()) {
         String columnName = definition.getColumnName();
         if (normalized) {
            columnName = SQLUtils.normalize(columnName);
         }

         columnNames.add(columnName);
      }

      return columnNames;
   }

   public List<String> getColumnComments() {
      List<String> comments = new ArrayList();

      for(SQLColumnDefinition definition : this.getColumnDefinitions()) {
         comments.add(((SQLCharExpr)definition.getComment()).getText());
      }

      return comments;
   }

   public void addColumn(String columnName, String dataType) {
      SQLColumnDefinition column = new SQLColumnDefinition();
      column.setName(columnName);
      column.setDataType(SQLParserUtils.createExprParser(dataType, this.dbType).parseDataType());
      this.addColumn(column);
   }

   public void addColumn(SQLColumnDefinition column) {
      if (column == null) {
         throw new IllegalArgumentException();
      } else {
         column.setParent(this);
         this.tableElementList.add(column);
      }
   }

   public boolean isIfNotExists() {
      return this.ifNotExists;
   }

   public void setIfNotExiists(boolean ifNotExists) {
      this.ifNotExists = ifNotExists;
   }

   public SQLExprTableSource getInherits() {
      return this.inherits;
   }

   public void setInherits(SQLExprTableSource inherits) {
      if (inherits != null) {
         inherits.setParent(this);
      }

      this.inherits = inherits;
   }

   public SQLSelect getSelect() {
      return this.select;
   }

   public void setSelect(SQLSelect select) {
      if (select != null) {
         select.setParent(this);
      }

      this.select = select;
   }

   public SQLExprTableSource getLike() {
      return this.like;
   }

   public void setLike(SQLName like) {
      this.setLike(new SQLExprTableSource(like));
   }

   public void setLike(SQLExprTableSource like) {
      if (like != null) {
         like.setParent(this);
      }

      this.like = like;
   }

   public Boolean getCompress() {
      return this.compress;
   }

   public void setCompress(Boolean compress) {
      this.compress = compress;
   }

   public Boolean getLogging() {
      return this.logging;
   }

   public void setLogging(Boolean logging) {
      this.logging = logging;
   }

   public SQLName getTablespace() {
      return this.tablespace;
   }

   public void setTablespace(SQLName x) {
      if (x != null) {
         x.setParent(this);
      }

      this.tablespace = x;
   }

   public SQLPartitionBy getPartitioning() {
      return this.partitioning;
   }

   public void setPartitioning(SQLPartitionBy partitioning) {
      if (partitioning != null) {
         partitioning.setParent(this);
      }

      this.partitioning = partitioning;
   }

   public List<SQLObject> getChildren() {
      List<SQLObject> children = new ArrayList();
      children.add(this.tableSource);
      children.addAll(this.tableElementList);
      if (this.inherits != null) {
         children.add(this.inherits);
      }

      if (this.select != null) {
         children.add(this.select);
      }

      return children;
   }

   public void addBodyBeforeComment(List<String> comments) {
      if (this.attributes == null) {
         this.attributes = new HashMap(1);
      }

      List<String> attrComments = (List)this.attributes.get("rowFormat.body_before_comment");
      if (attrComments == null) {
         this.attributes.put("rowFormat.body_before_comment", comments);
      } else {
         attrComments.addAll(comments);
      }

   }

   public List<String> getBodyBeforeCommentsDirect() {
      return this.attributes == null ? null : (List)this.attributes.get("rowFormat.body_before_comment");
   }

   public boolean hasBodyBeforeComment() {
      List<String> comments = this.getBodyBeforeCommentsDirect();
      if (comments == null) {
         return false;
      } else {
         return !comments.isEmpty();
      }
   }

   public String computeName() {
      if (this.tableSource == null) {
         return null;
      } else {
         SQLExpr expr = this.tableSource.getExpr();
         if (expr instanceof SQLName) {
            String name = ((SQLName)expr).getSimpleName();
            return SQLUtils.normalize(name);
         } else {
            return null;
         }
      }
   }

   public SQLColumnDefinition findColumn(String columName) {
      if (columName == null) {
         return null;
      } else {
         long hash = FnvHash.hashCode64(columName);
         return this.findColumn(hash);
      }
   }

   public SQLColumnDefinition findColumn(long columName_hash) {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLColumnDefinition) {
            SQLColumnDefinition column = (SQLColumnDefinition)element;
            if (column.nameHashCode64() == columName_hash) {
               return column;
            }
         }
      }

      for(SQLColumnDefinition column : this.partitionColumns) {
         if (column.nameHashCode64() == columName_hash) {
            return column;
         }
      }

      return null;
   }

   public boolean isPrimaryColumn(String columnName) {
      SQLPrimaryKey pk = this.findPrimaryKey();
      if (pk != null && pk.containsColumn(columnName)) {
         return true;
      } else {
         for(SQLColumnDefinition element : this.getColumnDefinitions()) {
            for(SQLColumnConstraint constraint : element.constraints) {
               if (constraint instanceof SQLColumnPrimaryKey && SQLUtils.normalize(element.getColumnName()).equalsIgnoreCase(SQLUtils.normalize(columnName))) {
                  return true;
               }
            }
         }

         return false;
      }
   }

   public boolean isPrimaryColumn(long columnNameHash) {
      SQLPrimaryKey pk = this.findPrimaryKey();
      return pk == null ? false : pk.containsColumn(columnNameHash);
   }

   public boolean isOnlyPrimaryKey(long columnNameHash) {
      SQLPrimaryKey pk = this.findPrimaryKey();
      if (pk == null) {
         return false;
      } else {
         return pk.containsColumn(columnNameHash) && pk.getColumns().size() == 1;
      }
   }

   public boolean isMUL(String columnName) {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof MySqlUnique) {
            MySqlUnique unique = (MySqlUnique)element;
            SQLExpr column = ((SQLSelectOrderByItem)unique.getColumns().get(0)).getExpr();
            if (column instanceof SQLIdentifierExpr && SQLUtils.nameEquals(columnName, ((SQLIdentifierExpr)column).getName())) {
               return unique.getColumns().size() > 1;
            }

            if (column instanceof SQLMethodInvokeExpr && SQLUtils.nameEquals(((SQLMethodInvokeExpr)column).getMethodName(), columnName)) {
               return true;
            }
         } else if (element instanceof MySqlKey) {
            MySqlKey unique = (MySqlKey)element;
            SQLExpr column = ((SQLSelectOrderByItem)unique.getColumns().get(0)).getExpr();
            if (column instanceof SQLIdentifierExpr && SQLUtils.nameEquals(columnName, ((SQLIdentifierExpr)column).getName())) {
               return true;
            }

            if (column instanceof SQLMethodInvokeExpr && SQLUtils.nameEquals(((SQLMethodInvokeExpr)column).getMethodName(), columnName)) {
               return true;
            }
         }
      }

      return false;
   }

   public boolean isUNI(String columnName) {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof MySqlUnique) {
            MySqlUnique unique = (MySqlUnique)element;
            if (unique.getColumns().size() != 0) {
               SQLExpr column = ((SQLSelectOrderByItem)unique.getColumns().get(0)).getExpr();
               if (column instanceof SQLIdentifierExpr && SQLUtils.nameEquals(columnName, ((SQLIdentifierExpr)column).getName())) {
                  return unique.getColumns().size() == 1;
               }

               if (column instanceof SQLMethodInvokeExpr && SQLUtils.nameEquals(((SQLMethodInvokeExpr)column).getMethodName(), columnName)) {
                  return true;
               }
            }
         }
      }

      return false;
   }

   public MySqlUnique findUnique(String columnName) {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof MySqlUnique) {
            MySqlUnique unique = (MySqlUnique)element;
            if (unique.containsColumn(columnName)) {
               return unique;
            }
         }
      }

      return null;
   }

   public SQLTableElement findIndex(String columnName) {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLUniqueConstraint) {
            SQLUniqueConstraint unique = (SQLUniqueConstraint)element;

            for(SQLSelectOrderByItem item : unique.getColumns()) {
               SQLExpr columnExpr = item.getExpr();
               if (columnExpr instanceof SQLIdentifierExpr) {
                  String keyColumName = ((SQLIdentifierExpr)columnExpr).getName();
                  keyColumName = SQLUtils.normalize(keyColumName);
                  if (keyColumName.equalsIgnoreCase(columnName)) {
                     return element;
                  }
               }
            }
         }
      }

      return null;
   }

   public void forEachColumn(Consumer<SQLColumnDefinition> columnConsumer) {
      if (columnConsumer != null) {
         for(SQLTableElement element : this.tableElementList) {
            if (element instanceof SQLColumnDefinition) {
               columnConsumer.accept((SQLColumnDefinition)element);
            }
         }

      }
   }

   public SQLPrimaryKey findPrimaryKey() {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLPrimaryKey) {
            return (SQLPrimaryKey)element;
         }
      }

      return null;
   }

   public List<SQLForeignKeyConstraint> findForeignKey() {
      List<SQLForeignKeyConstraint> fkList = new ArrayList();

      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLForeignKeyConstraint) {
            fkList.add((SQLForeignKeyConstraint)element);
         }
      }

      return fkList;
   }

   public boolean hashForeignKey() {
      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLForeignKeyConstraint) {
            return true;
         }
      }

      return false;
   }

   public boolean isReferenced(SQLName tableName) {
      return tableName == null ? false : this.isReferenced(tableName.getSimpleName());
   }

   public boolean isReferenced(String tableName) {
      if (tableName == null) {
         return false;
      } else {
         tableName = SQLUtils.normalize(tableName);

         for(SQLTableElement element : this.tableElementList) {
            if (element instanceof SQLForeignKeyConstraint) {
               SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint)element;
               String refTableName = fk.getReferencedTableName().getSimpleName();
               if (SQLUtils.nameEquals(tableName, refTableName)) {
                  return true;
               }
            }
         }

         return false;
      }
   }

   public SQLAlterTableStatement foreignKeyToAlterTable() {
      SQLAlterTableStatement stmt = new SQLAlterTableStatement();

      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement element = (SQLTableElement)this.tableElementList.get(i);
         if (element instanceof SQLForeignKeyConstraint) {
            SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint)element;
            this.tableElementList.remove(i);
            stmt.addItem(new SQLAlterTableAddConstraint(fk));
         }
      }

      if (stmt.getItems().size() == 0) {
         return null;
      } else {
         stmt.setDbType(this.getDbType());
         stmt.setTableSource(this.tableSource.clone());
         Collections.reverse(stmt.getItems());
         return stmt;
      }
   }

   public static void sort(List<SQLStatement> stmtList) {
      Map<String, SQLCreateTableStatement> tables = new HashMap();
      Map<String, List<SQLCreateTableStatement>> referencedTables = new HashMap();

      for(SQLStatement stmt : stmtList) {
         if (stmt instanceof SQLCreateTableStatement) {
            SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement)stmt;
            String tableName = createTableStmt.getName().getSimpleName();
            tableName = SQLUtils.normalize(tableName).toLowerCase();
            tables.put(tableName, createTableStmt);
         }
      }

      List<ListDG.Edge> edges = new ArrayList();

      for(SQLCreateTableStatement stmt : tables.values()) {
         for(SQLTableElement element : stmt.getTableElementList()) {
            if (element instanceof SQLForeignKeyConstraint) {
               SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint)element;
               String refTableName = fk.getReferencedTableName().getSimpleName();
               refTableName = SQLUtils.normalize(refTableName).toLowerCase();
               SQLCreateTableStatement refTable = (SQLCreateTableStatement)tables.get(refTableName);
               if (refTable != null) {
                  edges.add(new ListDG.Edge(stmt, refTable));
               }

               List<SQLCreateTableStatement> referencedList = (List)referencedTables.get(refTableName);
               if (referencedList == null) {
                  referencedList = new ArrayList();
                  referencedTables.put(refTableName, referencedList);
               }

               referencedList.add(stmt);
            }
         }
      }

      for(SQLStatement stmt : stmtList) {
         if (stmt instanceof OracleCreateSynonymStatement) {
            OracleCreateSynonymStatement createSynonym = (OracleCreateSynonymStatement)stmt;
            SQLName object = createSynonym.getObject();
            String refTableName = object.getSimpleName();
            SQLCreateTableStatement refTable = (SQLCreateTableStatement)tables.get(refTableName);
            if (refTable != null) {
               edges.add(new ListDG.Edge(stmt, refTable));
            }
         }
      }

      ListDG dg = new ListDG(stmtList, edges);
      SQLStatement[] tops = new SQLStatement[stmtList.size()];
      if (dg.topologicalSort(tops)) {
         int i = 0;

         for(int size = stmtList.size(); i < size; ++i) {
            stmtList.set(i, tops[size - i - 1]);
         }

      } else {
         List<SQLAlterTableStatement> alterList = new ArrayList();

         for(int i = edges.size() - 1; i >= 0; --i) {
            ListDG.Edge edge = (ListDG.Edge)edges.get(i);
            SQLCreateTableStatement from = (SQLCreateTableStatement)edge.from;
            String fromTableName = from.getName().getSimpleName();
            fromTableName = SQLUtils.normalize(fromTableName).toLowerCase();
            if (referencedTables.containsKey(fromTableName)) {
               edges.remove(i);
               Arrays.fill(tops, null);
               tops = new SQLStatement[stmtList.size()];
               dg = new ListDG(stmtList, edges);
               if (dg.topologicalSort(tops)) {
                  int j = 0;

                  for(int size = stmtList.size(); j < size; ++j) {
                     SQLStatement stmt = tops[size - j - 1];
                     stmtList.set(j, stmt);
                  }

                  SQLAlterTableStatement alter = from.foreignKeyToAlterTable();
                  alterList.add(alter);
                  stmtList.add(alter);
                  return;
               }

               edges.add(i, edge);
            }
         }

         for(int i = edges.size() - 1; i >= 0; --i) {
            ListDG.Edge edge = (ListDG.Edge)edges.get(i);
            SQLCreateTableStatement from = (SQLCreateTableStatement)edge.from;
            String fromTableName = from.getName().getSimpleName();
            fromTableName = SQLUtils.normalize(fromTableName).toLowerCase();
            if (referencedTables.containsKey(fromTableName)) {
               SQLAlterTableStatement alter = from.foreignKeyToAlterTable();
               edges.remove(i);
               if (alter != null) {
                  alterList.add(alter);
               }

               Arrays.fill(tops, null);
               tops = new SQLStatement[stmtList.size()];
               dg = new ListDG(stmtList, edges);
               if (dg.topologicalSort(tops)) {
                  int j = 0;

                  for(int size = stmtList.size(); j < size; ++j) {
                     SQLStatement stmt = tops[size - j - 1];
                     stmtList.set(j, stmt);
                  }

                  stmtList.addAll(alterList);
                  return;
               }
            }
         }

      }
   }

   public void simplify() {
      SQLName name = this.getName();
      if (name instanceof SQLPropertyExpr) {
         String tableName = ((SQLPropertyExpr)name).getName();
         tableName = SQLUtils.normalize(tableName);
         String normalized = SQLUtils.normalize(tableName, this.dbType);
         if (tableName != normalized) {
            this.setName(normalized);
            name = this.getName();
         }
      }

      if (name instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr identExpr = (SQLIdentifierExpr)name;
         String tableName = identExpr.getName();
         String normalized = SQLUtils.normalize(tableName, this.dbType);
         if (normalized != tableName) {
            this.setName(normalized);
         }
      }

      for(SQLTableElement element : this.tableElementList) {
         if (element instanceof SQLColumnDefinition) {
            SQLColumnDefinition column = (SQLColumnDefinition)element;
            column.simplify();
         } else if (element instanceof SQLConstraint) {
            ((SQLConstraint)element).simplify();
         }
      }

   }

   public boolean apply(SQLDropIndexStatement x) {
      long indexNameHashCode64 = x.getIndexName().nameHashCode64();

      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
         if (e instanceof SQLUniqueConstraint) {
            SQLUniqueConstraint unique = (SQLUniqueConstraint)e;
            if (unique.getName().nameHashCode64() == indexNameHashCode64) {
               this.tableElementList.remove(i);
               return true;
            }
         }
      }

      return false;
   }

   public boolean apply(SQLCommentStatement x) {
      SQLName on = x.getOn().getName();
      SQLExpr comment = x.getComment();
      if (comment == null) {
         return false;
      } else {
         SQLCommentStatement.Type type = x.getType();
         if (type == SQLCommentStatement.Type.TABLE) {
            if (!SQLUtils.nameEquals(this.getName(), on)) {
               return false;
            } else {
               this.setComment(comment.clone());
               return true;
            }
         } else if (type == SQLCommentStatement.Type.COLUMN) {
            SQLPropertyExpr propertyExpr = (SQLPropertyExpr)on;
            if (!SQLUtils.nameEquals(this.getName(), (SQLName)propertyExpr.getOwner())) {
               return false;
            } else {
               SQLColumnDefinition column = this.findColumn(propertyExpr.nameHashCode64());
               if (column != null) {
                  column.setComment(comment.clone());
               }

               return true;
            }
         } else {
            return false;
         }
      }
   }

   public boolean apply(SQLAlterTableStatement alter) {
      if (!SQLUtils.nameEquals(alter.getName(), this.getName())) {
         return false;
      } else {
         int applyCount = 0;

         for(SQLAlterTableItem item : alter.getItems()) {
            if (this.alterApply(item)) {
               ++applyCount;
            }
         }

         return applyCount > 0;
      }
   }

   protected boolean alterApply(SQLAlterTableItem item) {
      if (item instanceof SQLAlterTableDropColumnItem) {
         return this.apply((SQLAlterTableDropColumnItem)item);
      } else if (item instanceof SQLAlterTableAddColumn) {
         return this.apply((SQLAlterTableAddColumn)item);
      } else if (item instanceof SQLAlterTableAddConstraint) {
         return this.apply((SQLAlterTableAddConstraint)item);
      } else if (item instanceof SQLAlterTableDropPrimaryKey) {
         return this.apply((SQLAlterTableDropPrimaryKey)item);
      } else if (item instanceof SQLAlterTableDropIndex) {
         return this.apply((SQLAlterTableDropIndex)item);
      } else if (item instanceof SQLAlterTableDropConstraint) {
         return this.apply((SQLAlterTableDropConstraint)item);
      } else if (item instanceof SQLAlterTableDropKey) {
         return this.apply((SQLAlterTableDropKey)item);
      } else if (item instanceof SQLAlterTableDropForeignKey) {
         return this.apply((SQLAlterTableDropForeignKey)item);
      } else if (item instanceof SQLAlterTableRename) {
         return this.apply((SQLAlterTableRename)item);
      } else if (item instanceof SQLAlterTableRenameColumn) {
         return this.apply((SQLAlterTableRenameColumn)item);
      } else {
         return item instanceof SQLAlterTableAddIndex ? this.apply((SQLAlterTableAddIndex)item) : false;
      }
   }

   private boolean apply(SQLAlterTableRenameColumn item) {
      int columnIndex = this.columnIndexOf(item.getColumn());
      if (columnIndex == -1) {
         return false;
      } else {
         SQLColumnDefinition column = (SQLColumnDefinition)this.tableElementList.get(columnIndex);
         column.setName(item.getTo().clone());
         return true;
      }
   }

   public boolean renameColumn(String colummName, String newColumnName) {
      if (colummName != null && newColumnName != null && newColumnName.length() != 0) {
         int columnIndex = this.columnIndexOf(new SQLIdentifierExpr(colummName));
         if (columnIndex == -1) {
            return false;
         } else {
            SQLColumnDefinition column = (SQLColumnDefinition)this.tableElementList.get(columnIndex);
            column.setName((SQLName)(new SQLIdentifierExpr(newColumnName)));
            return true;
         }
      } else {
         return false;
      }
   }

   private boolean apply(SQLAlterTableRename item) {
      SQLName name = item.getToName();
      if (name == null) {
         return false;
      } else {
         this.setName(name.clone());
         return true;
      }
   }

   private boolean apply(SQLAlterTableDropForeignKey item) {
      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
         if (e instanceof SQLForeignKeyConstraint) {
            SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint)e;
            if (SQLUtils.nameEquals(fk.getName(), item.getIndexName())) {
               this.tableElementList.remove(i);
               return true;
            }
         }
      }

      return false;
   }

   private boolean apply(SQLAlterTableDropKey item) {
      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
         if (e instanceof SQLUniqueConstraint) {
            SQLUniqueConstraint unique = (SQLUniqueConstraint)e;
            if (SQLUtils.nameEquals(unique.getName(), item.getKeyName())) {
               this.tableElementList.remove(i);
               return true;
            }
         }
      }

      return false;
   }

   private boolean apply(SQLAlterTableDropConstraint item) {
      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
         if (e instanceof SQLConstraint) {
            SQLConstraint constraint = (SQLConstraint)e;
            if (SQLUtils.nameEquals(constraint.getName(), item.getConstraintName())) {
               this.tableElementList.remove(i);
               return true;
            }
         }
      }

      return false;
   }

   private boolean apply(SQLAlterTableDropIndex item) {
      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
         if (e instanceof SQLUniqueConstraint) {
            SQLUniqueConstraint unique = (SQLUniqueConstraint)e;
            if (SQLUtils.nameEquals(unique.getName(), item.getIndexName())) {
               this.tableElementList.remove(i);
               return true;
            }
         }
      }

      return false;
   }

   private boolean apply(SQLAlterTableDropPrimaryKey item) {
      for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
         SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
         if (e instanceof SQLPrimaryKey) {
            this.tableElementList.remove(i);
            return true;
         }
      }

      return false;
   }

   private boolean apply(SQLAlterTableAddConstraint item) {
      SQLName name = item.getConstraint().getName();
      if (name != null) {
         long nameHashCode = name.nameHashCode64();

         for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
            SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
            if (e instanceof SQLConstraint) {
               SQLName name1 = ((SQLConstraint)e).getName();
               if (name1 != null && name1.nameHashCode64() == nameHashCode) {
                  return false;
               }
            }
         }
      }

      this.tableElementList.add((SQLTableElement)item.getConstraint());
      return true;
   }

   private boolean apply(SQLAlterTableDropColumnItem item) {
      for(SQLName column : item.getColumns()) {
         String columnName = column.getSimpleName();

         for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
            SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
            if (e instanceof SQLColumnDefinition && SQLUtils.nameEquals(columnName, ((SQLColumnDefinition)e).getName().getSimpleName())) {
               this.tableElementList.remove(i);
            }
         }

         for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
            SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
            if (e instanceof SQLUnique) {
               SQLUnique unique = (SQLUnique)e;
               unique.applyDropColumn(column);
               if (unique.getColumns().size() == 0) {
                  this.tableElementList.remove(i);
               }
            }
         }
      }

      return true;
   }

   protected boolean apply(SQLAlterTableAddIndex item) {
      return false;
   }

   private boolean apply(SQLAlterTableAddColumn item) {
      int startIndex = this.tableElementList.size();
      if (item.isFirst()) {
         startIndex = 0;
      }

      int afterIndex = this.columnIndexOf(item.getAfterColumn());
      if (afterIndex != -1) {
         startIndex = afterIndex + 1;
      }

      int beforeIndex = this.columnIndexOf(item.getFirstColumn());
      if (beforeIndex != -1) {
         startIndex = beforeIndex;
      }

      for(int i = 0; i < item.getColumns().size(); ++i) {
         SQLColumnDefinition column = (SQLColumnDefinition)item.getColumns().get(i);
         int matchIndex = -1;

         for(int j = 0; j < this.tableElementList.size(); ++j) {
            SQLTableElement element = (SQLTableElement)this.tableElementList.get(j);
            if (element instanceof SQLColumnDefinition && column.nameHashCode64() == ((SQLColumnDefinition)element).nameHashCode64()) {
               matchIndex = j;
               break;
            }
         }

         if (matchIndex != -1) {
            return false;
         }

         this.tableElementList.add(i + startIndex, column);
         column.setParent(this);
      }

      return true;
   }

   protected int columnIndexOf(SQLName column) {
      if (column == null) {
         return -1;
      } else {
         String columnName = column.getSimpleName();

         for(int i = this.tableElementList.size() - 1; i >= 0; --i) {
            SQLTableElement e = (SQLTableElement)this.tableElementList.get(i);
            if (e instanceof SQLColumnDefinition && SQLUtils.nameEquals(columnName, ((SQLColumnDefinition)e).getName().getSimpleName())) {
               return i;
            }
         }

         return -1;
      }
   }

   public void cloneTo(SQLCreateTableStatement x) {
      x.setExternal(this.external);
      x.ifNotExists = this.ifNotExists;
      x.type = this.type;
      if (this.tableSource != null) {
         x.setTableSource(this.tableSource.clone());
      }

      for(SQLTableElement e : this.tableElementList) {
         SQLTableElement e2 = e.clone();
         e2.setParent(x);
         x.tableElementList.add(e2);
      }

      for(SQLColumnDefinition e : this.partitionColumns) {
         SQLColumnDefinition e2 = e.clone();
         e2.setParent(x);
         x.partitionColumns.add(e2);
      }

      if (this.inherits != null) {
         x.setInherits(this.inherits.clone());
      }

      if (this.select != null) {
         x.setSelect(this.select.clone());
      }

      if (this.comment != null) {
         x.setComment(this.comment.clone());
      }

      if (this.partitioning != null) {
         x.setPartitioning(this.partitioning.clone());
      }

      if (this.like != null) {
         x.setLike(this.like.clone());
      }

      x.compress = this.compress;
      x.logging = this.logging;
      if (this.tablespace != null) {
         x.setTablespace(this.tablespace.clone());
      }

      if (this.partitioning != null) {
         x.setPartitioning(this.partitioning.clone());
      }

      if (this.storedAs != null) {
         x.setStoredAs(this.storedAs.clone());
      }

      if (this.location != null) {
         x.setLocation(this.location.clone());
      }

      x.onCommitPreserveRows = this.onCommitPreserveRows;
      x.onCommitDeleteRows = this.onCommitDeleteRows;

      for(SQLAssignItem item : this.tableOptions) {
         SQLAssignItem item2 = item.clone();
         item2.setParent(item);
         x.tableOptions.add(item2);
      }

      for(SQLAssignItem item : this.tblProperties) {
         SQLAssignItem item2 = item.clone();
         item2.setParent(item);
         x.tblProperties.add(item2);
      }

      if (this.rowFormat != null) {
         x.setRowFormat(this.rowFormat.clone());
      }

      if (this.clusteringType != null) {
         x.setClusteringType(this.clusteringType);
      }

      for(SQLSelectOrderByItem e : this.clusteredBy) {
         SQLSelectOrderByItem e2 = e.clone();
         e2.setParent(x);
         x.clusteredBy.add(e2);
      }

      for(SQLSelectOrderByItem e : this.sortedBy) {
         SQLSelectOrderByItem e2 = e.clone();
         e2.setParent(x);
         x.sortedBy.add(e2);
      }

      x.buckets = this.buckets;
      x.shards = this.shards;
      x.dimension = this.dimension;
   }

   public boolean isReplace() {
      return this.replace;
   }

   public void setReplace(boolean replace) {
      this.ignore = false;
      this.replace = replace;
   }

   public boolean isIgnore() {
      return this.ignore;
   }

   public void setIgnore(boolean ignore) {
      this.replace = false;
      this.ignore = ignore;
   }

   public SQLExpr getStoredAs() {
      return this.storedAs;
   }

   public void setStoredAs(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
      }

      this.storedAs = x;
   }

   public SQLCreateTableStatement clone() {
      SQLCreateTableStatement x = new SQLCreateTableStatement(this.dbType);
      this.cloneTo(x);
      return x;
   }

   public String toString() {
      return SQLUtils.toSQLString(this, (DbType)this.dbType);
   }

   public boolean isOnCommitPreserveRows() {
      return this.onCommitPreserveRows;
   }

   public void setOnCommitPreserveRows(boolean onCommitPreserveRows) {
      this.onCommitPreserveRows = onCommitPreserveRows;
   }

   public boolean isExternal() {
      return this.external;
   }

   public void setExternal(boolean external) {
      this.external = external;
   }

   public ClusteringType getClusteringType() {
      return this.clusteringType;
   }

   public void setClusteringType(ClusteringType clusteringType) {
      this.clusteringType = clusteringType;
   }

   public List<SQLSelectOrderByItem> getClusteredBy() {
      return this.clusteredBy;
   }

   public void addClusteredByItem(SQLSelectOrderByItem item) {
      item.setParent(this);
      this.clusteredBy.add(item);
   }

   public List<SQLSelectOrderByItem> getSortedBy() {
      return this.sortedBy;
   }

   public void addSortedByItem(SQLSelectOrderByItem item) {
      item.setParent(this);
      this.sortedBy.add(item);
   }

   public int getBuckets() {
      return this.buckets;
   }

   public void setBuckets(int buckets) {
      this.buckets = buckets;
   }

   public int getShards() {
      return this.shards;
   }

   public void setShards(int shards) {
      this.shards = shards;
   }

   public List<SQLColumnDefinition> getPartitionColumns() {
      return this.partitionColumns;
   }

   public void addPartitionColumn(SQLColumnDefinition column) {
      if (column != null) {
         column.setParent(this);
      }

      this.partitionColumns.add(column);
   }

   public List<SQLAssignItem> getTableOptions() {
      return this.tableOptions;
   }

   public List<SQLAssignItem> getTblProperties() {
      return this.tblProperties;
   }

   public void addTblProperty(String name, SQLExpr value) {
      SQLAssignItem assignItem = new SQLAssignItem(new SQLIdentifierExpr(name), value);
      assignItem.setParent(this);
      this.tblProperties.add(assignItem);
   }

   public SQLExternalRecordFormat getRowFormat() {
      return this.rowFormat;
   }

   public void setRowFormat(SQLExternalRecordFormat x) {
      if (x != null) {
         x.setParent(this);
      }

      this.rowFormat = x;
   }

   public boolean isDimension() {
      return this.dimension;
   }

   public void setDimension(boolean dimension) {
      this.dimension = dimension;
   }

   public SQLExpr getLocation() {
      return this.location;
   }

   public void setLocation(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
      }

      this.location = x;
   }

   public void addOption(String name, SQLExpr value) {
      SQLAssignItem assignItem = new SQLAssignItem(new SQLIdentifierExpr(name), value);
      assignItem.setParent(this);
      this.tableOptions.add(assignItem);
   }

   public SQLExpr getOption(String name) {
      if (name == null) {
         return null;
      } else {
         long hash64 = FnvHash.hashCode64(name);

         for(SQLAssignItem item : this.tableOptions) {
            SQLExpr target = item.getTarget();
            if (target instanceof SQLIdentifierExpr && ((SQLIdentifierExpr)target).hashCode64() == hash64) {
               return item.getValue();
            }
         }

         return null;
      }
   }

   public SQLExpr getTblProperty(String name) {
      if (name == null) {
         return null;
      } else {
         long hash64 = FnvHash.hashCode64(name);

         for(SQLAssignItem item : this.tblProperties) {
            SQLExpr target = item.getTarget();
            if (target instanceof SQLIdentifierExpr && ((SQLIdentifierExpr)target).hashCode64() == hash64) {
               return item.getValue();
            }
         }

         return null;
      }
   }

   public Object getOptionValue(String name) {
      SQLExpr option = this.getOption(name);
      return option instanceof SQLValuableExpr ? ((SQLValuableExpr)option).getValue() : null;
   }

   public Object getTblPropertyValue(String name) {
      SQLExpr option = this.getTblProperty(name);
      return option instanceof SQLValuableExpr ? ((SQLValuableExpr)option).getValue() : null;
   }

   public Object getOptionOrTblPropertyValue(String name) {
      SQLExpr option = this.getTblProperty(name);
      if (option == null) {
         option = this.getOption(name);
      }

      return option instanceof SQLValuableExpr ? ((SQLValuableExpr)option).getValue() : null;
   }

   public String getCatalog() {
      return null;
   }

   public boolean containsDuplicateColumnNames() {
      return this.containsDuplicateColumnNames(false);
   }

   public boolean containsDuplicateColumnNames(boolean throwException) {
      Map<Long, SQLTableElement> columnMap = new HashMap();

      for(SQLTableElement item : this.tableElementList) {
         if (item instanceof SQLColumnDefinition) {
            SQLName columnName = ((SQLColumnDefinition)item).getName();
            long nameHashCode64 = columnName.nameHashCode64();
            SQLTableElement old = (SQLTableElement)columnMap.put(nameHashCode64, item);
            if (old != null) {
               if (throwException) {
                  throw new SemanticException("Table contains duplicate column names : " + SQLUtils.normalize(columnName.getSimpleName()));
               }

               return true;
            }
         }
      }

      return false;
   }

   public SQLExpr getEngine() {
      return this.engine;
   }

   public void setEngine(SQLExpr x) {
      if (x != null) {
         x.setParent(this);
      }

      this.engine = x;
   }

   public static enum Type {
      GLOBAL_TEMPORARY,
      LOCAL_TEMPORARY,
      TEMPORARY,
      SHADOW;
   }
}
