content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.wasp.plan.parser.druid.dialect;
  
  import com.alibaba.druid.sql.ast.SQLCommentHint;
  import com.alibaba.druid.sql.ast.SQLExpr;
  import com.alibaba.druid.sql.ast.SQLName;
  import com.alibaba.druid.sql.ast.SQLOrderBy;
  import com.alibaba.druid.sql.ast.SQLStatement;
  import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
  import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr;
  import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddPrimaryKey;
  import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropColumnItem;
  import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropIndex;
  import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
  import com.alibaba.druid.sql.ast.statement.SQLCreateDatabaseStatement;
  import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
  import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement;
  import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
  import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey;
  import com.alibaba.druid.sql.ast.statement.SQLSelect;
  import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
  import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
  import com.alibaba.druid.sql.ast.statement.SQLTableSource;
  import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
  import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
  import com.alibaba.druid.sql.parser.Lexer;
  import com.alibaba.druid.sql.parser.ParserException;
  import com.alibaba.druid.sql.parser.SQLSelectParser;
  import com.alibaba.druid.sql.parser.SQLStatementParser;
  import com.alibaba.druid.sql.parser.Token;
  import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlLockTableStatement.LockType;
  
  import java.util.List;
  
  /**
   * 
   *
   */
  public class WaspSqlStatementParser extends SQLStatementParser {
    private static final String COLLATE2 = "COLLATE";
    private static final String CASCADE = "CASCADE";
    private static final String RESTRICT = "RESTRICT";
    private static final String CHARACTER = "CHARACTER";
    private static final String SESSION = "SESSION";
    private static final String GLOBAL = "GLOBAL";
    private static final String VARIABLES = "VARIABLES";
    private static final String ERRORS = "ERRORS";
    private static final String STATUS = "STATUS";
    private static final String IGNORE = "IGNORE";
    private static final String DESCRIBE = "DESCRIBE";
    private static final String DESC = "DESC";
    private static final String WRITE = "WRITE";
    private static final String READ = "READ";
    private static final String LOCAL = "LOCAL";
    private static final String TABLES = "TABLES";
    private static final String TEMPORARY = "TEMPORARY";
    private static final String USER = "USER";
    private static final String SPATIAL = "SPATIAL";
    private static final String FULLTEXT = "FULLTEXT";
    private static final String REPLACE = "REPLACE";
    private static final String DELAYED = "DELAYED";
    private static final String LOW_PRIORITY = "LOW_PRIORITY";
  
    /**
     * @param lexer
     */
    public WaspSqlStatementParser(Lexer lexer) {
      super(new WaspSqlExprParser(lexer));
    }
  
    public WaspSqlStatementParser(String sql) {
      super(new WaspSqlExprParser(sql));
    }
  
    public SQLStatement parseCreate() {
      accept(Token.CREATE);
      List<SQLCommentHint> hints = this.exprParser.parseHints();
      if (lexer.token() == Token.TABLE || identifierEquals(TEMPORARY)) {
        WaspSqlCreateTableParser parser = new WaspSqlCreateTableParser(
            this.exprParser);
        WaspSqlCreateTableStatement stmt = parser.parseCrateTable(false);
        stmt.setHints(hints);
        return stmt;
      }
      if (lexer.token() == Token.DATABASE) {
        return parseCreateDatabase();
      }
      if (lexer.token() == Token.UNIQUE || lexer.token() == Token.INDEX
         // || identifierEquals(FULLTEXT) || identifierEquals(SPATIAL)
         ) {
        return parseCreateIndex();
      }
  
      if (identifierEquals(USER)) {
        return parseCreateUser();
      }
      throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
    }
  
    public SQLStatement parseCreateIndex()  {
      WaspSqlCreateIndexStatement stmt = new WaspSqlCreateIndexStatement();
      if (lexer.token() == Token.UNIQUE) {
        stmt.setType("UNIQUE");
        lexer.nextToken();
      } else if (identifierEquals(FULLTEXT)) {
        stmt.setType(FULLTEXT);
        lexer.nextToken();
      } else if (identifierEquals(SPATIAL)) {
        stmt.setType(SPATIAL);
        lexer.nextToken();
      }
      accept(Token.INDEX);
      stmt.setName(this.exprParser.name());
      parseCreateIndexUsing(stmt);
      accept(Token.ON);
      stmt.setTable(this.exprParser.name());
      accept(Token.LPAREN);
      for (;;) {
        SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
        stmt.getItems().add(item);
        if (lexer.token() == Token.COMMA) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      accept(Token.RPAREN);
      parseCreateIndexUsing(stmt);
      return stmt;
    }
  
    private void parseCreateIndexUsing(WaspSqlCreateIndexStatement stmt) {
      if (identifierEquals("USING")) {
        lexer.nextToken();
        if (identifierEquals("BTREE")) {
          stmt.setUsing("BTREE");
          lexer.nextToken();
        } else if (identifierEquals("HASH")) {
          stmt.setUsing("HASH");
          lexer.nextToken();
        } else {
          throw new ParserException("TODO " + lexer.token() + " "
              + lexer.stringVal());
        }
      } else if (identifierEquals("STORING")) {
        lexer.nextToken();
        accept(Token.LPAREN);
        for (;;) {
          stmt.getStoringCols().add(this.exprParser.expr());
          if (!(lexer.token() == (Token.COMMA))) {
            break;
          } else {
            lexer.nextToken();
          }
        }
        accept(Token.RPAREN);
      }
    }
  
    public SQLCreateTableStatement parseCreateTable() {
      WaspSqlCreateTableParser parser = new WaspSqlCreateTableParser(
          this.exprParser);
      return parser.parseCrateTable(false);
    }
  
    public SQLSelectStatement parseSelect() {
      return new SQLSelectStatement(
          new WaspSqlSelectParser(this.exprParser).select());
    }
  
    public SQLUpdateStatement parseUpdateStatement() {
      WaspSqlUpdateStatement stmt = createUpdateStatement();
      if (lexer.token() == Token.UPDATE) {
        lexer.nextToken();
        if (identifierEquals(LOW_PRIORITY)) {
          lexer.nextToken();
          stmt.setLowPriority(true);
        }
        if (identifierEquals(IGNORE)) {
          lexer.nextToken();
          stmt.setIgnore(true);
        }
        SQLTableSource tableSource = this.exprParser.createSelectParser()
            .parseTableSource();
        stmt.setTableSource(tableSource);
      }
      accept(Token.SET);
      for (;;) {
        SQLUpdateSetItem item = new SQLUpdateSetItem();
        item.setColumn(this.exprParser.name());
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        } else {
          accept(Token.COLONEQ);
        }
        item.setValue(this.exprParser.expr());
        stmt.getItems().add(item);
        if (lexer.token() == (Token.COMMA)) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      if (lexer.token() == (Token.WHERE)) {
        lexer.nextToken();
        stmt.setWhere(this.exprParser.expr());
      }
      stmt.setOrderBy(this.exprParser.parseOrderBy());
      stmt.setLimit(parseLimit());
      return stmt;
    }
  
    protected WaspSqlUpdateStatement createUpdateStatement() {
      return new WaspSqlUpdateStatement();
    }
  
    public WaspSqlDeleteStatement parseDeleteStatement() {
      WaspSqlDeleteStatement deleteStatement = new WaspSqlDeleteStatement();
      if (lexer.token() == Token.DELETE) {
        lexer.nextToken();
        if (identifierEquals(LOW_PRIORITY)) {
          deleteStatement.setLowPriority(true);
          lexer.nextToken();
        }
        if (identifierEquals("QUICK")) {
          deleteStatement.setQuick(true);
          lexer.nextToken();
        }
        if (identifierEquals(IGNORE)) {
          deleteStatement.setIgnore(true);
          lexer.nextToken();
        }
        if (lexer.token() == Token.IDENTIFIER) {
          deleteStatement.setTableSource(createSQLSelectParser()
              .parseTableSource());
          if (lexer.token() == Token.FROM) {
            lexer.nextToken();
            SQLTableSource tableSource = createSQLSelectParser()
                .parseTableSource();
            deleteStatement.setFrom(tableSource);
          }
        } else {
          if (lexer.token() == Token.FROM) {
            lexer.nextToken();
            deleteStatement.setTableSource(createSQLSelectParser()
                .parseTableSource());
          }
        }
        if (identifierEquals("USING")) {
          lexer.nextToken();
          SQLTableSource tableSource = createSQLSelectParser().parseTableSource();
          deleteStatement.setUsing(tableSource);
        }
      }
  
      if (lexer.token() == (Token.WHERE)) {
        lexer.nextToken();
        SQLExpr where = this.exprParser.expr();
        deleteStatement.setWhere(where);
      }
      if (lexer.token() == (Token.ORDER)) {
        SQLOrderBy orderBy = exprParser.parseOrderBy();
        deleteStatement.setOrderBy(orderBy);
      }
      deleteStatement.setLimit(parseLimit());
      return deleteStatement;
    }
  
    public SQLStatement parseCreateUser() {
      if (lexer.token() == Token.CREATE) {
        lexer.nextToken();
      }
      acceptIdentifier(USER);
      WaspSqlCreateUserStatement stmt = new WaspSqlCreateUserStatement();
      for (;;) {
        WaspSqlCreateUserStatement.UserSpecification userSpec = new WaspSqlCreateUserStatement.UserSpecification();
        SQLExpr expr = exprParser.primary();
        userSpec.setUser(expr);
        if (lexer.token() == Token.IDENTIFIED) {
          lexer.nextToken();
          if (lexer.token() == Token.BY) {
            lexer.nextToken();
            if (identifierEquals("PASSWORD")) {
              lexer.nextToken();
            }
            SQLCharExpr password = (SQLCharExpr) this.exprParser.expr();
            userSpec.setPassword(password);
          } else if (lexer.token() == Token.WITH) {
            lexer.nextToken();
            SQLCharExpr text = (SQLCharExpr) this.exprParser.expr();
            userSpec.setAuthPlugin(text);
          }
        }
        stmt.getUsers().add(userSpec);
        if (lexer.token() == Token.COMMA) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      return stmt;
    }
  
    public SQLStatement parseKill() {
      accept(Token.KILL);
      WaspSqlKillStatement stmt = new WaspSqlKillStatement();
      if (identifierEquals("CONNECTION")) {
        stmt.setType(WaspSqlKillStatement.Type.CONNECTION);
        lexer.nextToken();
      } else if (identifierEquals("QUERY")) {
        stmt.setType(WaspSqlKillStatement.Type.QUERY);
        lexer.nextToken();
      } else {
        throw new ParserException("not support kill type " + lexer.token());
      }
      SQLExpr threadId = this.exprParser.expr();
      stmt.setThreadId(threadId);
      return stmt;
    }
  
    public boolean parseStatementListDialect(List<SQLStatement> statementList) {
      if (lexer.token() == Token.KILL) {
        SQLStatement stmt = parseKill();
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("PREPARE")) {
        WaspSqlPrepareStatement stmt = parsePrepare();
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("EXECUTE")) {
        WaspSqlExecuteStatement stmt = parseExecute();
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("LOAD")) {
        SQLStatement stmt = parseLoad();
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("START")) {
        WaspSqlStartTransactionStatement stmt = parseStart();
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("SHOW")) {
        SQLStatement stmt = parseShow();
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("HELP")) {
        lexer.nextToken();
        WaspSqlHelpStatement stmt = new WaspSqlHelpStatement();
        stmt.setContent(this.exprParser.primary());
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals(DESC) || identifierEquals(DESCRIBE)) {
        SQLStatement stmt = parseDescribe();
        statementList.add(stmt);
        return true;
      }
  
      if (lexer.token() == Token.LOCK) {
        lexer.nextToken();
        acceptIdentifier(TABLES);
  
        WaspSqlLockTableStatement stmt = new WaspSqlLockTableStatement();
        stmt.setTableSource(this.exprParser.name());
  
        if (identifierEquals(READ)) {
          lexer.nextToken();
          if (identifierEquals(LOCAL)) {
            lexer.nextToken();
            stmt.setLockType(LockType.READ_LOCAL);
          } else {
            stmt.setLockType(LockType.READ);
          }
        } else if (identifierEquals(WRITE)) {
          stmt.setLockType(LockType.WRITE);
        } else if (identifierEquals(LOW_PRIORITY)) {
          lexer.nextToken();
          acceptIdentifier(WRITE);
          stmt.setLockType(LockType.LOW_PRIORITY_WRITE);
        }
  
        statementList.add(stmt);
        return true;
      }
  
      if (identifierEquals("UNLOCK")) {
        lexer.nextToken();
        acceptIdentifier(TABLES);
        statementList.add(new WaspSqlUnlockTablesStatement());
        return true;
      }
  
      return false;
    }
  
    public WaspSqlDescribeStatement parseDescribe() {
      if (lexer.token() == Token.DESC || identifierEquals(DESCRIBE)) {
        lexer.nextToken();
      } else {
        throw new ParserException("expect DESC, actual " + lexer.token());
      }
  
      WaspSqlDescribeStatement stmt = new WaspSqlDescribeStatement();
      stmt.setObject(this.exprParser.name());
  
      return stmt;
    }
  
    public SQLStatement parseShow() {
      acceptIdentifier("SHOW");
      if (lexer.token() == Token.FULL) {
        lexer.nextToken();
        if (identifierEquals("PROCESSLIST")) {
          lexer.nextToken();
          WaspSqlShowProcessListStatement stmt = new WaspSqlShowProcessListStatement();
          stmt.setFull(true);
          return stmt;
        }
        acceptIdentifier("COLUMNS");
        WaspSqlShowColumnsStatement stmt = parseShowColumns();
        stmt.setFull(true);
        return stmt;
      }
      if (identifierEquals("COLUMNS")) {
        lexer.nextToken();
        WaspSqlShowColumnsStatement stmt = parseShowColumns();
        return stmt;
      }
      if (identifierEquals(TABLES)) {
        lexer.nextToken();
        WaspSqlShowTablesStatement stmt = parseShowTabless();
        return stmt;
      }
      if (identifierEquals("DATABASES")) {
        lexer.nextToken();
        WaspSqlShowDatabasesStatement stmt = parseShowDatabases();
        return stmt;
      }
      if (identifierEquals("WARNINGS")) {
        lexer.nextToken();
        WaspSqlShowWarningsStatement stmt = parseShowWarnings();
        return stmt;
      }
      if (identifierEquals("COUNT")) {
        lexer.nextToken();
        accept(Token.LPAREN);
        accept(Token.STAR);
        accept(Token.RPAREN);
        if (identifierEquals(ERRORS)) {
          lexer.nextToken();
          WaspSqlShowErrorsStatement stmt = new WaspSqlShowErrorsStatement();
          stmt.setCount(true);
          return stmt;
        } else {
          acceptIdentifier("WARNINGS");
          WaspSqlShowWarningsStatement stmt = new WaspSqlShowWarningsStatement();
          stmt.setCount(true);
          return stmt;
        }
      }
      if (identifierEquals(ERRORS)) {
        lexer.nextToken();
        WaspSqlShowErrorsStatement stmt = new WaspSqlShowErrorsStatement();
        stmt.setLimit(parseLimit());
        return stmt;
      }
      if (identifierEquals(STATUS)) {
        lexer.nextToken();
        WaspSqlShowStatusStatement stmt = parseShowStatus();
        return stmt;
      }
      if (identifierEquals(VARIABLES)) {
        lexer.nextToken();
        WaspSqlShowVariantsStatement stmt = parseShowVariants();
        return stmt;
      }
      if (identifierEquals(GLOBAL)) {
        lexer.nextToken();
        if (identifierEquals(STATUS)) {
          lexer.nextToken();
          WaspSqlShowStatusStatement stmt = parseShowStatus();
          stmt.setGlobal(true);
          return stmt;
        }
        if (identifierEquals(VARIABLES)) {
          lexer.nextToken();
          WaspSqlShowVariantsStatement stmt = parseShowVariants();
          stmt.setGlobal(true);
          return stmt;
        }
      }
      if (identifierEquals(SESSION)) {
        lexer.nextToken();
        if (identifierEquals(STATUS)) {
          lexer.nextToken();
          WaspSqlShowStatusStatement stmt = parseShowStatus();
          stmt.setSession(true);
          return stmt;
        }
        if (identifierEquals(VARIABLES)) {
          lexer.nextToken();
          WaspSqlShowVariantsStatement stmt = parseShowVariants();
          stmt.setSession(true);
          return stmt;
        }
      }
      if (identifierEquals(CHARACTER)) {
        lexer.nextToken();
        accept(Token.SET);
        WaspSqlShowCharacterSetStatement stmt = new WaspSqlShowCharacterSetStatement();
        if (lexer.token() == Token.LIKE) {
          lexer.nextToken();
          stmt.setPattern(this.exprParser.expr());
        }
        if (lexer.token() == Token.WHERE) {
          lexer.nextToken();
          stmt.setWhere(this.exprParser.expr());
        }
        return stmt;
      }
      if (lexer.token() == Token.CREATE) {
        lexer.nextToken();
        if (lexer.token() == Token.DATABASE) {
          lexer.nextToken();
          WaspSqlShowCreateDatabaseStatement stmt = new WaspSqlShowCreateDatabaseStatement();
          stmt.setDatabase(this.exprParser.name());
          return stmt;
        }
        if (lexer.token() == Token.TABLE) {
          lexer.nextToken();
          WaspSqlShowCreateTableStatement stmt = new WaspSqlShowCreateTableStatement();
          stmt.setName(this.exprParser.name());
          return stmt;
        }
        throw new ParserException("TODO " + lexer.stringVal());
      }
      if (identifierEquals("GRANTS")) {
        lexer.nextToken();
        WaspSqlShowGrantsStatement stmt = new WaspSqlShowGrantsStatement();
        if (lexer.token() == Token.FOR) {
          lexer.nextToken();
          stmt.setUser(this.exprParser.expr());
        }
        return stmt;
      }
      if (lexer.token() == Token.INDEX || identifierEquals("INDEXES")) {
        lexer.nextToken();
        WaspSqlShowIndexesStatement stmt = new WaspSqlShowIndexesStatement();
        if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
          lexer.nextToken();
          SQLName table = exprParser.name();
          stmt.setTable(table);
          if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
            lexer.nextToken();
            SQLName database = exprParser.name();
            stmt.setDatabase(database);
          }
        }
        return stmt;
      }
      if (identifierEquals("OPEN")) {
        lexer.nextToken();
        acceptIdentifier(TABLES);
        WaspSqlShowOpenTablesStatement stmt = new WaspSqlShowOpenTablesStatement();
        if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
          lexer.nextToken();
          stmt.setDatabase(this.exprParser.name());
        }
        if (lexer.token() == Token.LIKE) {
          lexer.nextToken();
          stmt.setLike(this.exprParser.expr());
        }
        if (lexer.token() == Token.WHERE) {
          lexer.nextToken();
          stmt.setWhere(this.exprParser.expr());
        }
        return stmt;
      }
      if (identifierEquals("PROFILES")) {
        lexer.nextToken();
        WaspSqlShowProfilesStatement stmt = new WaspSqlShowProfilesStatement();
        return stmt;
      }
  
      if (identifierEquals("PROFILE")) {
        lexer.nextToken();
        WaspSqlShowProfileStatement stmt = new WaspSqlShowProfileStatement();
        for (;;) {
          if (lexer.token() == Token.ALL) {
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.ALL);
            lexer.nextToken();
          } else if (identifierEquals("BLOCK")) {
            lexer.nextToken();
            acceptIdentifier("IO");
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.BLOCK_IO);
          } else if (identifierEquals("CONTEXT")) {
            lexer.nextToken();
            acceptIdentifier("SWITCHES");
            stmt.getTypes()
                .add(WaspSqlShowProfileStatement.Type.CONTEXT_SWITCHES);
          } else if (identifierEquals("CPU")) {
            lexer.nextToken();
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.CPU);
          } else if (identifierEquals("IPC")) {
            lexer.nextToken();
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.IPC);
          } else if (identifierEquals("MEMORY")) {
            lexer.nextToken();
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.MEMORY);
          } else if (identifierEquals("PAGE")) {
            lexer.nextToken();
            acceptIdentifier("FAULTS");
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.PAGE_FAULTS);
          } else if (identifierEquals("SOURCE")) {
            lexer.nextToken();
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.SOURCE);
          } else if (identifierEquals("SWAPS")) {
            lexer.nextToken();
            stmt.getTypes().add(WaspSqlShowProfileStatement.Type.SWAPS);
          } else {
            break;
          }
  
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          }
          break;
        }
  
        if (lexer.token() == Token.FOR) {
          lexer.nextToken();
          acceptIdentifier("QUERY");
          stmt.setForQuery(this.exprParser.primary());
        }
  
        stmt.setLimit(this.parseLimit());
  
        return stmt;
      }
      if (lexer.token() == Token.TABLE) {
        lexer.nextToken();
        acceptIdentifier(STATUS);
        WaspSqlShowTableStatusStatement stmt = new WaspSqlShowTableStatusStatement();
        if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
          lexer.nextToken();
          stmt.setDatabase(this.exprParser.name());
        }
        if (lexer.token() == Token.LIKE) {
          lexer.nextToken();
          stmt.setLike(this.exprParser.expr());
        }
        if (lexer.token() == Token.WHERE) {
          lexer.nextToken();
          stmt.setWhere(this.exprParser.expr());
        }
        return stmt;
      }
      throw new ParserException("TODO " + lexer.stringVal());
    }
  
    private WaspSqlShowStatusStatement parseShowStatus() {
      WaspSqlShowStatusStatement stmt = new WaspSqlShowStatusStatement();
      if (lexer.token() == Token.LIKE) {
        lexer.nextToken();
        SQLExpr like = exprParser.expr();
        stmt.setLike(like);
      }
      if (lexer.token() == Token.WHERE) {
        lexer.nextToken();
        SQLExpr where = exprParser.expr();
        stmt.setWhere(where);
      }
      return stmt;
    }
  
    private WaspSqlShowVariantsStatement parseShowVariants() {
      WaspSqlShowVariantsStatement stmt = new WaspSqlShowVariantsStatement();
      if (lexer.token() == Token.LIKE) {
        lexer.nextToken();
        SQLExpr like = exprParser.expr();
        stmt.setLike(like);
      }
      if (lexer.token() == Token.WHERE) {
        lexer.nextToken();
        SQLExpr where = exprParser.expr();
        stmt.setWhere(where);
      }
      return stmt;
    }
  
    private WaspSqlShowWarningsStatement parseShowWarnings() {
      WaspSqlShowWarningsStatement stmt = new WaspSqlShowWarningsStatement();
      stmt.setLimit(parseLimit());
      return stmt;
    }
  
    private WaspSqlShowDatabasesStatement parseShowDatabases() {
      WaspSqlShowDatabasesStatement stmt = new WaspSqlShowDatabasesStatement();
      if (lexer.token() == Token.LIKE) {
        lexer.nextToken();
        SQLExpr like = exprParser.expr();
        stmt.setLike(like);
      }
      if (lexer.token() == Token.WHERE) {
        lexer.nextToken();
        SQLExpr where = exprParser.expr();
        stmt.setWhere(where);
      }
      return stmt;
    }
  
    private WaspSqlShowTablesStatement parseShowTabless() {
      WaspSqlShowTablesStatement stmt = new WaspSqlShowTablesStatement();
      if (lexer.token() == Token.FROM) {
        lexer.nextToken();
        SQLName database = exprParser.name();
        stmt.setDatabase(database);
      }
      if (lexer.token() == Token.LIKE) {
        lexer.nextToken();
        SQLExpr like = exprParser.expr();
        stmt.setLike(like);
      }
      if (lexer.token() == Token.WHERE) {
        lexer.nextToken();
        SQLExpr where = exprParser.expr();
        stmt.setWhere(where);
      }
      return stmt;
    }
  
    private WaspSqlShowColumnsStatement parseShowColumns() {
      WaspSqlShowColumnsStatement stmt = new WaspSqlShowColumnsStatement();
      if (lexer.token() == Token.FROM) {
        lexer.nextToken();
        SQLName table = exprParser.name();
        stmt.setTable(table);
        if (lexer.token() == Token.FROM) {
          lexer.nextToken();
          SQLName database = exprParser.name();
          stmt.setDatabase(database);
        }
      }
      if (lexer.token() == Token.LIKE) {
        lexer.nextToken();
        SQLExpr like = exprParser.expr();
        stmt.setLike(like);
      }
      if (lexer.token() == Token.WHERE) {
        lexer.nextToken();
        SQLExpr where = exprParser.expr();
        stmt.setWhere(where);
      }
      return stmt;
    }
  
    public WaspSqlStartTransactionStatement parseStart() {
      acceptIdentifier("START");
      acceptIdentifier("TRANSACTION");
      WaspSqlStartTransactionStatement stmt = new WaspSqlStartTransactionStatement();
      if (identifierEquals("WITH")) {
        lexer.nextToken();
        acceptIdentifier("CONSISTENT");
        acceptIdentifier("SNAPSHOT");
        stmt.setConsistentSnapshot(true);
      }
      if (identifierEquals("BEGIN")) {
        lexer.nextToken();
        stmt.setBegin(true);
        if (identifierEquals("WORK")) {
          lexer.nextToken();
          stmt.setWork(true);
        }
      }
      return stmt;
    }
  
    protected SQLStatement parseLoad() {
      acceptIdentifier("LOAD");
      if (identifierEquals("DATA")) {
        SQLStatement stmt = parseLoadDataInFile();
        return stmt;
      }
      if (identifierEquals("XML")) {
        SQLStatement stmt = parseLoadXml();
        return stmt;
      }
      throw new ParserException("TODO");
    }
  
    protected WaspSqlLoadXmlStatement parseLoadXml() {
      acceptIdentifier("XML");
      WaspSqlLoadXmlStatement stmt = new WaspSqlLoadXmlStatement();
      if (identifierEquals(LOW_PRIORITY)) {
        stmt.setLowPriority(true);
        lexer.nextToken();
      }
      if (identifierEquals("CONCURRENT")) {
        stmt.setConcurrent(true);
        lexer.nextToken();
      }
      if (identifierEquals(LOCAL)) {
        stmt.setLocal(true);
        lexer.nextToken();
      }
      acceptIdentifier("INFILE");
      SQLLiteralExpr fileName = (SQLLiteralExpr) exprParser.expr();
      stmt.setFileName(fileName);
      if (identifierEquals(REPLACE)) {
        stmt.setReplicate(true);
        lexer.nextToken();
      }
      if (identifierEquals(IGNORE)) {
        stmt.setIgnore(true);
        lexer.nextToken();
      }
      accept(Token.INTO);
      accept(Token.TABLE);
      SQLName tableName = exprParser.name();
      stmt.setTableName(tableName);
      if (identifierEquals(CHARACTER)) {
        lexer.nextToken();
        accept(Token.SET);
        if (lexer.token() != Token.LITERAL_CHARS) {
          throw new ParserException("syntax error, illegal charset");
        }
        String charset = lexer.stringVal();
        lexer.nextToken();
        stmt.setCharset(charset);
      }
      if (identifierEquals("ROWS")) {
        lexer.nextToken();
        accept(Token.IDENTIFIED);
        accept(Token.BY);
        SQLExpr rowsIdentifiedBy = exprParser.expr();
        stmt.setRowsIdentifiedBy(rowsIdentifiedBy);
      }
      if (identifierEquals(IGNORE)) {
        throw new ParserException("TODO");
      }
      if (lexer.token() == Token.SET) {
        throw new ParserException("TODO");
      }
      return stmt;
    }
  
    protected WaspSqlLoadDataInFileStatement parseLoadDataInFile() {
      acceptIdentifier("DATA");
      WaspSqlLoadDataInFileStatement stmt = new WaspSqlLoadDataInFileStatement();
      if (identifierEquals(LOW_PRIORITY)) {
        stmt.setLowPriority(true);
        lexer.nextToken();
      }
      if (identifierEquals("CONCURRENT")) {
        stmt.setConcurrent(true);
        lexer.nextToken();
      }
      if (identifierEquals(LOCAL)) {
        stmt.setLocal(true);
        lexer.nextToken();
      }
      acceptIdentifier("INFILE");
      SQLLiteralExpr fileName = (SQLLiteralExpr) exprParser.expr();
      stmt.setFileName(fileName);
      if (identifierEquals(REPLACE)) {
        stmt.setReplicate(true);
        lexer.nextToken();
      }
      if (identifierEquals(IGNORE)) {
        stmt.setIgnore(true);
        lexer.nextToken();
      }
      accept(Token.INTO);
      accept(Token.TABLE);
      SQLName tableName = exprParser.name();
      stmt.setTableName(tableName);
      if (identifierEquals(CHARACTER)) {
        lexer.nextToken();
        accept(Token.SET);
        if (lexer.token() != Token.LITERAL_CHARS) {
          throw new ParserException("syntax error, illegal charset");
        }
        String charset = lexer.stringVal();
        lexer.nextToken();
        stmt.setCharset(charset);
      }
      if (identifierEquals("FIELDS") || identifierEquals("COLUMNS")) {
        throw new ParserException("TODO");
      }
      if (identifierEquals("LINES")) {
        throw new ParserException("TODO");
      }
      if (identifierEquals(IGNORE)) {
        throw new ParserException("TODO");
      }
      if (lexer.token() == Token.SET) {
        throw new ParserException("TODO");
      }
      return stmt;
    }
  
    public WaspSqlPrepareStatement parsePrepare() {
      acceptIdentifier("PREPARE");
      SQLName name = exprParser.name();
      accept(Token.FROM);
      SQLExpr from = exprParser.expr();
      return new WaspSqlPrepareStatement(name, from);
    }
  
    public WaspSqlExecuteStatement parseExecute() {
      acceptIdentifier("EXECUTE");
      WaspSqlExecuteStatement stmt = new WaspSqlExecuteStatement();
      SQLName statementName = exprParser.name();
      stmt.setStatementName(statementName);
      if (identifierEquals("USING")) {
        lexer.nextToken();
        exprParser.exprList(stmt.getParameters());
      }
      return stmt;
    }
  
    public SQLInsertStatement parseInsert() {
      WaspSqlInsertStatement insertStatement = new WaspSqlInsertStatement();
      if (lexer.token() == Token.INSERT) {
        lexer.nextToken();
        if (identifierEquals(LOW_PRIORITY)) {
          insertStatement.setLowPriority(true);
          lexer.nextToken();
        }
        if (identifierEquals(DELAYED)) {
          insertStatement.setDelayed(true);
          lexer.nextToken();
        }
        if (identifierEquals("HIGH_PRIORITY")) {
          insertStatement.setHighPriority(true);
          lexer.nextToken();
        }
        if (identifierEquals(IGNORE)) {
          insertStatement.setIgnore(true);
          lexer.nextToken();
        }
        if (lexer.token() == Token.INTO) {
          lexer.nextToken();
        }
        SQLName tableName = this.exprParser.name();
        insertStatement.setTableName(tableName);
        if (lexer.token() == Token.IDENTIFIER && !identifierEquals("VALUE")) {
          insertStatement.setAlias(lexer.stringVal());
          lexer.nextToken();
        }
      }
      if (lexer.token() == (Token.LPAREN)) {
        lexer.nextToken();
        if (lexer.token() == (Token.SELECT)) {
          SQLSelect select = this.exprParser.createSelectParser().select();
          select.setParent(insertStatement);
          insertStatement.setQuery(select);
        } else {
          this.exprParser.exprList(insertStatement.getColumns());
        }
        accept(Token.RPAREN);
      }
      if (lexer.token() == Token.VALUES || identifierEquals("VALUE")) {
        lexer.nextToken();
        for (;;) {
          accept(Token.LPAREN);
          SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
          this.exprParser.exprList(values.getValues());
          insertStatement.getValuesList().add(values);
          accept(Token.RPAREN);
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          } else {
            break;
          }
        }
      } else if (lexer.token() == Token.SET) {
        lexer.nextToken();
        SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
        insertStatement.getValuesList().add(values);
        for (;;) {
          SQLName name = this.exprParser.name();
          insertStatement.getColumns().add(name);
          if (lexer.token() == Token.EQ) {
            lexer.nextToken();
          } else {
            accept(Token.COLONEQ);
          }
          values.getValues().add(this.exprParser.expr());
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          }
          break;
        }
      } else if (lexer.token() == (Token.SELECT)) {
        SQLSelect select = this.exprParser.createSelectParser().select();
        select.setParent(insertStatement);
        insertStatement.setQuery(select);
      } else if (lexer.token() == (Token.LPAREN)) {
        lexer.nextToken();
        SQLSelect select = this.exprParser.createSelectParser().select();
        select.setParent(insertStatement);
        insertStatement.setQuery(select);
        accept(Token.RPAREN);
      }
      if (lexer.token() == Token.ON) {
        lexer.nextToken();
        acceptIdentifier("DUPLICATE");
        accept(Token.KEY);
        accept(Token.UPDATE);
        exprParser.exprList(insertStatement.getDuplicateKeyUpdate());
      }
  
      return insertStatement;
    }
  
    public SQLStatement parseDropUser() {
      acceptIdentifier(USER);
      WaspSqlDropUser stmt = new WaspSqlDropUser();
      for (;;) {
        SQLExpr expr = this.exprParser.expr();
        stmt.getUsers().add(expr);
        if (lexer.token() == Token.COMMA) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      return stmt;
    }
  
    protected SQLDropTableStatement parseDropTable(boolean acceptDrop) {
      if (acceptDrop) {
        accept(Token.DROP);
      }
      WaspSqlDropTableStatement stmt = new WaspSqlDropTableStatement();
      if (identifierEquals(TEMPORARY)) {
        lexer.nextToken();
        stmt.setTemporary(true);
      }
      accept(Token.TABLE);
      if (lexer.token() == Token.IF) {
        lexer.nextToken();
        accept(Token.EXISTS);
        stmt.setIfExists(true);
      }
      for (;;) {
        SQLName name = this.exprParser.name();
        stmt.addTableSource(name);
        if (lexer.token() == Token.COMMA) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      if (identifierEquals(RESTRICT)) {
        stmt.setOption(RESTRICT);
        lexer.nextToken();
      } else if (identifierEquals(CASCADE)) {
        stmt.setOption(CASCADE);
        lexer.nextToken();
      }
      return stmt;
    }
  
    public SQLSelectParser createSQLSelectParser() {
      return new WaspSqlSelectParser(this.exprParser);
    }
  
    public Limit parseLimit() {
      return ((WaspSqlExprParser) this.exprParser).parseLimit();
    }
  
    public SQLStatement parseAlter() {
      accept(Token.ALTER);
      boolean ignore = false;
      if (identifierEquals(IGNORE)) {
        ignore = true;
        lexer.nextToken();
      }
      if (lexer.token() == Token.TABLE) {
        lexer.nextToken();
        WaspSqlAlterTableStatement stmt = new WaspSqlAlterTableStatement();
        stmt.setIgnore(ignore);
        stmt.setName(this.exprParser.name());
        for (;;) {
          if (identifierEquals("ADD")) {
            lexer.nextToken();
            if (identifierEquals("COLUMN")) {
              lexer.nextToken();
              WaspSqlAlterTableAddColumn item = new WaspSqlAlterTableAddColumn();
              SQLColumnDefinition columnDef = this.exprParser.parseColumn();
              item.getColumns().add(columnDef);
              if (identifierEquals("AFTER")) {
                lexer.nextToken();
                item.setAfterColumn(this.exprParser.name());
              } else if (identifierEquals("FIRST")) {
                lexer.nextToken();
                item.setFirst(true);
              }
              stmt.getItems().add(item);
            } else if (lexer.token() == Token.INDEX) {
              lexer.nextToken();
              WaspSqlAlterTableAddIndex item = new WaspSqlAlterTableAddIndex();
  
              if (lexer.token() == Token.LPAREN) {
                lexer.nextToken();
              } else {
                item.setName(this.exprParser.name());
                accept(Token.LPAREN);
              }
              for (;;) {
                SQLSelectOrderByItem column = this.exprParser
                    .parseSelectOrderByItem();
                item.getItems().add(column);
                if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
                }
                break;
              }
              accept(Token.RPAREN);
              stmt.getItems().add(item);
            } else if (lexer.token() == Token.UNIQUE) {
              lexer.nextToken();
              if (lexer.token() == Token.INDEX) {
                lexer.nextToken();
              }
              WaspSqlAlterTableAddUnique item = new WaspSqlAlterTableAddUnique();
              if (lexer.token() == Token.LPAREN) {
                lexer.nextToken();
              } else {
                item.setName(this.exprParser.name());
                accept(Token.LPAREN);
              }
              for (;;) {
                SQLSelectOrderByItem column = this.exprParser
                    .parseSelectOrderByItem();
                item.getItems().add(column);
                if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
                }
                break;
              }
              accept(Token.RPAREN);
              stmt.getItems().add(item);
            } else if (lexer.token() == Token.PRIMARY) {
              SQLPrimaryKey primaryKey = ((WaspSqlExprParser) this.exprParser)
                  .parsePrimaryKey();
              SQLAlterTableAddPrimaryKey item = new SQLAlterTableAddPrimaryKey();
              item.setPrimaryKey(primaryKey);
              stmt.getItems().add(item);
            } else if (lexer.token() == Token.KEY) {
              throw new ParserException("TODO " + lexer.token() + " "
                  + lexer.stringVal());
            } else if (lexer.token() == Token.CONSTRAINT) {
              throw new ParserException("TODO " + lexer.token() + " "
                  + lexer.stringVal());
            } else if (identifierEquals(FULLTEXT)) {
              throw new ParserException("TODO " + lexer.token() + " "
                  + lexer.stringVal());
            } else if (identifierEquals(SPATIAL)) {
              throw new ParserException("TODO " + lexer.token() + " "
                  + lexer.stringVal());
            } else {
              WaspSqlAlterTableAddColumn item = new WaspSqlAlterTableAddColumn();
              SQLColumnDefinition columnDef = this.exprParser.parseColumn();
              item.getColumns().add(columnDef);
              if (identifierEquals("AFTER")) {
                lexer.nextToken();
                item.setAfterColumn(this.exprParser.name());
              }
              stmt.getItems().add(item);
            }
          } else if (identifierEquals("ALTER")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("CHANGE")) {
            lexer.nextToken();
            if (identifierEquals("COLUMN")) {
              lexer.nextToken();
            }
            WaspSqlAlterTableChangeColumn item = new WaspSqlAlterTableChangeColumn();
            item.setColumnName(this.exprParser.name());
            item.setNewColumnDefinition(this.exprParser.parseColumn());
            if (identifierEquals("AFTER")) {
              lexer.nextToken();
              item.setAfterColumn(this.exprParser.name());
            } else if (identifierEquals("FIRST")) {
              lexer.nextToken();
              if (lexer.token() == Token.IDENTIFIER) {
                item.setFirstColumn(this.exprParser.name());
              } else {
                item.setFirst(true);
              }
            }
            stmt.getItems().add(item);
          } else if (identifierEquals("MODIFY")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (lexer.token() == Token.DROP) {
            lexer.nextToken();
            if (lexer.token() == Token.INDEX) {
              lexer.nextToken();
              SQLName indexName = this.exprParser.name();
              SQLAlterTableDropIndex item = new SQLAlterTableDropIndex();
              item.setIndexName(indexName);
              stmt.getItems().add(item);
            } else {
              if (identifierEquals("COLUMN")) {
                lexer.nextToken();
              }
              SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
              item.setColumnName(this.exprParser.name());
              stmt.getItems().add(item);
            }
          } else if (identifierEquals("DISABLE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("ENABLE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("RENAME")) {
            lexer.nextToken();
            WaspSqlRenameTableStatement renameStmt = new WaspSqlRenameTableStatement();
            WaspSqlRenameTableStatement.Item item = new WaspSqlRenameTableStatement.Item();
            item.setName(stmt.getTableSource().getExpr());
            item.setTo(this.exprParser.name());
            renameStmt.getItems().add(item);
  
            return renameStmt;
          } else if (lexer.token() == Token.ORDER) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("CONVERT")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (lexer.token() == Token.DEFAULT) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("DISCARD")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("IMPORT")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("FORCE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("TRUNCATE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("COALESCE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
  
          } else if (identifierEquals("REORGANIZE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("EXCHANGE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("ANALYZE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("CHECK")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("OPTIMIZE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("REBUILD")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("REPAIR")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals("REMOVE")) {
            throw new ParserException("TODO " + lexer.token() + " "
                + lexer.stringVal());
          } else if (identifierEquals(CHARACTER)) {
            lexer.nextToken();
            accept(Token.SET);
            accept(Token.EQ);
            WaspSqlAlterTableCharacter item = new WaspSqlAlterTableCharacter();
            item.setCharacterSet(this.exprParser.primary());
            if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
              acceptIdentifier(COLLATE2);
              accept(Token.EQ);
              item.setCollate(this.exprParser.primary());
            }
            stmt.getItems().add(item);
          } else {
            break;
          }
  
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          } else {
            break;
          }
        }
  
        return stmt;
      }
      throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
    }
  
    public SQLStatement parseRename() {
      WaspSqlRenameTableStatement stmt = new WaspSqlRenameTableStatement();
      acceptIdentifier("RENAME");
      accept(Token.TABLE);
      for (;;) {
        WaspSqlRenameTableStatement.Item item = new WaspSqlRenameTableStatement.Item();
        item.setName(this.exprParser.name());
        acceptIdentifier("TO");
        item.setTo(this.exprParser.name());
        stmt.getItems().add(item);
        if (lexer.token() == Token.COMMA) {
          lexer.nextToken();
          continue;
        }
        break;
      }
      return stmt;
    }
  
    public SQLStatement parseCreateDatabase() {
      if (lexer.token() == Token.CREATE) {
        lexer.nextToken();
      }
      accept(Token.DATABASE);
      SQLCreateDatabaseStatement stmt = new SQLCreateDatabaseStatement();
      stmt.setName(this.exprParser.name());
      if (lexer.token() == Token.DEFAULT) {
        lexer.nextToken();
      }
      return stmt;
    }
  }