package com.chenyang.druid.sql.dialect.spark.parser;

import com.chenyang.druid.DbType;
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.SQLStatement;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLNullExpr;
import com.chenyang.druid.sql.ast.statement.SQLAssignItem;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCreateContextStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateDirectoryStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateDomainStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTypeStatement;
import com.chenyang.druid.sql.ast.statement.SQLExplainStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLInsertInto;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLScriptCommitStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowColumnsStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowErrorsStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowTablesStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowViewsStatement;
import com.chenyang.druid.sql.ast.statement.SQLTableConstraint;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveCreateFunctionStatement;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlExprParser;
import com.chenyang.druid.sql.dialect.spark.ast.expr.HiveRowFormat;
import com.chenyang.druid.sql.dialect.spark.ast.expr.SparkColumnSpecExpr;
import com.chenyang.druid.sql.dialect.spark.ast.expr.SparkDefProperty;
import com.chenyang.druid.sql.dialect.spark.ast.expr.SparkSelectTableReference;
import com.chenyang.druid.sql.dialect.spark.ast.expr.SparkTablePartition;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkAlterDatabaseStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkAlterTableStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkAlterViewStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkCacheTableStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkCreateDatabaseStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkCreateViewStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkExplainStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkInsertStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkLoadStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkRefreshStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkRepairTableStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkShowCreateTableStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkShowDatabasesStatement;
import com.chenyang.druid.sql.dialect.spark.ast.statement.SparkShowTableStatement;
import com.chenyang.druid.sql.parser.Lexer;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.parser.SQLCreateTableParser;
import com.chenyang.druid.sql.parser.SQLExprParser;
import com.chenyang.druid.sql.parser.SQLSelectParser;
import com.chenyang.druid.sql.parser.SQLStatementParser;
import com.chenyang.druid.sql.parser.Token;
import com.chenyang.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.List;

public class SparkStatementParser extends SQLStatementParser {
   public SparkStatementParser(String sql) {
      super((SQLExprParser)(new SparkExprParser(sql)));
      ((SparkExprParser)this.exprParser).setSparkSqlParser(this);
   }

   public SQLCreateTableParser getSQLCreateTableParser() {
      return new SparkCreateTableParser(this.exprParser);
   }

   public SparkExprParser getExprParser() {
      return (SparkExprParser)this.exprParser;
   }

   public void parseStatementList(List<SQLStatement> statementList, int max, SQLObject parent) {
      while(max == -1 || statementList.size() < max) {
         if (this.lexer.token() == Token.EOF) {
            return;
         }

         if (this.lexer.token() == Token.END) {
            return;
         }

         if (this.lexer.token() == Token.ELSE) {
            return;
         }

         if (this.lexer.token() == Token.SEMI) {
            this.lexer.nextToken();
            if (statementList.size() > 0) {
               SQLStatement lastStmt = (SQLStatement)statementList.get(statementList.size() - 1);
               lastStmt.setAfterSemi(true);
            }
         } else if (this.lexer.token() == Token.SELECT) {
            SQLStatement stmt = new SQLSelectStatement((new SparkSelectParser(this.exprParser)).select(), DbType.spark);
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.CREATE) {
            SQLStatement stmt = this.parseCreate();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.INSERT) {
            SQLStatement stmt = this.parseInsert();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.identifierEquals("LOAD")) {
            SQLStatement stmt = this.parseLoad();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.SLASH) {
            this.lexer.nextToken();
            SQLStatement stmt = new SQLScriptCommitStatement();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.ALTER) {
            SQLStatement stmt = this.parseAlter();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.WITH) {
            SQLSelectStatement stmt = new SQLSelectStatement(this.createSQLSelectParser().select(), this.dbType);
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.TRUNCATE) {
            SQLStatement stmt = this.parseTruncate();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.EXPLAIN) {
            SQLExplainStatement stmt = this.parseExplain();
            statementList.add(stmt);
         } else if (this.lexer.identifierEquals("MSCK")) {
            this.lexer.nextToken();
            SparkRepairTableStatement repairTableStatement = this.parseRepairTable();
            statementList.add(repairTableStatement);
         } else if (this.lexer.token() != Token.CACHE && !this.lexer.identifierEquals("CACHE")) {
            if (this.lexer.token() == Token.IDENTIFIER) {
               String strVal = this.lexer.stringVal();
               if (strVal.equalsIgnoreCase("RENAME")) {
                  SQLStatement stmt = this.parseRename();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (!strVal.equalsIgnoreCase("SHOW")) {
                  if (this.lexer.token() != Token.DESC && !this.lexer.identifierEquals(FnvHash.Constants.DESCRIBE)) {
                     if (this.lexer.identifierEquals("REFRESH")) {
                        SQLStatement stmt = this.parseRefresh();
                        statementList.add(stmt);
                     } else {
                        SQLExpr expr = this.exprParser.expr();
                        SQLExprStatement stmt = new SQLExprStatement(expr);
                        stmt.setDbType(this.dbType);
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     }
                  } else {
                     SQLStatement stmt = this.parseDescribe();
                     statementList.add(stmt);
                  }
               } else {
                  this.lexer.nextToken();
                  if (this.lexer.identifierEquals("ERR")) {
                     this.lexer.nextToken();
                  } else {
                     if (this.lexer.identifierEquals("DATABASES") || this.lexer.identifierEquals("SCHEMAS")) {
                        SparkShowDatabasesStatement stmt = new SparkShowDatabasesStatement();
                        if (this.lexer.identifierEquals("DATABASES")) {
                           stmt.setDatabases(true);
                        } else {
                           stmt.setDatabases(false);
                        }

                        this.lexer.nextToken();
                        if (this.lexer.token() == Token.LIKE) {
                           this.lexer.nextToken();
                           SQLExpr like = this.exprParser.expr();
                           stmt.setLike(like);
                        }

                        statementList.add(stmt);
                        continue;
                     }

                     if (this.lexer.identifierEquals("COLUMNS")) {
                        this.lexer.nextToken();
                        SQLShowColumnsStatement stmt = this.parseShowColumns();
                        statementList.add(stmt);
                        continue;
                     }

                     if (this.lexer.identifierEquals("TABLES")) {
                        this.lexer.nextToken();
                        SQLShowTablesStatement stmt = this.parseShowTables();
                        statementList.add(stmt);
                        continue;
                     }

                     if (this.lexer.token() == Token.TABLE) {
                        this.lexer.nextToken();
                        SQLStatement stmt = this.parseShowTable();
                        statementList.add(stmt);
                        continue;
                     }
                  }

                  if (!this.lexer.identifierEquals(FnvHash.Constants.VIEWS)) {
                     if (this.lexer.token() == Token.CREATE) {
                        this.lexer.nextToken();
                        this.accept(Token.TABLE);
                        SparkShowCreateTableStatement stmt = new SparkShowCreateTableStatement();
                        stmt.setTable(new SQLExprTableSource(this.exprParser.expr()));
                        if (this.lexer.token() == Token.AS) {
                           this.lexer.nextToken();
                           this.acceptIdentifier("SERDE");
                           stmt.setSerde(true);
                        }

                        statementList.add(stmt);
                     } else {
                        this.accept(Token.ERRORS);
                        SQLShowErrorsStatement stmt = new SQLShowErrorsStatement();
                        stmt.setDbType(this.dbType);
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     }
                  } else {
                     this.lexer.nextToken();
                     SQLShowViewsStatement stmt = new SQLShowViewsStatement();
                     if (this.lexer.token() == Token.IN || this.lexer.token() == Token.FROM) {
                        this.lexer.nextToken();
                        SQLName db = this.exprParser.name();
                        stmt.setDatabase(db);
                     }

                     if (this.lexer.token() == Token.LIKE) {
                        this.lexer.nextToken();
                        SQLExpr pattern = this.exprParser.expr();
                        stmt.setLike(pattern);
                     }

                     statementList.add(stmt);
                  }
               }
            } else if (this.lexer.token() != Token.LPAREN) {
               if (this.lexer.token() == Token.COMMENT) {
                  statementList.add(this.parseComment());
               } else if (this.lexer.token() == Token.DROP) {
                  Lexer.SavePoint savePoint = this.lexer.mark();
                  this.lexer.nextToken();
                  boolean temporary = false;
                  if (this.lexer.identifierEquals(Token.TEMPORARY.name)) {
                     this.lexer.nextToken();
                     temporary = true;
                  }

                  if (this.lexer.token() == Token.TABLE) {
                     SQLDropTableStatement stmt = this.parseDropTable(false);
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() != Token.DATABASE && this.lexer.token() != Token.SCHEMA) {
                     if (this.lexer.token() == Token.VIEW) {
                        SQLStatement stmt = this.parseDropView(false);
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
                        this.lexer.reset(savePoint);
                        SQLStatement stmt = this.parseDropType();
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else {
                        if (this.lexer.token() != Token.FUNCTION) {
                           throw new ParserException("TODO : " + this.lexer.info());
                        }

                        SQLDropFunctionStatement dropFunc = this.parseDropFunction(false);
                        dropFunc.setTemporary(temporary);
                        statementList.add(dropFunc);
                     }
                  } else {
                     this.lexer.nextToken();
                     SQLDropDatabaseStatement stmt = new SQLDropDatabaseStatement();
                     if (this.lexer.token() == Token.IF) {
                        stmt.setIfExists(true);
                        this.lexer.nextToken();
                        this.lexer.nextToken();
                     }

                     stmt.setDatabase((SQLExpr)this.exprParser.name());
                     if (this.lexer.identifierEquals(Token.RESTRICT.name)) {
                        stmt.setRestrict(true);
                        this.lexer.nextToken();
                     }

                     if (this.lexer.identifierEquals(Token.CASCADE.name)) {
                        stmt.setCascade(true);
                        this.lexer.nextToken();
                     }

                     statementList.add(stmt);
                  }
               } else if (this.lexer.token() == Token.NULL) {
                  this.lexer.nextToken();
                  SQLExprStatement stmt = new SQLExprStatement(new SQLNullExpr());
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.CASE) {
                  SQLStatement stmt = this.parseCase();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.FUNCTION) {
                  SQLStatement stmt = this.parseFunction();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.QUES) {
                  SQLExpr expr = this.exprParser.expr();
                  SQLExprStatement stmt = new SQLExprStatement(expr);
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.USE) {
                  SQLStatement stmt = this.parseUse();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else {
                  if (this.lexer.token() != Token.DESC && !this.lexer.identifierEquals(FnvHash.Constants.DESCRIBE)) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  SQLStatement stmt = this.parseDescribe();
                  statementList.add(stmt);
               }
            } else {
               Lexer.SavePoint savePoint = this.lexer.mark();
               this.lexer.nextToken();

               int parenCount;
               for(parenCount = 0; this.lexer.token() == Token.LPAREN; ++parenCount) {
                  savePoint = this.lexer.mark();
                  this.lexer.nextToken();
               }

               if (this.lexer.token() != Token.SELECT) {
                  throw new ParserException("TODO : " + this.lexer.info());
               }

               this.lexer.reset(savePoint);
               SQLStatement stmt = this.parseSelect();
               stmt.setParent(parent);
               statementList.add(stmt);

               for(int i = 0; i < parenCount; ++i) {
                  this.accept(Token.RPAREN);
               }
            }
         } else {
            this.lexer.nextToken();
            SparkCacheTableStatement stmt = this.parseSparkCacheTable();
            statementList.add(stmt);
         }
      }

   }

   private SparkRepairTableStatement parseRepairTable() {
      this.acceptIdentifier("REPAIR");
      this.accept(Token.TABLE);
      SparkRepairTableStatement repairTableStatement = new SparkRepairTableStatement();
      repairTableStatement.setName(this.exprParser.name());
      if (this.lexer.identifierEquals("ADD")) {
         this.lexer.nextToken();
         repairTableStatement.setAction(SparkRepairTableStatement.PartitionAction.ADD);
         this.acceptIdentifier("PARTITIONS");
      } else if (this.lexer.token() == Token.DROP) {
         this.lexer.nextToken();
         repairTableStatement.setAction(SparkRepairTableStatement.PartitionAction.DROP);
         this.acceptIdentifier("PARTITIONS");
      } else if (this.lexer.identifierEquals("SYNC")) {
         this.lexer.nextToken();
         repairTableStatement.setAction(SparkRepairTableStatement.PartitionAction.SYNC);
         this.acceptIdentifier("PARTITIONS");
      }

      return repairTableStatement;
   }

   public SQLStatement parseDropType() {
      if (this.lexer.token() == Token.DROP) {
         this.lexer.nextToken();
      }

      SQLDropTypeStatement stmt = new SQLDropTypeStatement();
      stmt.setDbType(this.dbType);
      this.acceptIdentifier("TYPE");
      stmt.setName(this.exprParser.name());
      return stmt;
   }

   public SQLStatement parseFunction() {
      return null;
   }

   public SQLStatement parseInsert() {
      SparkInsertStatement insert = new SparkInsertStatement();
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         insert.addBeforeComment(this.lexer.readAndResetComments());
      }

      SQLSelectParser selectParser = this.createSQLSelectParser();
      this.accept(Token.INSERT);
      if (this.lexer.token() == Token.INTO) {
         this.lexer.nextToken();
      } else {
         this.accept(Token.OVERWRITE);
         insert.setOverwrite(true);
      }

      if (this.lexer.identifierEquals("LOCAL")) {
         insert.setLocal(true);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals("DIRECTORY")) {
         this.lexer.nextToken();
         insert.setDirectory(true);
         if (this.lexer.token() != Token.USING && this.lexer.token() != Token.ROW && this.lexer.token() != Token.VALUES && !this.lexer.identifierEquals("STORED")) {
            insert.setDirectoryPath(this.exprParser.expr());
         }

         boolean flag = true;

         while(flag) {
            if (this.lexer.token() == Token.USING) {
               this.lexer.nextToken();
               SparkInsertStatement.SparkFormat format = new SparkInsertStatement.SparkFormat();
               insert.setSparkFormat(format);
               format.setParent(insert);
               format.setFileFormat(this.exprParser.expr());
               if (this.lexer.identifierEquals("OPTIONS")) {
                  this.lexer.nextToken();

                  SQLExpr key;
                  SQLExpr value;
                  for(; this.lexer.token() != Token.RPAREN; format.addOption(new SQLAssignItem(key, value))) {
                     this.lexer.nextToken();
                     value = null;
                     key = this.exprParser.expr();
                     if (key instanceof SQLBinaryOpExpr) {
                        SQLBinaryOpExpr b = (SQLBinaryOpExpr)key;
                        key = b.getLeft();
                        value = b.getRight();
                     } else {
                        value = this.exprParser.expr();
                     }
                  }

                  this.lexer.nextToken();
               }
            } else if (this.lexer.token() == Token.ROW) {
               SparkExprParser parser = (SparkExprParser)this.exprParser;
               HiveRowFormat hiveRowFormat = parser.parseHiveRowFormat();
               insert.setHiveFormat(hiveRowFormat);
            } else if (this.lexer.identifierEquals("STORED")) {
               this.lexer.nextToken();
               this.accept(Token.AS);
               insert.setStoredAs(this.exprParser.expr());
            } else {
               flag = false;
            }
         }
      } else {
         if (this.lexer.token() == Token.TABLE) {
            this.lexer.nextToken();
         }

         this.parseTable(insert);
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();
            this.exprParser.exprList(insert.getColumns(), insert);
            this.lexer.nextToken();
         }
      }

      if (this.lexer.token() == Token.FROM) {
         this.lexer.nextToken();
         insert.setFromTable(this.exprParser.expr());
      }

      if (this.lexer.token() == Token.VALUES) {
         this.lexer.nextToken();

         while(true) {
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
               this.exprParser.exprList(values.getValues(), values);
               insert.addValueCause(values);
               this.accept(Token.RPAREN);
            }

            if (this.lexer.token() != Token.COMMA) {
               break;
            }

            this.lexer.nextToken();
         }
      } else if (this.lexer.token() == Token.TABLE) {
         this.lexer.nextToken();
         SQLExpr table = this.exprParser.expr();
         insert.setQueryTable(table);
      } else {
         if (this.lexer.token() == Token.AS || this.lexer.identifierEquals("AS")) {
            this.lexer.nextToken();
         }

         SQLSelect query = selectParser.select();
         insert.setQuery(query);
      }

      return insert;
   }

   protected void parseTable(SQLInsertInto insertStatement) {
      SQLName tableName = this.exprParser.name();
      SparkSelectTableReference ref = new SparkSelectTableReference();
      ref.setExpr(tableName);
      if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();

         SparkTablePartition partition;
         SQLExpr col;
         SQLExpr value;
         for(partition = new SparkTablePartition(); this.lexer.token() != Token.RPAREN; partition.addDef(col, value)) {
            this.lexer.nextToken();
            value = null;
            col = this.exprParser.expr();
            if (col instanceof SQLBinaryOpExpr) {
               SQLBinaryOpExpr b = (SQLBinaryOpExpr)col;
               value = b.getRight();
               col = b.getLeft();
            } else {
               this.accept(Token.EQ);
               value = this.exprParser.expr();
            }
         }

         this.lexer.nextToken();
         ref.setPartition(partition);
      }

      insertStatement.setTableSource((SQLExprTableSource)ref);
   }

   protected SQLStatement parseLoad() {
      this.acceptIdentifier("LOAD");
      this.acceptIdentifier("DATA");
      SparkLoadStatement statement = new SparkLoadStatement();
      if (this.lexer.identifierEquals("LOCAL")) {
         this.lexer.nextToken();
         statement.setLocal(true);
      }

      this.acceptIdentifier("INPATH");
      statement.setInPath(this.exprParser.expr());
      if (this.lexer.token() == Token.OVERWRITE) {
         this.lexer.nextToken();
         statement.setOverwrite(true);
      }

      this.accept(Token.INTO);
      this.accept(Token.TABLE);
      SQLName tableName = this.exprParser.name();
      SparkSelectTableReference ref = new SparkSelectTableReference();
      ref.setExpr(tableName);
      if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();

         SparkTablePartition partition;
         SQLExpr col;
         SQLExpr value;
         for(partition = new SparkTablePartition(); this.lexer.token() != Token.RPAREN; partition.addDef(col, value)) {
            this.lexer.nextToken();
            value = null;
            col = this.exprParser.expr();
            if (col instanceof SQLBinaryOpExpr) {
               SQLBinaryOpExpr b = (SQLBinaryOpExpr)col;
               value = b.getRight();
               col = b.getLeft();
            } else {
               this.accept(Token.EQ);
               value = this.exprParser.expr();
            }
         }

         this.lexer.nextToken();
         ref.setPartition(partition);
      }

      statement.setTable(ref);
      return statement;
   }

   public SQLExplainStatement parseExplain() {
      this.accept(Token.EXPLAIN);
      SparkExplainStatement explain = new SparkExplainStatement(this.dbType);
      if (this.lexer.identifierEquals(FnvHash.Constants.EXTENDED)) {
         this.lexer.nextToken();
         explain.setExtended(true);
      } else if (this.lexer.identifierEquals("CODEGEN")) {
         this.lexer.nextToken();
         explain.setCodegen(true);
      } else if (this.lexer.identifierEquals("COST")) {
         this.lexer.nextToken();
         explain.setCost(true);
      } else if (this.lexer.identifierEquals("FORMATTED")) {
         this.lexer.nextToken();
         explain.setFormatted(true);
      }

      explain.setStatement(this.parseStatement());
      return explain;
   }

   public SQLStatement parseAlter() {
      List<String> comments = null;
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         comments = this.lexer.readAndResetComments();
      }

      this.accept(Token.ALTER);
      if (this.lexer.token() != Token.DATABASE && this.lexer.token() != Token.SCHEMA && !this.lexer.identifierEquals("NAMESPACE")) {
         if (this.lexer.token() == Token.TABLE) {
            SQLStatement alterTable = this.parseAlterTable();
            if (comments != null) {
               alterTable.addBeforeComment(comments);
            }

            return alterTable;
         } else if (this.lexer.token() == Token.VIEW) {
            SQLStatement alterView = this.parseAlterView();
            if (comments != null) {
               alterView.addBeforeComment(comments);
            }

            return alterView;
         } else {
            throw new ParserException("TODO " + this.lexer.info());
         }
      } else {
         SQLStatement alterDatabase = this.parseAlterDatabase();
         if (comments != null) {
            alterDatabase.addBeforeComment(comments);
         }

         return alterDatabase;
      }
   }

   public SQLStatement parseAlterDatabase() {
      SparkAlterDatabaseStatement stmt = new SparkAlterDatabaseStatement();
      stmt.setAlterType(new SQLIdentifierExpr(this.lexer.stringVal()));
      this.lexer.nextToken();
      stmt.setName(this.exprParser.expr());
      this.accept(Token.SET);
      if (this.lexer.identifierEquals("LOCATION")) {
         this.lexer.nextToken();
         stmt.setLocation(this.exprParser.expr());
      } else {
         stmt.setPropType(new SQLIdentifierExpr(this.lexer.stringVal()));
         this.lexer.nextToken();
         List<SparkDefProperty> props = this.parseProperties();
         stmt.setProps(props);
      }

      return stmt;
   }

   public SQLStatement parseAlterTable() {
      this.accept(Token.TABLE);
      SparkAlterTableStatement stmt = new SparkAlterTableStatement();
      SparkSelectTableReference table = this.parseTable();
      stmt.setTable(table);
      if (this.lexer.identifierEquals("RENAME")) {
         this.lexer.nextToken();
         if (this.lexer.token() != Token.TO && !this.lexer.identifierEquals("TO")) {
            if (this.lexer.token() == Token.COLUMN || this.lexer.identifierEquals("COLUMN")) {
               this.lexer.nextToken();
               stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.RENAME_COLUMN);
               SparkAlterTableStatement.SparkAlterTableRenameColumn renameColumn = new SparkAlterTableStatement.SparkAlterTableRenameColumn();
               renameColumn.setColumn(this.exprParser.expr());
               this.accept(Token.TO);
               renameColumn.setTo(this.exprParser.expr());
               stmt.setRenameColumn(renameColumn);
            }
         } else {
            this.lexer.nextToken();
            stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.RENAME_TABLE_NAME);
            SparkAlterTableStatement.SparkAlterTableReNameTo rename = new SparkAlterTableStatement.SparkAlterTableReNameTo();
            if (table.getPartition() == null) {
               rename.setPartition(this.parsePartition());
            } else {
               rename.setName(this.exprParser.expr());
            }

            stmt.setRename(rename);
         }
      } else if (this.lexer.identifierEquals("ADD")) {
         this.lexer.nextToken();
         if (this.lexer.token() != Token.COLUMNS && !this.lexer.identifierEquals("COLUMNS")) {
            stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.ADD_OR_DROP_PARTITION);
            SparkAlterTableStatement.SparkAlterTableAddOrDropPartition addPartition = new SparkAlterTableStatement.SparkAlterTableAddOrDropPartition();
            addPartition.setAdd(true);
            if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
               addPartition.setExists(true);
               this.lexer.nextToken();
               this.lexer.nextToken();
               this.lexer.nextToken();
            }

            do {
               addPartition.addPartition(this.parsePartition());
            } while(this.lexer.token() != Token.EOF && this.lexer.token() != Token.SEMI);

            stmt.setAddOrDropPartition(addPartition);
         } else {
            this.lexer.nextToken();
            stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.ADD_COLUMNS);
            SparkAlterTableStatement.SparkAlterTableAddColumns addColumns = new SparkAlterTableStatement.SparkAlterTableAddColumns();
            List<SparkColumnSpecExpr> cols = this.parseColumns();
            addColumns.setColumns(cols);
            stmt.setAddColumns(addColumns);
         }
      } else if (this.lexer.token() != Token.DROP && !this.lexer.identifierEquals("DROP")) {
         if (this.lexer.token() != Token.ALTER && !this.lexer.identifierEquals("ALTER") && !this.lexer.identifierEquals("CHANGE")) {
            if (this.lexer.token() != Token.REPLACE && !this.lexer.identifierEquals("REPLACE")) {
               if (this.lexer.token() != Token.SET && !this.lexer.identifierEquals("SET") && !this.lexer.identifierEquals("UNSET")) {
                  if (this.lexer.identifierEquals("RECOVER")) {
                     stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.RECOVER_PARTITIONS);
                     this.lexer.nextToken();
                     this.lexer.nextToken();
                  }
               } else {
                  stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.SET_OR_UNSET_PROPS);
                  SparkAlterTableStatement.SparkAlterTableSetUnsetProps setProps = new SparkAlterTableStatement.SparkAlterTableSetUnsetProps();
                  if (this.lexer.identifierEquals("UNSET")) {
                     setProps.setUnset(true);
                  }

                  this.lexer.nextToken();
                  if (this.lexer.identifierEquals("FILEFORMAT")) {
                     this.lexer.nextToken();
                     setProps.setFileFormat(this.exprParser.expr());
                  }

                  if (this.lexer.identifierEquals("LOCATION")) {
                     this.lexer.nextToken();
                     setProps.setLocation(this.exprParser.expr());
                  }

                  if (this.lexer.identifierEquals("SERDE")) {
                     this.lexer.nextToken();
                     setProps.setSerde(this.exprParser.expr());
                     if (this.lexer.token() == Token.WITH || this.lexer.identifierEquals("WITH")) {
                        this.lexer.nextToken();
                     }
                  }

                  if (this.lexer.identifierEquals("TBLPROPERTIES") || this.lexer.identifierEquals("SERDEPROPERTIES")) {
                     setProps.setTblProps(this.lexer.identifierEquals("TBLPROPERTIES"));
                     this.lexer.nextToken();
                     if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
                        setProps.setExists(true);
                        this.lexer.nextToken();
                     }

                     List<SparkDefProperty> props = this.parseProperties();
                     setProps.setProps(props);
                  }

                  stmt.setSetUnsetProps(setProps);
               }
            } else {
               this.lexer.nextToken();
               this.lexer.nextToken();
               stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.REPLACE_COLUMNS);
               SparkAlterTableStatement.SparkAlterTableReplaceColumns replaceColumns = new SparkAlterTableStatement.SparkAlterTableReplaceColumns();
               replaceColumns.setColumns(this.parseColumns());
               stmt.setReplaceColumns(replaceColumns);
            }
         } else {
            this.lexer.nextToken();
            stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.ALTER_CHANGE_COLUMN);
            this.lexer.nextToken();
            SparkAlterTableStatement.SparkAlterTableChangeColumn changeColumn = new SparkAlterTableStatement.SparkAlterTableChangeColumn();
            changeColumn.setColumn(this.exprParser.expr());
            SparkColumnSpecExpr spec = new SparkColumnSpecExpr();
            spec.setName(this.exprParser.expr());
            if (this.lexer.token() != Token.COMMENT && this.lexer.token() != Token.EOF) {
               spec.setType(this.exprParser.expr());
            }

            if (this.lexer.token() == Token.COMMENT) {
               this.lexer.nextToken();
               spec.setComment(this.exprParser.expr());
            }

            changeColumn.setSpec(spec);
            stmt.setChangeColumn(changeColumn);
         }
      } else {
         this.lexer.nextToken();
         if (this.lexer.token() != Token.COLUMNS && !this.lexer.identifierEquals("COLUMNS") && this.lexer.token() != Token.COLUMN && !this.lexer.identifierEquals("COLUMN")) {
            stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.ADD_OR_DROP_PARTITION);
            SparkAlterTableStatement.SparkAlterTableAddOrDropPartition dropPartition = new SparkAlterTableStatement.SparkAlterTableAddOrDropPartition();
            if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
               dropPartition.setExists(true);
               this.lexer.nextToken();
               this.lexer.nextToken();
            }

            do {
               dropPartition.addPartition(this.parsePartition());
            } while(this.lexer.token() != Token.PARTITION && this.lexer.token() != Token.EOF && this.lexer.token() != Token.SEMI && !this.lexer.identifierEquals("PURGE"));

            if (this.lexer.identifierEquals("PURGE")) {
               dropPartition.setPurge(true);
            }

            stmt.setAddOrDropPartition(dropPartition);
         } else {
            this.lexer.nextToken();
            stmt.setAlterTableType(SparkAlterTableStatement.AlterTableType.DROP_COLUMNS);
            SparkAlterTableStatement.SparkAlterTableDropColumns dropColumns = new SparkAlterTableStatement.SparkAlterTableDropColumns();
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
            }

            new ArrayList();

            do {
               dropColumns.addColumn(this.exprParser.expr());
               this.lexer.nextToken();
            } while(this.lexer.token() != Token.RPAREN && this.lexer.token() != Token.EOF && this.lexer.token() != Token.SEMI);

            if (this.lexer.token() == Token.RPAREN || this.lexer.token() == Token.SEMI) {
               this.lexer.nextToken();
            }

            stmt.setDropColumns(dropColumns);
         }
      }

      return stmt;
   }

   public SQLStatement parseAlterView() {
      this.accept(Token.VIEW);
      SparkAlterViewStatement view = new SparkAlterViewStatement();
      view.setName(this.exprParser.expr());
      if (this.lexer.identifierEquals("RENAME")) {
         this.lexer.nextToken();
         this.accept(Token.TO);
         SparkAlterViewStatement.SparkAlterViewReNameTo rename = new SparkAlterViewStatement.SparkAlterViewReNameTo();
         rename.setName(this.exprParser.expr());
         view.setRename(rename);
      } else if (this.lexer.token() != Token.SET && !this.lexer.identifierEquals("SET") && !this.lexer.identifierEquals("UNSET")) {
         if (this.lexer.token() == Token.AS || this.lexer.identifierEquals("AS")) {
            this.lexer.nextToken();
            SQLSelect select = this.createSQLSelectParser().select();
            view.setSelect(select);
         }
      } else {
         SparkAlterViewStatement.SparkAlterViewSetUnsetProps set = new SparkAlterViewStatement.SparkAlterViewSetUnsetProps();
         set.setUnset(this.lexer.identifierEquals("UNSET"));
         this.lexer.nextToken();
         this.acceptIdentifier("TBLPROPERTIES");
         if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
            set.setExists(true);
            this.lexer.nextToken();
         }

         set.setProps(this.parseProperties());
         view.setSetProps(set);
      }

      return view;
   }

   public List<SparkDefProperty> parseProperties() {
      List<SparkDefProperty> props = new ArrayList();

      while(this.lexer.token() != Token.RPAREN) {
         this.lexer.nextToken();
         SQLExpr value = null;
         SQLExpr key = this.exprParser.expr();
         if (key instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr b = (SQLBinaryOpExpr)key;
            value = b.getRight();
            key = b.getLeft();
         } else if (this.lexer.token() == Token.EQ || this.lexer.identifierEquals("EQ") || this.lexer.token() != Token.COMMA && this.lexer.token() != Token.RPAREN) {
            if (this.lexer.token() == Token.EQ || this.lexer.identifierEquals("EQ")) {
               this.lexer.nextToken();
            }

            value = this.exprParser.expr();
         }

         SparkDefProperty prop = new SparkDefProperty(key, value);
         props.add(prop);
      }

      if (this.lexer.token() == Token.RPAREN) {
         this.lexer.nextToken();
      }

      return props;
   }

   private SparkSelectTableReference parseTable() {
      SparkSelectTableReference ref = new SparkSelectTableReference();
      SQLName tableName = this.exprParser.name();
      ref.setExpr(tableName);
      if (this.lexer.token() == Token.PARTITION) {
         ref.setPartition(this.parsePartition());
      }

      return ref;
   }

   private SparkTablePartition parsePartition() {
      this.accept(Token.PARTITION);

      SparkTablePartition partition;
      SQLExpr col;
      SQLExpr value;
      for(partition = new SparkTablePartition(); this.lexer.token() != Token.RPAREN && this.lexer.token() != Token.EOF; partition.addDef(col, value)) {
         this.lexer.nextToken();
         value = null;
         col = this.exprParser.expr();
         if (col instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr b = (SQLBinaryOpExpr)col;
            value = b.getRight();
            col = b.getLeft();
         } else {
            this.accept(Token.EQ);
            value = this.exprParser.expr();
         }
      }

      if (this.lexer.token() == Token.RPAREN) {
         this.lexer.nextToken();
      }

      return partition;
   }

   private List<SparkColumnSpecExpr> parseColumns() {
      List<SparkColumnSpecExpr> cols = new ArrayList();
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
      }

      do {
         SparkColumnSpecExpr expr = new SparkColumnSpecExpr();
         SQLExpr col = this.exprParser.expr();
         expr.setName(col);
         if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.COMMENT && this.lexer.token() != Token.RPAREN) {
            SQLExpr type = this.exprParser.expr();
            expr.setType(type);
         }

         if (this.lexer.token() == Token.COMMENT) {
            this.lexer.nextToken();
            SQLExpr comment = this.exprParser.expr();
            expr.setComment(comment);
         }

         if (this.lexer.token() != Token.RPAREN && this.lexer.token() != Token.EOF && this.lexer.token() != Token.COMMA && this.lexer.token() != Token.SEMI) {
            SQLExpr position = this.exprParser.expr();
            expr.setPosition(position);
         }

         cols.add(expr);
         if (this.lexer.token() == Token.COMMA) {
            this.lexer.nextToken();
         }
      } while(this.lexer.token() != Token.RPAREN && this.lexer.token() != Token.EOF && this.lexer.token() != Token.SEMI);

      if (this.lexer.token() == Token.RPAREN) {
         this.lexer.nextToken();
      }

      return cols;
   }

   public SQLStatement parseCreate() {
      char markChar = this.lexer.current();
      int markBp = this.lexer.bp();
      List<String> comments = null;
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         comments = this.lexer.readAndResetComments();
      }

      this.accept(Token.CREATE);
      boolean global = false;
      if (this.lexer.identifierEquals(FnvHash.Constants.GLOBAL)) {
         this.lexer.nextToken();
         global = true;
      }

      boolean temporary = false;
      if (this.lexer.identifierEquals(FnvHash.Constants.TEMPORARY) || this.lexer.token() == Token.TEMPORARY) {
         this.lexer.nextToken();
         temporary = true;
      }

      boolean nonclustered = false;
      if (this.lexer.identifierEquals(FnvHash.Constants.NONCLUSTERED)) {
         this.lexer.nextToken();
         nonclustered = true;
      }

      boolean external = false;
      if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
         this.lexer.nextToken();
         external = true;
      }

      Token token = this.lexer.token();
      switch (this.lexer.token()) {
         case TABLE:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            SQLCreateTableParser createTableParser = this.getSQLCreateTableParser();
            SQLCreateTableStatement stmt = createTableParser.parseCreateTable();
            if (temporary) {
               if (global) {
                  stmt.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY);
               } else {
                  stmt.setType(SQLCreateTableStatement.Type.TEMPORARY);
               }
            }

            if (comments != null) {
               stmt.addBeforeComment(comments);
            }

            return stmt;
         case INDEX:
         case NOT:
         case UNIQUE:
            SQLCreateIndexStatement createIndex = this.parseCreateIndex(false);
            if (nonclustered) {
               createIndex.setType("NONCLUSTERED");
            }

            return createIndex;
         case SEQUENCE:
            return this.parseCreateSequence(false);
         case DATABASE:
         case SCHEMA:
            SQLStatement stmt2 = this.parseCreateDatabase();
            if (comments != null) {
               stmt2.addBeforeComment(comments);
               List<String> var11 = null;
            }

            return stmt2;
         case USER:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            return this.parseCreateUser();
         case FUNCTION:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            SQLStatement createFunct = this.parseCreateFunction();
            return createFunct;
         case TABLESPACE:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            return this.parseCreateTableSpace();
         default:
            if (token == Token.OR) {
               this.lexer.nextToken();
               this.accept(Token.REPLACE);
               if (this.lexer.identifierEquals(FnvHash.Constants.GLOBAL)) {
                  this.lexer.nextToken();
                  global = true;
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.TEMPORARY) || this.lexer.token() == Token.TEMPORARY) {
                  this.lexer.nextToken();
                  temporary = true;
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.FORCE)) {
                  this.lexer.nextToken();
               }

               if (this.lexer.token() == Token.PROCEDURE) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateProcedure();
               } else if (this.lexer.token() == Token.VIEW) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateView();
               } else if (this.lexer.token() == Token.TRIGGER) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateTrigger();
               } else if (this.lexer.token() == Token.FUNCTION) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateFunction();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.PACKAGE)) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreatePackage();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateType();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.PUBLIC)) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateSynonym();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.SYNONYM)) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateSynonym();
               } else if (this.lexer.token() != Token.INDEX && !this.lexer.identifierEquals("CLUSTER") && this.lexer.token() != Token.NOT && this.lexer.token() != Token.UNIQUE && !this.lexer.identifierEquals("SPATIAL") && !this.lexer.identifierEquals("BITMAP")) {
                  if (this.lexer.identifierEquals("CONTEXT")) {
                     this.lexer.reset(markBp, markChar, Token.CREATE);
                     SQLCreateContextStatement contextStatement = this.parseCreateContext();
                     return contextStatement;
                  } else if (this.lexer.identifierEquals("DIRECTORY")) {
                     this.lexer.reset(markBp, markChar, Token.CREATE);
                     SQLCreateDirectoryStatement createDirectory = this.parseCreateDirectory();
                     return createDirectory;
                  } else {
                     throw new ParserException("TODO " + this.lexer.info());
                  }
               } else {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  SQLCreateIndexStatement createIndex3 = this.parseCreateIndex(true);
                  if (nonclustered) {
                     createIndex3.setType("NONCLUSTERED");
                  }

                  return createIndex3;
               }
            } else if (this.lexer.identifierEquals(FnvHash.Constants.PUBLIC)) {
               this.lexer.nextToken();
               if (this.lexer.identifierEquals("SYNONYM")) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateSynonym();
               } else {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreateDbLink();
               }
            } else if (this.lexer.identifierEquals("SHARE")) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateDbLink();
            } else if (this.lexer.identifierEquals("SYNONYM")) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateSynonym();
            } else if (token == Token.VIEW) {
               return this.parseCreateView();
            } else if (token == Token.TRIGGER) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateTrigger();
            } else if (token == Token.PROCEDURE) {
               SQLCreateProcedureStatement stmt4 = this.parseCreateProcedure();
               stmt4.setCreate(true);
               return stmt4;
            } else if (this.lexer.identifierEquals(FnvHash.Constants.BITMAP)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateIndex(true);
            } else if (this.lexer.identifierEquals(FnvHash.Constants.MATERIALIZED)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateMaterializedView();
            } else if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateType();
            } else if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               SQLCreateTableStatement createTable = this.parseCreateTable();
               if (comments != null) {
                  createTable.addBeforeComment(comments);
                  List<String> var12 = null;
               }

               return createTable;
            } else if (this.lexer.identifierEquals(FnvHash.Constants.TABLEGROUP)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateTableGroup();
            } else if (this.lexer.identifierEquals(FnvHash.Constants.DIMENSION)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateTable();
            } else if (this.lexer.identifierEquals(FnvHash.Constants.ROLE)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateRole();
            } else if (this.lexer.identifierEquals(FnvHash.Constants.RESOURCE)) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateResourceGroup();
            } else if (this.lexer.token() == Token.FOREIGN) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateTable();
            } else if (!this.lexer.identifierEquals("CLUSTER") && !this.lexer.identifierEquals("SPATIAL") && !this.lexer.identifierEquals("BITMAP") && !this.lexer.identifierEquals("CONTEXT")) {
               if (this.lexer.identifierEquals("DOMAIN")) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  SQLCreateDomainStatement createDomain = this.parseCreateDomain();
                  return createDomain;
               } else if (this.lexer.identifierEquals("DIRECTORY")) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  SQLCreateDirectoryStatement createDirectory = this.parseCreateDirectory();
                  return createDirectory;
               } else if (this.lexer.identifierEquals(FnvHash.Constants.PACKAGE)) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  return this.parseCreatePackage();
               } else {
                  throw new ParserException("TODO " + this.lexer.info());
               }
            } else {
               this.lexer.nextToken();
               this.lexer.nextToken();
               if (this.lexer.token() == Token.USING) {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  SQLCreateContextStatement contextStatement = this.parseCreateContext();
                  return contextStatement;
               } else {
                  this.lexer.reset(markBp, markChar, Token.CREATE);
                  SQLCreateIndexStatement createIndex5 = this.parseCreateIndex(true);
                  if (nonclustered) {
                     createIndex5.setType("NONCLUSTERED");
                  }

                  return createIndex5;
               }
            }
      }
   }

   public SQLCreateViewStatement parseCreateView() {
      SparkCreateViewStatement createView = new SparkCreateViewStatement();
      if (this.lexer.token() == Token.CREATE) {
         this.lexer.nextToken();
      }

      if (this.lexer.token() == Token.OR) {
         this.lexer.nextToken();
         this.accept(Token.REPLACE);
         createView.setOrReplace(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.GLOBAL)) {
         this.lexer.nextToken();
         createView.setGlobal(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.TEMPORARY) || this.lexer.token() == Token.TEMPORARY) {
         this.lexer.nextToken();
         createView.setTemporary(true);
      }

      if (this.lexer.identifierEquals("ALGORITHM")) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         String algorithm = this.lexer.stringVal();
         createView.setAlgorithm(algorithm);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         SQLName definer = ((MySqlExprParser)this.exprParser).userName();
         createView.setDefiner(definer);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SQL)) {
         this.lexer.nextToken();
         this.acceptIdentifier("SECURITY");
         String sqlSecurity = this.lexer.stringVal();
         createView.setSqlSecurity(sqlSecurity);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.FORCE)) {
         this.lexer.nextToken();
         createView.setForce(true);
      }

      this.accept(Token.VIEW);
      if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
         this.lexer.nextToken();
         this.accept(Token.NOT);
         this.accept(Token.EXISTS);
         createView.setIfNotExists(true);
      }

      createView.setName(this.exprParser.name());
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();

         while(true) {
            if (this.lexer.token() == Token.CONSTRAINT) {
               SQLTableConstraint constraint = (SQLTableConstraint)this.exprParser.parseConstaint();
               createView.addColumn(constraint);
            } else {
               SQLColumnDefinition column = new SQLColumnDefinition();
               column.setDbType(this.dbType);
               SQLName expr = this.exprParser.name();
               column.setName(expr);
               if (this.dbType == DbType.odps && expr.getSimpleName().startsWith("@")) {
                  column.setDataType(this.exprParser.parseDataType());
               }

               this.exprParser.parseColumnRest(column);
               if (this.lexer.token() == Token.COMMENT) {
                  this.lexer.nextToken();
                  SQLExpr comment;
                  if (this.lexer.token() == Token.LITERAL_ALIAS) {
                     String alias = this.lexer.stringVal();
                     if (alias.length() > 2 && alias.charAt(0) == '"' && alias.charAt(alias.length() - 1) == '"') {
                        alias = alias.substring(1, alias.length() - 1);
                     }

                     comment = new SQLCharExpr(alias);
                     this.lexer.nextToken();
                  } else {
                     comment = this.exprParser.primary();
                  }

                  column.setComment(comment);
               }

               column.setParent(createView);
               createView.addColumn(column);
            }

            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.token() == Token.COMMENT) {
         this.lexer.nextToken();
         SQLCharExpr comment = (SQLCharExpr)this.exprParser.primary();
         createView.setComment(comment);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.TBLPROPERTIES)) {
         this.parseTblProperties(createView);
      }

      this.accept(Token.AS);
      SQLSelectParser selectParser = this.createSQLSelectParser();
      createView.setSubQuery(selectParser.select());
      if (this.lexer.token() == Token.WITH) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("CASCADED")) {
            createView.setWithCascaded(true);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("LOCAL")) {
            createView.setWithLocal(true);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("READ")) {
            this.lexer.nextToken();
            this.accept(Token.ONLY);
            createView.setWithReadOnly(true);
         }

         if (this.lexer.token() == Token.CHECK) {
            this.lexer.nextToken();
            this.acceptIdentifier("OPTION");
            createView.setWithCheckOption(true);
         }
      }

      return createView;
   }

   private void parseTblProperties(SparkCreateViewStatement stmt) {
      this.lexer.nextToken();
      this.accept(Token.LPAREN);

      do {
         String name = this.lexer.stringVal();
         this.lexer.nextToken();
         if (this.lexer.token() == Token.DOT) {
            this.lexer.nextToken();
            name = name + "." + this.lexer.stringVal();
            this.lexer.nextToken();
         }

         this.accept(Token.EQ);
         SQLExpr value = this.exprParser.primary();
         stmt.addTblProperty(name, value);
         if (this.lexer.token() != Token.COMMA) {
            break;
         }

         this.lexer.nextToken();
      } while(this.lexer.token() != Token.RPAREN);

      this.accept(Token.RPAREN);
   }

   public SQLStatement parseCreateDatabase() {
      SparkCreateDatabaseStatement stmt = new SparkCreateDatabaseStatement();
      stmt.setType(new SQLIdentifierExpr(this.lexer.stringVal()));
      this.lexer.nextToken();
      if (this.lexer.token() == Token.IF || this.lexer.identifierEquals("IF")) {
         stmt.setExists(true);
         this.lexer.nextToken();
         this.lexer.nextToken();
         this.lexer.nextToken();
      }

      stmt.setName(this.exprParser.expr());
      boolean flag = true;

      while(flag) {
         if (this.lexer.token() != Token.COMMENT && !this.lexer.identifierEquals("COMMENT")) {
            if (this.lexer.identifierEquals("LOCATION")) {
               this.lexer.nextToken();
               stmt.setLocation(this.exprParser.expr());
            } else if (this.lexer.token() != Token.WITH && !this.lexer.identifierEquals("WITH")) {
               flag = false;
            } else {
               this.lexer.nextToken();
               this.acceptIdentifier("DBPROPERTIES");
               List<SparkDefProperty> props = this.parseProperties();
               stmt.setProps(props);
            }
         } else {
            this.lexer.nextToken();
            stmt.setComment(this.exprParser.expr());
         }
      }

      return stmt;
   }

   public SQLCreateFunctionStatement parseCreateFunction() {
      return this.parseHiveCreateFunction();
   }

   public SQLCreateFunctionStatement parseHiveCreateFunction() {
      HiveCreateFunctionStatement stmt = new HiveCreateFunctionStatement();
      stmt.setDbType(this.dbType);
      this.accept(Token.CREATE);
      if (this.lexer.token() == Token.OR) {
         this.accept(Token.OR);
         this.accept(Token.REPLACE);
         stmt.setOrReplace(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.TEMPORARY)) {
         this.lexer.nextToken();
         stmt.setTemporary(true);
      }

      this.accept(Token.FUNCTION);
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      if (this.lexer.token() == Token.AS) {
         this.lexer.setToken(Token.IDENTIFIER);
         this.lexer.nextToken();
         SQLExpr className = this.exprParser.expr();
         stmt.setClassName(className);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.LOCATION)) {
         this.lexer.nextToken();
         SQLExpr location = this.exprParser.primary();
         stmt.setLocationn(location);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SYMBOL)) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         SQLExpr symbol = this.exprParser.primary();
         stmt.setSymbol(symbol);
      }

      if (this.lexer.token() == Token.USING || this.lexer.hash_lower() == FnvHash.Constants.USING) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals(FnvHash.Constants.JAR)) {
            this.lexer.nextToken();
            stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.JAR);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.ARCHIVE)) {
            this.lexer.nextToken();
            stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.ARCHIVE);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.FILE)) {
            this.lexer.nextToken();
            stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.FILE);
         }

         SQLExpr location = this.exprParser.primary();
         stmt.setLocationn(location);
      }

      return stmt;
   }

   public SparkCacheTableStatement parseSparkCacheTable() {
      SparkCacheTableStatement stmt = new SparkCacheTableStatement();
      this.lexer.nextToken();
      if (this.lexer.identifierEquals("LAZY")) {
         stmt.setLazy(true);
         this.lexer.nextToken();
      }

      stmt.setTable(this.exprParser.expr());
      if (this.lexer.identifierEquals("OPTIONS")) {
         this.lexer.nextToken();
         stmt.setOptions(this.parseProperties());
      }

      if (this.lexer.token() == Token.AS || this.lexer.identifierEquals("AS")) {
         this.lexer.nextToken();
      }

      if (this.lexer.token() != Token.EOF && this.lexer.token() != Token.SEMI) {
         SparkCacheTableStatement.SparkCacheTableStatementQuery query = new SparkCacheTableStatement.SparkCacheTableStatementQuery();
         if (this.lexer.token() == Token.FROM) {
            query.setFromQuery(this.exprParser.expr());
         } else if (this.lexer.token() == Token.TABLE) {
            query.setTableQuery(this.exprParser.expr());
         }

         query.setQuery(this.createSQLSelectParser().select());
         stmt.setQuery(query);
      }

      return stmt;
   }

   public SQLStatement parseRefresh() {
      if (this.lexer.identifierEquals("REFRESH")) {
         this.lexer.nextToken();
      }

      SparkRefreshStatement stmt = new SparkRefreshStatement();
      if (this.lexer.token() == Token.TABLE) {
         stmt.setType(SparkRefreshStatement.Type.TABLE);
         this.lexer.nextToken();
      } else if (this.lexer.token() == Token.FUNCTION) {
         stmt.setType(SparkRefreshStatement.Type.FUNCTION);
         this.lexer.nextToken();
      } else {
         stmt.setType(SparkRefreshStatement.Type.RESOURCE_PATH);
      }

      stmt.setName(this.exprParser.name());
      return stmt;
   }

   protected SQLStatement parseShowTable() {
      SparkShowTableStatement stmt = new SparkShowTableStatement();
      if (this.lexer.identifierEquals(FnvHash.Constants.SHOW)) {
         this.lexer.nextToken();
      }

      if (this.lexer.token() == Token.TABLE) {
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.EXTENDED)) {
         this.lexer.nextToken();
         stmt.setExtended(true);
      }

      if (this.lexer.token() == Token.FROM || this.lexer.token() == Token.IN) {
         this.lexer.nextToken();
         SQLName database = this.exprParser.name();
         stmt.setDatabase(database);
      }

      if (this.lexer.token() == Token.LIKE) {
         this.lexer.nextToken();
         SQLExpr like = this.exprParser.expr();
         stmt.setLike(like);
      }

      if (this.lexer.token() == Token.PARTITION) {
         SparkTablePartition partition = this.parsePartition();
         stmt.setPartition(partition);
      }

      return stmt;
   }
}
