content: /*
   * Copyright 1999-2017 Alibaba Group Holding Ltd.
   *
   * Licensed 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.druid.sql.parser;
  
  import com.alibaba.druid.DbType;
  import com.alibaba.druid.sql.ast.*;
  import com.alibaba.druid.sql.ast.SQLParameter.ParameterType;
  import com.alibaba.druid.sql.ast.expr.*;
  import com.alibaba.druid.sql.ast.statement.*;
  import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerType;
  import com.alibaba.druid.sql.dialect.hive.ast.HiveInsert;
  import com.alibaba.druid.sql.dialect.hive.ast.HiveInsertStatement;
  import com.alibaba.druid.sql.dialect.hive.ast.HiveMultiInsertStatement;
  import com.alibaba.druid.sql.dialect.hive.stmt.HiveCreateFunctionStatement;
  import com.alibaba.druid.sql.dialect.hive.stmt.HiveMsckRepairStatement;
  import com.alibaba.druid.sql.dialect.mysql.ast.FullTextType;
  import com.alibaba.druid.sql.dialect.mysql.ast.statement.*;
  import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser;
  import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser;
  import com.alibaba.druid.sql.repository.SchemaRepository;
  import com.alibaba.druid.util.FnvHash;
  import com.alibaba.druid.util.FnvHash.Constants;
  import com.alibaba.druid.util.MySqlUtils;
  import com.alibaba.druid.util.StringUtils;
  
  import java.math.BigDecimal;
  import java.sql.SQLException;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.TimeZone;
  import java.util.UUID;
  
  import static com.alibaba.druid.sql.parser.DialectFeature.ParserFeature.*;
  import static com.alibaba.druid.sql.parser.Token.*;
  
  public class SQLStatementParser extends SQLParser {
      protected SchemaRepository repository;
      protected SQLExprParser exprParser;
      protected boolean parseCompleteValues = true;
      protected int parseValuesSize = 3;
      protected SQLSelectListCache selectListCache;
      protected InsertColumnsCache insertColumnsCache;
  
      protected java.sql.Timestamp now;
      protected java.sql.Date currentDate;
  
      protected Token expectedNextToken;
      private static final boolean END_TOKEN_CHECKING_ENABLED = !Boolean.getBoolean("druid_sql_parser_end_token_checking_disabled");
      private static final String UNSUPPORT_TOKEN_MSG_PREFIX = "not supported.";
  
      public SQLStatementParser(String sql) {
          this(sql, null);
      }
  
      public SQLStatementParser(String sql, DbType dbType) {
          this(new SQLExprParser(sql, dbType));
      }
  
      public SQLStatementParser(String sql, DbType dbType, SQLParserFeature... features) {
          this(new SQLExprParser(sql, dbType, features));
      }
  
      public SQLStatementParser(SQLExprParser exprParser) {
          super(exprParser.getLexer(), exprParser.getDbType());
          this.exprParser = exprParser;
          this.dbType = exprParser.dbType;
      }
  
      protected SQLStatementParser(Lexer lexer, DbType dbType) {
          super(lexer, dbType);
      }
  
      public boolean isKeepComments() {
          return lexer.isKeepComments();
      }
  
      public void setKeepComments(boolean keepComments) {
          this.lexer.setKeepComments(keepComments);
      }
  
      public SQLExprParser getExprParser() {
          return exprParser;
      }
  
      public SchemaRepository getRepository() {
          return repository;
      }
  
      public void setRepository(SchemaRepository repository) {
          this.repository = repository;
      }
  
      public List<SQLStatement> parseStatementList() {
          List<SQLStatement> statementList = new ArrayList<SQLStatement>();
          parseStatementList(statementList, -1, null);
          return statementList;
      }
  
      public List<SQLStatement> parseStatementList(SQLObject parent) {
          List<SQLStatement> statementList = new ArrayList<SQLStatement>();
          parseStatementList(statementList, -1, parent);
          return statementList;
      }
  
      public void parseStatementList(List<SQLStatement> statementList) {
          parseStatementList(statementList, -1, null);
      }
  
      public void parseStatementList(List<SQLStatement> statementList, int max) {
          parseStatementList(statementList, max, null);
      }
  
      public void parseStatementList(List<SQLStatement> statementList, int max, SQLObject parent) {
          if (lexer.token == Token.SELECT) {
              String[] words = lexer.text.split("\\s+");
              if (words.length == 2
                      && "select".equalsIgnoreCase(words[0])
                      && "@@session.tx_read_only".equalsIgnoreCase(words[1])) {
                  SQLSelect select = new SQLSelect();
                  MySqlSelectQueryBlock queryBlock = new MySqlSelectQueryBlock();
                  queryBlock.addSelectItem(new SQLPropertyExpr(new SQLVariantRefExpr("@@session"), "tx_read_only"));
                  select.setQuery(queryBlock);
  
                  SQLSelectStatement stmt = new SQLSelectStatement(select);
                  statementList.add(stmt);
  
                  lexer.reset(29, '\u001A', Token.EOF);
                  return;
              }
          }
  
          boolean semi = false;
          for (int i = 0; ; i++) {
              if (max != -1) {
                  if (statementList.size() >= max) {
                      return;
                  }
              }
              while (lexer.token == MULTI_LINE_COMMENT || lexer.token == LINE_COMMENT) {
                  lexer.nextToken();
              }
  
              switch (lexer.token) {
                  case EOF:
                  case END:
                  case UNTIL:
                  case ELSE:
                  case WHEN:
                  case EXCEPTION:
                      if (lexer.isKeepComments() && lexer.hasComment() && !statementList.isEmpty()) {
                          SQLStatement stmt = statementList.get(statementList.size() - 1);
                          stmt.addAfterComment(lexer.readAndResetComments());
                      }
                      if (END == lexer.token && dialectFeatureEnabled(ParseStatementListWhen)) {
                          Lexer.SavePoint savePoint = lexer.mark();
                          lexer.nextToken();
                          if (lexer.token == Token.IF) {
                              lexer.reset(savePoint);
                              return;
                          }
  
                          if (parent instanceof SQLBlockStatement) {
                              lexer.reset(savePoint);
                              return;
                          }
  
                          lexer.reset(savePoint);
                          SQLStatement stmt = parseEnd();
                          stmt.setParent(parent);
                          statementList.add(stmt);
                          continue;
                      }
                      return;
                  case ELSEIF:
                      if (parent instanceof SQLIfStatement) {
                          return;
                      }
                      break;
                  case SEMI: {
                      char ch = lexer.ch;
                      lexer.nextToken();
  
                      if (statementList.size() > 0) {
                          SQLStatement lastStmt = statementList.get(statementList.size() - 1);
                          lastStmt.setAfterSemi(true);
  
                          if (lexer.isKeepComments()) {
                              if (ch == '\n'
                                      && lexer.getComments() != null
                                      && !lexer.getComments().isEmpty()
                                      && !(lastStmt instanceof SQLSetStatement)
                              ) {
                                  lexer.getComments().add(0, new String("\n"));
                              }
                              lastStmt.addAfterComment(lexer.readAndResetComments());
                          }
                      }
  
                      semi = true;
  
                      continue;
                  }
                  case WITH: {
                      SQLStatement stmt = parseWith();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case SELECT: {
                      MySqlHintStatement hintStatement = null;
                      if (i == 1
                              && !statementList.isEmpty()
                              && statementList.get(statementList.size() - i) instanceof MySqlHintStatement) {
                          hintStatement = (MySqlHintStatement) statementList.get(statementList.size() - i);
                      } else if (i > 0 && dialectFeatureEnabled(ParseStatementListSelectUnsupportedSyntax) && !semi
                              && !(!statementList.isEmpty() && statementList.size() - i >= 0 && statementList.get(statementList.size() - i).isAfterSemi())
                      ) {
                          throw new ParserException("syntax error. " + lexer.info());
                      }
                      SQLStatement stmt = parseSelect();
                      stmt.setParent(parent);
                      if (hintStatement != null && stmt instanceof SQLStatementImpl) {
                          SQLStatementImpl stmtImpl = (SQLStatementImpl) stmt;
                          List<SQLCommentHint> hints = stmtImpl.getHeadHintsDirect();
                          if (hints == null) {
                              stmtImpl.setHeadHints(hintStatement.getHints());
                          } else {
                              hints.addAll(hintStatement.getHints());
                          }
                          statementList.set(statementList.size() - 1, stmt);
                      } else {
                          statementList.add(stmt);
                      }
                      semi = false;
                      continue;
                  }
                  case UPDATE: {
                      //FOR ADS
                      Lexer.SavePoint savePoint = lexer.mark();
                      lexer.nextToken();
                      if (dialectFeatureEnabled(ParseStatementListUpdatePlanCache) && lexer.identifierEquals("PLANCACHE")) {
                          lexer.nextToken();
                          if (lexer.token == Token.SELECT) {
                              MySqlUpdatePlanCacheStatement stmt = new MySqlUpdatePlanCacheStatement();
  
                              SQLSelect fromSelect = createSQLSelectParser().select();
                              accept(Token.TO);
                              SQLSelect toSelect = createSQLSelectParser().select();
  
                              stmt.setFormSelect(fromSelect);
                              stmt.setToSelect(toSelect);
  
                              statementList.add(stmt);
                              continue;
                          }
                      }
  
                      lexer.reset(savePoint);
                      SQLStatement stmt = parseUpdateStatement();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case CREATE: {
                      List<String> comments = null;
                      if (lexer.isKeepComments() && lexer.hasComment()) {
                          comments = lexer.readAndResetComments();
                      }
                      SQLStatement stmt = parseCreate();
                      stmt.addBeforeComment(comments);
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case INSERT: {
                      SQLStatement stmt = parseInsert();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case DELETE: {
                      SQLStatement stmt = parseDeleteStatement();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case GET: {
                      SQLStatement stmt = parseGetDiagnosticsStatement();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case EXPLAIN: {
                      if (lexer.keepSourceLocation) {
                          lexer.computeRowAndColumn();
                      }
                      int sourceLine = lexer.posLine;
                      int sourceColumn = lexer.posColumn;
  
                      //FOR ADS
                      Lexer.SavePoint savePoint = lexer.mark();
                      lexer.nextToken();
  
                      if (lexer.identifierEquals("PLANCACHE")) {
                          lexer.nextToken();
                          MySqlExplainPlanCacheStatement stmt = new MySqlExplainPlanCacheStatement();
                          stmt.setSource(sourceLine, sourceColumn);
                          statementList.add(stmt);
  //                    } else if(lexer.token ==  Token.ANALYZE) {
  //                        lexer.nextToken();
  //
  //                        SQLExplainAnalyzeStatement stmt = new SQLExplainAnalyzeStatement();
  //                        stmt.setSelect(createSQLSelectParser().select());
  //                        statementList.add(stmt);
                      } else {
                          lexer.reset(savePoint);
                          SQLExplainStatement stmt = parseExplain();
                          stmt.setSource(sourceLine, sourceColumn);
                          stmt.setParent(parent);
                          statementList.add(stmt);
                      }
                      continue;
                  }
                  case SET: {
                      SQLStatement stmt = parseSet();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case ALTER: {
                      SQLStatement stmt = parseAlter();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case TRUNCATE: {
                      SQLStatement stmt = parseTruncate();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case USE: {
                      SQLStatement stmt = parseUse();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case GRANT: {
                      SQLStatement stmt = parseGrant();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case REVOKE: {
                      SQLStatement stmt = parseRevoke();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case SHOW: {
                      SQLStatement stmt = parseShow();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case MERGE: {
                      SQLStatement stmt = parseMerge();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case REPEAT: {
                      SQLStatement stmt = parseRepeat();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case DECLARE: {
                      SQLStatement stmt = parseDeclare();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case WHILE: {
                      SQLStatement stmt = parseWhile();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case IF: {
                      SQLStatement stmt = parseIf();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case CASE: {
                      SQLStatement stmt = parseCase();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case OPEN: {
                      SQLStatement stmt = parseOpen();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case FETCH: {
                      SQLStatement stmt = parseFetch();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case DROP: {
                      SQLStatement stmt = parseDrop();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case COMMENT: {
                      SQLStatement stmt = parseComment();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case KILL: {
                      SQLStatement stmt = parseKill();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case CLOSE: {
                      SQLStatement stmt = parseClose();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case RETURN: {
                      SQLStatement stmt = parseReturn();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case UPSERT: {
                      SQLStatement stmt = parseUpsert();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case LEAVE: {
                      SQLStatement stmt = parseLeave();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  case CACHE: {
                      SQLStatement stmt = parseCache();
                      stmt.setParent(parent);
                      statementList.add(stmt);
                      continue;
                  }
                  default:
                      break;
              }
  
              if (lexer.token == Token.LBRACE || lexer.identifierEquals("CALL")) {
                  SQLCallStatement stmt = parseCall();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("UPSERT")) {
                  SQLStatement stmt = parseUpsert();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("LIST")) {
                  Lexer.SavePoint mark = lexer.mark();
  
                  SQLStatement stmt = parseList();
                  if (stmt != null) {
                      statementList.add(stmt);
                      continue;
                  } else {
                      lexer.reset(mark);
                  }
              }
  
              if (lexer.identifierEquals("RENAME")) {
                  SQLStatement stmt = parseRename();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("RELEASE")) {
                  SQLStatement stmt = parseReleaseSavePoint();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("SAVEPOINT")) {
                  SQLStatement stmt = parseSavePoint();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("REFRESH")) {
                  SQLStatement stmt = parseRefresh();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("SETPROJECT")) {
                  SQLStatement stmt = parseSet();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.COPY)) {
                  SQLStatement stmt = parseCopy();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.token == Token.DESC || lexer.identifierEquals(FnvHash.Constants.DESCRIBE)) {
                  SQLStatement stmt = parseDescribe();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("ROLLBACK")) {
                  SQLStatement stmt = parseRollback();
                  statementList.add(stmt);
  
                  if (parent instanceof SQLBlockStatement
                          && dialectFeatureEnabled(ParseStatementListRollbackReturn)) {
                      return;
                  }
  
                  continue;
              }
  
              if (lexer.identifierEquals("DUMP")) {
                  SQLStatement stmt = parseDump();
                  statementList.add(stmt);
  
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.COMMIT)) {
                  SQLStatement stmt = parseCommit();
  
                  statementList.add(stmt);
  
                  if (parent instanceof SQLBlockStatement
                          && dialectFeatureEnabled(ParseStatementListCommitReturn)) {
                      return;
                  }
  
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.RETURN)) {
                  SQLStatement stmt = parseReturn();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.PURGE)) {
                  SQLStatement stmt = parsePurge();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.FLASHBACK)) {
                  SQLStatement stmt = parseFlashback();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.WHO)) {
                  SQLStatement stmt = parseWhoami();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.token == Token.FOR) {
                  SQLStatement stmt = parseFor();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.token == Token.LPAREN) {
                  Lexer.SavePoint savePoint = lexer.markOut();
  
                  int parenCount = 0;
                  do {
                      lexer.nextToken();
                      parenCount++;
                  } while (lexer.token == Token.LPAREN);
  
                  if (lexer.token == RPAREN && parenCount == 1 && dialectFeatureEnabled(ParseStatementListLparenContinue)) {
                      lexer.nextToken();
                      continue;
                  }
  
                  if (lexer.token == Token.SELECT) {
                      lexer.reset(savePoint);
                      SQLStatement stmt = parseSelect();
                      statementList.add(stmt);
                      continue;
                  } else {
                      throw new ParserException("TODO " + lexer.info());
                  }
              }
  
              if (lexer.token == Token.VALUES) {
                  SQLValuesTableSource values = this.createSQLSelectParser().parseValues();
                  SQLSelectStatement stmt = new SQLSelectStatement();
                  stmt.setSelect(
                          new SQLSelect(values)
                  );
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.identifierEquals("OPTIMIZE")) {
                  SQLStatement stmt = parseOptimize();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.token == WHILE) {
                  SQLStatement stmt = parseWhile();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.token == LOOP) {
                  SQLStatement stmt = parseLoop();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.token == CONTINUE) {
                  SQLStatement stmt = parseContinue();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.token == LEAVE) {
                  SQLStatement stmt = parseLeave();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.identifierEquals("EXECUTE")) {
                  SQLStatement stmt = parseExecute();
                  statementList.add(stmt);
                  stmt.setParent(parent);
                  continue;
              }
  
              if (lexer.identifierEquals("START")) {
                  SQLStartTransactionStatement stmt = parseStart();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.token == COMPUTE) {
                  SQLStatement stmt = parseCompute();
                  statementList.add(stmt);
                  continue;
              }
  
              if (lexer.identifierEquals("RESET")) {
                  SQLStatement stmt = parseReset();
                  statementList.add(stmt);
                  continue;
              }
  
              int size = statementList.size();
              if (parseStatementListDialect(statementList)) {
                  if (parent != null) {
                      for (int j = size; j < statementList.size(); ++j) {
                          SQLStatement dialectStmt = statementList.get(j);
                          dialectStmt.setParent(parent);
                      }
                  }
  
                  continue;
              }
  
              // throw new ParserException("syntax error, " + lexer.token + " "
              // + lexer.stringVal() + ", pos "
              // + lexer.pos());
              throw new ParserException(UNSUPPORT_TOKEN_MSG_PREFIX + lexer.info());
          }
  
      }
  
      public SQLStatement parseExecute() {
          throw new ParserException("TODO");
      }
  
      private SQLStatement parseGetDiagnosticsStatement() {
          accept(Token.GET);
          accept(Token.DIAGNOSTICS);
  
          SQLGetDiagnosticsStatement stmt = new SQLGetDiagnosticsStatement();
          stmt.setDbType(dbType);
  
          stmt.setExpr(this.exprParser.expr());
          return stmt;
      }
  
      public SQLStatement parseOptimize() {
          acceptIdentifier("OPTIMIZE");
          accept(TABLE);
  
          SQLOptimizeStatement stmt = new SQLOptimizeStatement();
          List<SQLName> names = new ArrayList<SQLName>();
          this.exprParser.names(names, stmt);
  
          for (SQLName name : names) {
              stmt.addTableSource(new SQLExprTableSource(name));
          }
  
          if (lexer.token == ON) {
              lexer.nextToken();
              acceptIdentifier("CLUSTER");
              SQLName cluster = this.exprParser.name();
              stmt.setCluster(cluster);
          }
  
          if (lexer.token() == FINAL) {
              lexer.nextToken();
              stmt.setFinal(true);
          }
  
          if (lexer.identifierEquals("DEDUPLICATE")) {
              lexer.nextToken();
              stmt.setDeduplicate(true);
              if (lexer.token == BY) {
                  lexer.nextToken();
                  stmt.setDeduplicateBy(
                          this.exprParser.expr()
                  );
              }
          }
  
          return stmt;
      }
  
      public SQLStatement parseCopy() {
          throw new ParserException("TODO : " + lexer.info());
      }
  
      public SQLStatement parseFor() {
          accept(Token.FOR);
  
          SQLForStatement stmt = new SQLForStatement();
          stmt.setDbType(dbType);
  
          stmt.setIndex(this.exprParser.name());
          accept(Token.IN);
          stmt.setRange(this.exprParser.expr());
  
          accept(Token.LOOP);
  
          this.parseStatementList(stmt.getStatements(), -1, stmt);
          accept(Token.END);
          accept(Token.LOOP);
  
          accept(Token.SEMI);
          stmt.setAfterSemi(true);
  
          return stmt;
      }
  
      public SQLStatement parseFlashback() {
          throw new ParserException("not supported. " + lexer.info());
      }
  
      public SQLStatement parseDump() {
          SQLDumpStatement stmt = new SQLDumpStatement();
          acceptIdentifier("DUMP");
          acceptIdentifier("DATA");
  
          if (lexer.identifierEquals(FnvHash.Constants.OVERWRITE)) {
              lexer.nextToken();
              stmt.setOverwrite(true);
          }
  
          if (lexer.token == Token.INTO) {
              lexer.nextToken();
              if (lexer.token == LITERAL_CHARS) {
                  stmt.setInto(new SQLCharExpr(lexer.stringVal));
                  lexer.nextToken();
              } else {
                  stmt.setInto(this.exprParser.expr());
              }
          }
  
          SQLSelect select = createSQLSelectParser().select();
          stmt.setSelect(select);
          return stmt;
      }
  
      public SQLStatement parseDrop() {
          List<String> beforeComments = null;
          if (lexer.isKeepComments() && lexer.hasComment()) {
              beforeComments = lexer.readAndResetComments();
          }
  
          Lexer.SavePoint mark = lexer.mark();
          lexer.nextToken();
  
          final SQLStatement stmt;
  
          List<SQLCommentHint> hints = null;
          if (lexer.token == Token.HINT) {
              hints = this.exprParser.parseHints();
          }
  
          boolean temporary = false;
  
          if (lexer.token == Token.TEMPORARY || lexer.identifierEquals(FnvHash.Constants.TEMPORARY)) {
              lexer.nextToken();
              temporary = true;
          }
  
          boolean physical = false;
          if (lexer.identifierEquals(FnvHash.Constants.PHYSICAL)) {
              lexer.nextToken();
              physical = true;
          }
  
          switch (lexer.token) {
              case USER:
                  stmt = parseDropUser();
                  break;
              case INDEX:
                  stmt = parseDropIndex();
                  break;
              case VIEW:
                  stmt = parseDropView(false);
                  break;
              case TRIGGER:
                  stmt = parseDropTrigger(false);
                  break;
              case DATABASE:
                  stmt = parseDropDatabaseOrSchema(false);
                  if (physical) {
                      ((SQLDropDatabaseStatement) stmt).setPhysical(physical);
                  }
                  break;
              case SCHEMA:
                  stmt = parseDropSchema(physical);
                  break;
              case FUNCTION:
                  SQLDropFunctionStatement dropFunc = parseDropFunction(false);
                  if (temporary) {
                      dropFunc.setTemporary(true);
                  }
                  stmt = dropFunc;
                  break;
              case TABLESPACE:
                  stmt = parseDropTablespace(false);
                  break;
              case PROCEDURE:
                  stmt = parseDropProcedure(false);
                  break;
              case SEQUENCE:
                  stmt = parseDropSequence(false);
                  break;
              case TABLE: {
                  SQLDropTableStatement dropTable = parseDropTable(false);
                  if (temporary) {
                      dropTable.setTemporary(true);
                  }
                  if (hints != null) {
                      dropTable.setHints(hints);
                  }
                  stmt = dropTable;
                  break;
              }
              default:
                  if (lexer.token == Token.TABLE || lexer.identifierEquals("TEMPORARY") || lexer.identifierEquals(FnvHash.Constants.PARTITIONED)) {
                      SQLDropTableStatement dropTable = parseDropTable(false);
                      if (hints != null) {
                          dropTable.setHints(hints);
                      }
                      stmt = dropTable;
                  } else if (lexer.identifierEquals(Constants.TABLES)) {
                      stmt = parseDropTable(false);
                  } else if (lexer.identifierEquals(FnvHash.Constants.EVENT)) {
                      stmt = parseDropEvent();
                  } else if (lexer.identifierEquals(FnvHash.Constants.RESOURCE)) {
                      stmt = parseDropResource();
  
                  } else if (lexer.identifierEquals(FnvHash.Constants.LOGFILE)) {
                      stmt = parseDropLogFileGroup();
  
                  } else if (lexer.identifierEquals(FnvHash.Constants.SERVER)) {
                      stmt = parseDropServer();
  
                  } else if (lexer.identifierEquals(FnvHash.Constants.TABLEGROUP)) {
                      stmt = parseDropTableGroup();
                  } else if (lexer.identifierEquals(FnvHash.Constants.ROLE)) {
                      lexer.reset(mark);
                      stmt = parseDropRole();
                  } else if (lexer.identifierEquals(FnvHash.Constants.OUTLINE)) {
                      lexer.reset(mark);
                      stmt = parseDropOutline();
  
                  } else if (lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
                      lexer.nextToken();
                      if (lexer.token == Token.TABLE) {
                          lexer.reset(mark);
                          stmt = parseDropTable(true);
                      } else if (lexer.identifierEquals(FnvHash.Constants.CATALOG)) {
                          lexer.reset(mark);
                          stmt = parseDropCatalog();
                      } else {
                          throw new ParserException("TODO " + lexer.info());
                      }
                  } else if (lexer.token() == Token.FULLTEXT) {
                      lexer.nextToken();
  
                      FullTextType type = parseFullTextType();
                      SQLName name = this.exprParser.name();
  
                      MysqlDropFullTextStatement x = new MysqlDropFullTextStatement();
                      x.setName(name);
                      x.setType(type);
                      stmt = x;
                  } else if (lexer.identifierEquals("INSTANCE_GROUP")) {
                      lexer.nextToken();
                      MySqlManageInstanceGroupStatement x = new MySqlManageInstanceGroupStatement();
                      x.setOperation(new SQLIdentifierExpr("DROP"));
  
                      for (; ; ) {
                          x.getGroupNames().add(exprParser.expr());
                          if (lexer.token() == Token.COMMA) {
                              lexer.nextToken();
                              continue;
                          }
                          break;
                      }
                      stmt = x;
                  } else if (lexer.identifierEquals("MATERIALIZED")) {
                      stmt = parseDropMaterializedView();
                  } else if (lexer.identifierEquals("OFFLINEMODEL")) {
                      stmt = parseDropOfflineModel();
                  } else {
                      throw new ParserException("TODO " + lexer.info());
                  }
  
                  break;
          }
  
          if (beforeComments != null) {
              stmt.addBeforeComment(beforeComments);
          }
  
          return stmt;
      }
  
      protected FullTextType parseFullTextType() {
          FullTextType textType;
  
          if (lexer.identifierEquals(FnvHash.Constants.CHARFILTER)) {
              textType = FullTextType.CHARFILTER;
          } else if (lexer.identifierEquals(FnvHash.Constants.TOKENIZER)) {
              textType = FullTextType.TOKENIZER;
          } else if (lexer.identifierEquals(FnvHash.Constants.TOKENFILTER)) {
              textType = FullTextType.TOKENFILTER;
          } else if (lexer.identifierEquals(FnvHash.Constants.ANALYZER)) {
              textType = FullTextType.ANALYZER;
          } else if (lexer.identifierEquals(FnvHash.Constants.DICTIONARY)) {
              textType = FullTextType.DICTIONARY;
          } else {
              throw new ParserException("type of full text must be [CHARFILTER/TOKENIZER/TOKENFILTER/ANALYZER/DICTIONARY] .");
          }
          lexer.nextToken();
          return textType;
      }
  
      protected SQLStatement parseWhoami() {
          lexer.nextToken();
          acceptIdentifier("AM");
          acceptIdentifier("I");
          return new SQLWhoamiStatement();
      }
  
      protected SQLStatement parseDropOutline() {
          accept(Token.DROP);
  
          SQLDropOutlineStatement stmt = new SQLDropOutlineStatement();
          stmt.setDbType(dbType);
  
          acceptIdentifier("OUTLINE");
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      public SQLStatement parseRefresh() {
          if (lexer.identifierEquals("REFRESH")) {
              lexer.nextToken();
          }
          SQLRefreshMaterializedViewStatement stmt = new SQLRefreshMaterializedViewStatement();
          stmt.setDbType(dbType);
  
          acceptIdentifier("MATERIALIZED");
  
          if (lexer.identifierEquals("CONCURRENTLY")) {
              lexer.nextToken();
              stmt.setConcurrently(true);
          }
          accept(Token.VIEW);
  
          stmt.setName(this.exprParser.name());
  
          if (lexer.token() == WITH) {
              lexer.nextToken();
  
              if (lexer.token() == IDENTIFIER && "NO".equalsIgnoreCase(lexer.stringVal())) {
                  lexer.nextToken();
                  stmt.setWithNoData(true);
              }
  
              if (lexer.token() == IDENTIFIER && "DATA".equalsIgnoreCase(lexer.stringVal())) {
                  lexer.nextToken();
                  stmt.setWithData(true);
              } else {
                  throw new ParserException("syntax error, expect DATA, actual " + lexer.token() + ", pos " + lexer.pos());
              }
          }
          return stmt;
      }
  
      public SQLStatement parseShowMaterializedView() {
          if (lexer.token() == Token.SHOW) {
              lexer.nextToken();
          }
          SQLShowMaterializedViewStatement stmt = new SQLShowMaterializedViewStatement();
          stmt.setDbType(dbType);
  
          acceptIdentifier("MATERIALIZED");
  
          acceptIdentifier("VIEWS");
  
          if (lexer.token() == Token.LIKE) {
              lexer.nextToken();
              stmt.setLike(this.exprParser.charExpr());
          }
  
          return stmt;
      }
  
      public SQLStatement parseDropMaterializedView() {
          if (lexer.token() == Token.DROP) {
              lexer.nextToken();
          }
          SQLDropMaterializedViewStatement stmt = new SQLDropMaterializedViewStatement();
          stmt.setDbType(dbType);
  
          acceptIdentifier("MATERIALIZED");
  
          accept(Token.VIEW);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          stmt.setName(this.exprParser.name());
          return stmt;
      }
  
      public SQLStatement parseDropOfflineModel() {
          if (lexer.token() == Token.DROP) {
              lexer.nextToken();
          }
          SQLDropOfflineModelStatement stmt = new SQLDropOfflineModelStatement();
          stmt.setDbType(dbType);
  
          acceptIdentifier("OFFLINEMODEL");
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          stmt.setName(this.exprParser.name());
          return stmt;
      }
  
      protected SQLStatement parseDropCatalog() {
          accept(Token.DROP);
  
          SQLDropCatalogStatement stmt = new SQLDropCatalogStatement(dbType);
  
          if (lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
              stmt.setExternal(true);
              lexer.nextToken();
          }
  
          acceptIdentifier("CATALOG");
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement parseDropRole() {
          accept(Token.DROP);
          acceptIdentifier("ROLE");
  
          SQLDropRoleStatement stmt = new SQLDropRoleStatement();
          stmt.setDbType(dbType);
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement parseDropTableGroup() {
          if (lexer.token == Token.DROP) {
              lexer.nextToken();
          }
  
          acceptIdentifier("TABLEGROUP");
  
          SQLDropTableGroupStatement stmt = new SQLDropTableGroupStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement parseDropServer() {
          if (lexer.token == Token.DROP) {
              lexer.nextToken();
          }
  
          acceptIdentifier("SERVER");
  
          SQLDropServerStatement stmt = new SQLDropServerStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement parseDropLogFileGroup() {
          if (lexer.token == Token.DROP) {
              lexer.nextToken();
          }
  
          acceptIdentifier("LOGFILE");
          accept(Token.GROUP);
  
          SQLDropLogFileGroupStatement stmt = new SQLDropLogFileGroupStatement();
          stmt.setDbType(dbType);
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
              lexer.nextToken();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
              }
              SQLExpr engine = this.exprParser.primary();
              stmt.setEngine(engine);
          }
  
          return stmt;
      }
  
      protected SQLStatement parseDropEvent() {
          if (lexer.token == Token.DROP) {
              lexer.nextToken();
          }
  
          acceptIdentifier("EVENT");
  
          SQLDropEventStatement stmt = new SQLDropEventStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement parseDropResource() {
          if (lexer.token == Token.DROP) {
              lexer.nextToken();
          }
  
          acceptIdentifier("RESOURCE");
  
          if (lexer.token == GROUP) {
              lexer.nextToken();
              SQLDropResourceGroupStatement stmt = new SQLDropResourceGroupStatement();
              stmt.setDbType(dbType);
  
              if (lexer.token == Token.IF) {
                  lexer.nextToken();
                  accept(Token.EXISTS);
                  stmt.setIfExists(true);
              }
  
              SQLName name = this.exprParser.name();
              stmt.setName(name);
  
              return stmt;
          }
  
          SQLDropResourceStatement stmt = new SQLDropResourceStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement alterTableSpace() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLStatement alterProcedure() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLStatement parseAlterFunction() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      public SQLStatement parseKill() {
          throw new ParserException("not supported. " + lexer.info());
      }
  
      public SQLStatement parseCase() {
          throw new ParserException("not supported. " + lexer.info());
      }
  
      public SQLStatement parseIf() {
          accept(Token.IF);
          SQLIfStatement stmt = new SQLIfStatement();
          stmt.setCondition(this.exprParser.expr());
          accept(Token.THEN);
  
          this.parseStatementList(stmt.getStatements(), -1, stmt);
  
          parseIfElse(stmt);
  
          accept(Token.END);
          accept(Token.IF);
          accept(Token.SEMI);
          stmt.setAfterSemi(true);
          return stmt;
      }
  
      protected void parseIfElse(SQLIfStatement stmt) {
          while (lexer.token == Token.ELSE || lexer.token == Token.ELSEIF) {
              boolean isConcatenated = lexer.token == Token.ELSEIF;
              if (lexer.nextIf(Token.ELSEIF) || (lexer.nextIf(Token.ELSE) && lexer.nextIf(Token.IF))) {
                  SQLIfStatement.ElseIf elseIf = new SQLIfStatement.ElseIf();
                  elseIf.setConcatenated(isConcatenated);
                  elseIf.setCondition(this.exprParser.expr());
                  elseIf.setParent(stmt);
                  accept(Token.THEN);
                  this.parseStatementList(elseIf.getStatements(), -1, elseIf);
                  stmt.getElseIfList().add(elseIf);
              } else {
                  SQLIfStatement.Else elseItem = new SQLIfStatement.Else();
                  this.parseStatementList(elseItem.getStatements(), -1, elseItem);
                  stmt.setElseItem(elseItem);
                  break;
              }
          }
      }
  
      public SQLWhileStatement parseWhile() {
          accept(Token.WHILE);
          SQLWhileStatement stmt = new SQLWhileStatement();
  
          stmt.setCondition(this.exprParser.expr());
  
          accept(Token.DO);
  
          this.parseStatementList(stmt.getStatements(), -1, stmt);
  
          accept(Token.END);
  
          accept(Token.WHILE);
  
          accept(SEMI);
  
          stmt.setAfterSemi(true);
  
          return stmt;
      }
  
      public SQLStatement parseLoop() {
          accept(Token.LOOP);
          SQLLoopStatement stmt = new SQLLoopStatement();
          this.parseStatementList(stmt.getStatements(), -1, stmt);
          accept(Token.END);
          accept(Token.LOOP);
          return stmt;
      }
  
      public SQLStatement parseDeclare() {
          throw new ParserException("not supported. " + lexer.info());
      }
  
      public SQLStatement parseContinue() {
          accept(Token.CONTINUE);
          return new SQLContinueStatement();
      }
  
      public SQLStatement parseRepeat() {
          throw new ParserException("not supported. " + lexer.info());
      }
  
      public SQLStatement parseLeave() {
          accept(LEAVE);
          return new SQLLeaveStatement();
      }
  
      public SQLStatement parseCache() {
          throw new ParserException("not supported. " + lexer.info());
      }
  
      public SQLStatement parsePurge() {
          acceptIdentifier("PURGE");
  
          if (lexer.token == Token.TABLE) {
              lexer.nextToken();
              SQLName tableName = this.exprParser.name();
  
              SQLPurgeTableStatement stmt = new SQLPurgeTableStatement();
              stmt.setTable(tableName);
  
              return stmt;
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.RECYCLEBIN)) {
              lexer.nextToken();
              SQLPurgeRecyclebinStatement stmt = new SQLPurgeRecyclebinStatement();
              return stmt;
          }
  
          if (lexer.token == ALL) {
              lexer.nextToken();
  
              SQLPurgeTableStatement stmt = new SQLPurgeTableStatement();
              stmt.setAll(true);
  
              if (lexer.token == LITERAL_INT) {
                  stmt.setCount(lexer.integerValue().intValue());
                  lexer.nextToken();
              }
              return stmt;
          }
  
          if (lexer.identifierEquals("TEMPORARY")) {
              lexer.nextToken();
              acceptIdentifier("OUTPUT");
              SQLPurgeTemporaryOutputStatement stmt = new SQLPurgeTemporaryOutputStatement();
              stmt.setName(
                      this.exprParser.name());
              return stmt;
          }
  
          SQLPurgeLogsStatement stmt = new SQLPurgeLogsStatement();
  
          if (lexer.token == Token.BINARY) {
              lexer.nextToken();
              stmt.setBinary(true);
          } else if (lexer.identifierEquals(FnvHash.Constants.MASTER)) {
              lexer.nextToken();
              stmt.setMaster(true);
          }
  
          if (lexer.token == ALL) {
              lexer.nextToken();
              stmt.setAll(true);
              return stmt;
          }
  
          acceptIdentifier("LOGS");
  
          if (lexer.token == Token.TO) {
              lexer.nextToken();
              SQLExpr to = this.exprParser.expr();
              stmt.setTo(to);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.BEFORE)) {
              lexer.nextToken();
              SQLExpr before = this.exprParser.expr();
              stmt.setBefore(before);
          }
  
          return stmt;
      }
  
      public SQLStatement parseReturn() {
          if (lexer.token == Token.RETURN
                  || lexer.identifierEquals("RETURN")) {
              lexer.nextToken();
          }
  
          SQLReturnStatement stmt = new SQLReturnStatement();
          if (lexer.token != Token.SEMI) {
              SQLExpr expr = this.exprParser.expr();
              stmt.setExpr(expr);
          }
  
          if (lexer.token == Token.SEMI) {
              accept(Token.SEMI);
              stmt.setAfterSemi(true);
          }
  
          return stmt;
      }
  
      public SQLStatement parseUpsert() {
          SQLInsertStatement insertStatement = new SQLInsertStatement();
  
          if (lexer.token == Token.UPSERT || lexer.identifierEquals("UPSERT")) {
              lexer.nextToken();
              insertStatement.setUpsert(true);
          }
  
          parseInsert0(insertStatement);
          return insertStatement;
      }
  
      public SQLStatement parseRollback() {
          lexer.nextToken();
  
          if (lexer.nextIfIdentifier("TRANSACTION")) {
              return new SQLRollbackTransactionStatement();
          }
  
          if (lexer.identifierEquals("WORK")) {
              lexer.nextToken();
          }
  
          SQLRollbackStatement stmt = new SQLRollbackStatement(getDbType());
  
          if (lexer.token == Token.TO) {
              lexer.nextToken();
  
              if (lexer.identifierEquals("SAVEPOINT") || lexer.token == Token.SAVEPOINT) {
                  lexer.nextToken();
              }
  
              stmt.setTo(this.exprParser.name());
          }
          return stmt;
      }
  
      public SQLStatement parseCommit() {
          acceptIdentifier("COMMIT");
          if (lexer.nextIfIdentifier("TRANSACTION")) {
              return new SQLCommitTransactionStatement();
          }
          return new SQLCommitStatement();
      }
  
      public SQLStatement parseShow() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      public SQLUseStatement parseUse() {
          accept(Token.USE);
          SQLUseStatement stmt = new SQLUseStatement(getDbType());
          stmt.setDatabase(this.exprParser.name());
          return stmt;
      }
  
      protected SQLExpr parseUser() {
          SQLExpr user = this.exprParser.expr();
          return user;
      }
  
      public SQLGrantStatement parseGrant() {
          accept(Token.GRANT);
          SQLGrantStatement stmt = new SQLGrantStatement(getDbType());
  
          parsePrivilege(stmt.getPrivileges(), stmt);
  
          if (lexer.token == Token.ON) {
              lexer.nextToken();
  
              switch (lexer.token) {
                  case PROCEDURE:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.PROCEDURE);
                      break;
                  case FUNCTION:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.FUNCTION);
                      break;
                  case TABLE:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.TABLE);
                      break;
                  case USER:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.USER);
                      break;
                  case DATABASE:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.DATABASE);
                      break;
                  case IDENTIFIER:
                      if (lexer.identifierEquals("SYSTEM")) {
                          lexer.nextToken();
                          stmt.setResourceType(SQLObjectType.SYSTEM);
                      }
                      break;
                  default:
                      break;
              }
  
              if (stmt.getResourceType() != null && lexer.token == Token.COLONCOLON) {
                  lexer.nextToken(); // sql server
              }
  
              SQLExpr expr;
              if (lexer.token == Token.DOT) {
                  expr = new SQLAllColumnExpr();
                  lexer.nextToken();
              } else {
                  expr = this.exprParser.expr();
              }
  
              if (stmt.getResourceType() == SQLObjectType.TABLE || stmt.getResourceType() == null) {
                  stmt.setResource(new SQLExprTableSource(expr));
              } else {
                  stmt.setResource(expr);
              }
          }
  
          if (lexer.token == Token.TO) {
              lexer.nextToken();
              for (; ; ) {
                  SQLExpr user = parseUser();
                  stmt.getUsers().add(user);
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
  
                  break;
              }
          }
  
          if (lexer.token == Token.WITH) {
              lexer.nextToken();
  
              if (lexer.token == Token.GRANT) {
                  lexer.nextToken();
                  acceptIdentifier("OPTION");
                  stmt.setWithGrantOption(true);
              }
  
              for (; ; ) {
                  if (lexer.identifierEquals("MAX_QUERIES_PER_HOUR")) {
                      lexer.nextToken();
                      stmt.setMaxQueriesPerHour(this.exprParser.primary());
                      continue;
                  }
  
                  if (lexer.identifierEquals("MAX_UPDATES_PER_HOUR")) {
                      lexer.nextToken();
                      stmt.setMaxUpdatesPerHour(this.exprParser.primary());
                      continue;
                  }
  
                  if (lexer.identifierEquals("MAX_CONNECTIONS_PER_HOUR")) {
                      lexer.nextToken();
                      stmt.setMaxConnectionsPerHour(this.exprParser.primary());
                      continue;
                  }
  
                  if (lexer.identifierEquals("MAX_USER_CONNECTIONS")) {
                      lexer.nextToken();
                      stmt.setMaxUserConnections(this.exprParser.primary());
                      continue;
                  }
  
                  break;
              }
          }
  
          if (lexer.identifierEquals("ADMIN")) {
              lexer.nextToken();
              acceptIdentifier("OPTION");
              stmt.setAdminOption(true);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.IDENTIFIED)) {
              lexer.nextToken();
              accept(Token.BY);
  
              if (lexer.identifierEquals("PASSWORD")) {
                  lexer.nextToken();
                  String password = lexer.stringVal();
                  accept(Token.LITERAL_CHARS);
                  stmt.setIdentifiedByPassword(password);
              } else {
                  stmt.setIdentifiedBy(this.exprParser.expr());
              }
          }
  
          if (lexer.token == Token.WITH) {
              lexer.nextToken();
              if (lexer.token == Token.GRANT) {
                  lexer.nextToken();
                  acceptIdentifier("OPTION");
                  stmt.setWithGrantOption(true);
              }
          }
  
          return stmt;
      }
  
      protected void parsePrivilege(List<SQLPrivilegeItem> privileges, SQLObject parent) {
          for (; ; ) {
              String privilege = null;
              if (lexer.token == Token.ALL) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("PRIVILEGES")) {
                      privilege = "ALL PRIVILEGES";
                      lexer.nextToken();
                  } else {
                      privilege = "ALL";
                  }
              } else if (lexer.token == Token.SELECT) {
                  privilege = "SELECT";
                  lexer.nextToken();
              } else if (lexer.token == Token.UPDATE) {
                  privilege = "UPDATE";
                  lexer.nextToken();
              } else if (lexer.token == Token.DELETE) {
                  privilege = "DELETE";
                  lexer.nextToken();
              } else if (lexer.token == Token.INSERT) {
                  privilege = "INSERT";
                  lexer.nextToken();
              } else if (lexer.token == Token.INDEX) {
                  lexer.nextToken();
                  privilege = "INDEX";
              } else if (lexer.token == Token.TRIGGER) {
                  lexer.nextToken();
                  privilege = "TRIGGER";
              } else if (lexer.token == Token.REFERENCES) {
                  privilege = "REFERENCES";
                  lexer.nextToken();
              } else if (lexer.token == Token.DESC) {
                  privilege = "DESCRIBE";
                  lexer.nextToken();
              } else if (lexer.token == TRUNCATE) {
                  privilege = "TRUNCATE";
                  lexer.nextToken();
              } else if (lexer.token == Token.CREATE) {
                  lexer.nextToken();
  
                  if (lexer.token == Token.TABLE) {
                      privilege = "CREATE TABLE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.SESSION) {
                      privilege = "CREATE SESSION";
                      lexer.nextToken();
                  } else if (lexer.token == Token.TABLESPACE) {
                      privilege = "CREATE TABLESPACE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.USER) {
                      privilege = "CREATE USER";
                      lexer.nextToken();
                  } else if (lexer.token == Token.VIEW) {
                      privilege = "CREATE VIEW";
                      lexer.nextToken();
                  } else if (lexer.token == Token.PROCEDURE) {
                      privilege = "CREATE PROCEDURE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.SEQUENCE) {
                      privilege = "CREATE SEQUENCE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.ANY) {
                      lexer.nextToken();
  
                      if (lexer.token == Token.TABLE) {
                          lexer.nextToken();
                          privilege = "CREATE ANY TABLE";
                      } else if (lexer.identifierEquals("MATERIALIZED")) {
                          lexer.nextToken();
                          accept(Token.VIEW);
                          privilege = "CREATE ANY MATERIALIZED VIEW";
                      } else {
                          throw new ParserException("TODO : " + lexer.token + " " + lexer.stringVal());
                      }
                  } else if (lexer.identifierEquals("SYNONYM")) {
                      privilege = "CREATE SYNONYM";
                      lexer.nextToken();
                  } else if (lexer.identifierEquals("ROUTINE")) {
                      privilege = "CREATE ROUTINE";
                      lexer.nextToken();
                  } else if (lexer.identifierEquals("TEMPORARY")) {
                      lexer.nextToken();
                      acceptIdentifier("TABLES");
                      privilege = "CREATE TEMPORARY TABLES";
                  } else if (lexer.token == Token.ON) {
                      privilege = "CREATE";
                  } else if (lexer.token == Token.COMMA) {
                      privilege = "CREATE";
                  } else {
                      throw new ParserException("TODO : " + lexer.token + " " + lexer.stringVal());
                  }
              } else if (lexer.token == Token.ALTER) {
                  lexer.nextToken();
                  if (lexer.token == Token.TABLE) {
                      privilege = "ALTER TABLE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.SESSION) {
                      privilege = "ALTER SESSION";
                      lexer.nextToken();
                  } else if (lexer.identifierEquals(FnvHash.Constants.ROUTINE)) {
                      privilege = "ALTER ROUTINE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.ANY) {
                      lexer.nextToken();
  
                      if (lexer.token == Token.TABLE) {
                          lexer.nextToken();
                          privilege = "ALTER ANY TABLE";
                      } else if (lexer.identifierEquals("MATERIALIZED")) {
                          lexer.nextToken();
                          accept(Token.VIEW);
                          privilege = "ALTER ANY MATERIALIZED VIEW";
                      } else {
                          throw new ParserException("TODO : " + lexer.token + " " + lexer.stringVal());
                      }
                  } else if (lexer.token == Token.ON || lexer.token == Token.COMMA) {
                      privilege = "ALTER";
                  } else {
                      throw new ParserException("TODO : " + lexer.token + " " + lexer.stringVal());
                  }
              } else if (lexer.token == Token.DROP) {
                  lexer.nextToken();
                  if (lexer.token == Token.DROP) {
                      privilege = "DROP TABLE";
                      lexer.nextToken();
                  } else if (lexer.token == Token.SESSION) {
                      privilege = "DROP SESSION";
                      lexer.nextToken();
                  } else if (lexer.token == Token.ANY) {
                      lexer.nextToken();
  
                      if (lexer.token == Token.TABLE) {
                          lexer.nextToken();
                          privilege = "DROP ANY TABLE";
                      } else if (lexer.identifierEquals("MATERIALIZED")) {
                          lexer.nextToken();
                          accept(Token.VIEW);
                          privilege = "DROP ANY MATERIALIZED VIEW";
                      } else {
                          throw new ParserException("TODO : " + lexer.token + " " + lexer.stringVal());
                      }
                  } else {
                      privilege = "DROP";
                  }
              } else if (lexer.identifierEquals("USAGE")) {
                  privilege = "USAGE";
                  lexer.nextToken();
              } else if (lexer.identifierEquals("EXECUTE")) {
                  privilege = "EXECUTE";
                  lexer.nextToken();
              } else if (lexer.identifierEquals("PROXY")) {
                  privilege = "PROXY";
                  lexer.nextToken();
              } else if (lexer.identifierEquals("QUERY")) {
                  lexer.nextToken();
                  acceptIdentifier("REWRITE");
                  privilege = "QUERY REWRITE";
              } else if (lexer.identifierEquals("GLOBAL")) {
                  lexer.nextToken();
                  acceptIdentifier("QUERY");
                  acceptIdentifier("REWRITE");
                  privilege = "GLOBAL QUERY REWRITE";
              } else if (lexer.identifierEquals("INHERIT")) {
                  lexer.nextToken();
                  acceptIdentifier("PRIVILEGES");
                  privilege = "INHERIT PRIVILEGES";
              } else if (lexer.identifierEquals("EVENT")) {
                  lexer.nextToken();
                  privilege = "EVENT";
              } else if (lexer.identifierEquals("FILE")) {
                  lexer.nextToken();
                  privilege = "FILE";
              } else if (lexer.identifierEquals("DESCRIBE")) {
                  lexer.nextToken();
                  privilege = "DESCRIBE";
              } else if (lexer.token == Token.GRANT) {
                  lexer.nextToken();
                  acceptIdentifier("OPTION");
  
                  if (lexer.token == Token.FOR) {
                      privilege = "GRANT OPTION FOR";
                      lexer.nextToken();
                  } else {
                      privilege = "GRANT OPTION";
                  }
              } else if (lexer.token == Token.LOCK) {
                  lexer.nextToken();
                  acceptIdentifier("TABLES");
                  privilege = "LOCK TABLES";
              } else if (lexer.identifierEquals("PROCESS")) {
                  lexer.nextToken();
                  privilege = "PROCESS";
              } else if (lexer.identifierEquals("RELOAD")) {
                  lexer.nextToken();
                  privilege = "RELOAD";
              } else if (lexer.identifierEquals("CONNECT")) {
                  privilege = "CONNECT";
                  lexer.nextToken();
              } else if (lexer.identifierEquals("RESOURCE")) {
                  lexer.nextToken();
                  privilege = "RESOURCE";
              } else if (lexer.token == Token.CONNECT) {
                  lexer.nextToken();
                  privilege = "CONNECT";
              } else if (lexer.identifierEquals("REPLICATION")) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("SLAVE")) {
                      lexer.nextToken();
                      privilege = "REPLICATION SLAVE";
                  } else {
                      acceptIdentifier("CLIENT");
                      privilege = "REPLICATION CLIENT";
                  }
              } else if (lexer.token == Token.SHOW) {
                  lexer.nextToken();
  
                  if (lexer.token == Token.VIEW) {
                      lexer.nextToken();
                      privilege = "SHOW VIEW";
                  } else if (lexer.identifierEquals("DATABASES")) {
                      acceptIdentifier("DATABASES");
                      privilege = "SHOW DATABASES";
                  } else {
                      privilege = "SHOW";
                  }
              } else if (lexer.identifierEquals("SHUTDOWN")) {
                  lexer.nextToken();
                  privilege = "SHUTDOWN";
              } else if (lexer.identifierEquals("SUPER")) {
                  lexer.nextToken();
                  privilege = "SUPER";
  
              } else if (lexer.identifierEquals("CONTROL")) { // sqlserver
                  lexer.nextToken();
                  privilege = "CONTROL";
              } else if (lexer.identifierEquals("IMPERSONATE")) { // sqlserver
                  lexer.nextToken();
                  privilege = "IMPERSONATE";
              } else if (lexer.identifierEquals("LOAD")) { // sqlserver
                  lexer.nextToken();
                  if (lexer.identifierEquals("DATA")) {
                      lexer.nextToken();
                      privilege = "LOAD DATA";
                  }
              } else if (lexer.identifierEquals("DUMP")) { // sqlserver
                  lexer.nextToken();
                  if (lexer.identifierEquals("DATA")) {
                      lexer.nextToken();
                      privilege = "DUMP DATA";
                  }
              }
  
              if (privilege != null) {
                  SQLExpr expr = new SQLIdentifierExpr(privilege);
  
                  SQLPrivilegeItem privilegeItem = new SQLPrivilegeItem();
                  privilegeItem.setAction(expr);
  
                  if (lexer.token == Token.LPAREN) {
                      lexer.nextToken();
                      for (; ; ) {
                          privilegeItem.getColumns().add(this.exprParser.name());
  
                          if (lexer.token() == Token.COMMA) {
                              lexer.nextToken();
                              continue;
                          }
                          break;
                      }
                      accept(Token.RPAREN);
                  }
  
                  expr.setParent(parent);
                  privileges.add(privilegeItem);
              }
  
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
      }
  
      public SQLRevokeStatement parseRevoke() {
          accept(Token.REVOKE);
  
          SQLRevokeStatement stmt = new SQLRevokeStatement(dbType);
  
          if (lexer.token == Token.GRANT) {
              lexer.nextToken();
              acceptIdentifier("OPTION");
  
              stmt.setGrantOption(true);
  
              if (lexer.token == Token.FOR) {
                  lexer.nextToken();
              }
          }
  
          parsePrivilege(stmt.getPrivileges(), stmt);
  
          if (lexer.token == Token.ON) {
              lexer.nextToken();
  
              switch (lexer.token) {
                  case PROCEDURE:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.PROCEDURE);
                      break;
                  case FUNCTION:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.FUNCTION);
                      break;
                  case TABLE:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.TABLE);
                      break;
                  case USER:
                      lexer.nextToken();
                      stmt.setResourceType(SQLObjectType.USER);
                      break;
                  case IDENTIFIER:
                      if (lexer.identifierEquals("SYSTEM")) {
                          lexer.nextToken();
                          stmt.setResourceType(SQLObjectType.SYSTEM);
                      } else if (lexer.identifierEquals("PROJECT")) {
                          lexer.nextToken();
                          stmt.setResourceType(SQLObjectType.PROJECT);
                      }
  
                      break;
                  default:
                      break;
              }
  
              SQLExpr expr = this.exprParser.expr();
              if (stmt.getResourceType() == SQLObjectType.TABLE || stmt.getResourceType() == null) {
                  stmt.setResource(new SQLExprTableSource(expr));
              } else {
                  stmt.setResource(expr);
              }
          }
  
          if (lexer.token == Token.FROM) {
              lexer.nextToken();
              for (; ; ) {
                  if (lexer.token() == Token.USER && dialectFeatureEnabled(ParseRevokeFromUser)) {
                      lexer.nextToken();
                  }
                  SQLExpr user = parseUser();
                  stmt.getUsers().add(user);
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
                  break;
              }
          }
  
          return stmt;
      }
  
      public SQLStatement parseSavePoint() {
          acceptIdentifier("SAVEPOINT");
          SQLSavePointStatement stmt = new SQLSavePointStatement(getDbType());
          stmt.setName(this.exprParser.name());
          return stmt;
      }
  
      public SQLStatement parseReleaseSavePoint() {
          acceptIdentifier("RELEASE");
          acceptIdentifier("SAVEPOINT");
          SQLReleaseSavePointStatement stmt = new SQLReleaseSavePointStatement(getDbType());
          stmt.setName(this.exprParser.name());
          return stmt;
      }
  
      public final SQLStatement parseAlter() {
          List<String> comments = null;
          if (lexer.isKeepComments() && lexer.hasComment()) {
              comments = lexer.readAndResetComments();
          }
  
          Lexer.SavePoint mark = lexer.mark();
          accept(Token.ALTER);
  
          SQLStatement stmt;
          switch (lexer.token) {
              case TABLE:
                  lexer.reset(mark);
                  stmt = alterTable();
                  break;
              case VIEW:
                  lexer.reset(mark);
                  stmt = alterView();
                  break;
              case INDEX:
                  lexer.reset(mark);
                  stmt = alterIndex();
                  break;
              case DATABASE:
                  lexer.reset(mark);
                  stmt = alterDatabase();
                  break;
              case USER:
                  stmt = alterUser();
                  break;
              case SCHEMA:
                  lexer.reset(mark);
                  stmt = alterSchema();
                  break;
              case FUNCTION:
                  lexer.reset(mark);
                  stmt = parseAlterFunction();
                  break;
              case PROCEDURE:
                  lexer.reset(mark);
                  stmt = alterProcedure();
                  break;
              case TABLESPACE:
                  lexer.reset(mark);
                  stmt = alterTableSpace();
                  break;
              case SEQUENCE:
                  lexer.reset(mark);
                  stmt = alterSequence();
                  break;
              case FULLTEXT:
                  lexer.reset(mark);
                  stmt = alterFullText();
                  break;
              case IDENTIFIER:
                  if (lexer.identifierEquals(FnvHash.Constants.MATERIALIZED)) {
                      stmt = alterMaterialized();
                  } else if (lexer.identifierEquals(Constants.RESOURCE)) {
                      lexer.reset(mark);
                      stmt = alterResourceGroup();
                  } else if (lexer.identifierEquals(Constants.EVENT)) {
                      lexer.reset(mark);
                      stmt = alterEvent();
                  } else {
                      stmt = alterRest(mark);
                  }
                  break;
              default:
                  stmt = alterRest(mark);
                  break;
          }
  
          if (comments != null) {
              stmt.addBeforeComment(comments);
          }
  
          return stmt;
      }
  
      protected SQLStatement alterRest(Lexer.SavePoint mark) {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLStatement alterEvent() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      public SQLStatement alterUser() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLStatement alterView() {
          accept(ALTER);
          accept(VIEW);
          SQLName viewName = this.exprParser.name();
  
          if (lexer.nextIfIdentifier(Constants.RENAME)) {
              SQLAlterViewRenameStatement stmt = new SQLAlterViewRenameStatement();
              stmt.setName(viewName);
              accept(Token.TO);
              stmt.setTo(
                      exprParser.name()
              );
  
              if (lexer.identifierEquals("CHANGEOWNER")) {
                  lexer.nextToken();
                  accept(TO);
  
                  stmt.setChangeOwnerTo(
                          exprParser.name()
                  );
              }
  
              return stmt;
          }
  
          SQLAlterViewStatement alterView = new SQLAlterViewStatement(dbType);
          alterView.setTableSource(new SQLExprTableSource(viewName));
  
          for (; ; ) {
              if (lexer.nextIf(SET)) {
                  acceptIdentifier("TBLPROPERTIES");
                  this.exprParser.parseAssignItem(alterView.getOptions(), alterView);
                  continue;
              }
  
              if (lexer.nextIfIdentifier("UNSET")) {
                  alterViewUnset(alterView);
                  continue;
              }
              break;
          }
  
          if (lexer.nextIf(AS)) {
              if (lexer.token() == SELECT) {
                  alterView.setSubQuery(
                          createSQLSelectParser()
                                  .select()
                  );
                  return alterView;
              }
          }
          return alterView;
      }
  
      protected SQLAlterTableStatement createSQLAlterTableStatement() {
          return new SQLAlterTableStatement(dbType);
      }
  
      protected SQLAlterStatement alterTable() {
          SQLAlterTableStatement stmt = createSQLAlterTableStatement();
  
          accept(ALTER);
          alterTableBefore(stmt);
          accept(TABLE);
  
          if (lexer.token == ONLY) {
              lexer.nextToken();
              stmt.setOnly(true);
          }
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          stmt.setName(this.exprParser.name());
  
          return alterTableAfterName(stmt);
      }
  
      protected void alterTableBefore(SQLAlterTableStatement stmt) {
      }
  
      protected boolean alterTableAfterNameRest(SQLAlterTableStatement stmt) {
          return true;
      }
      protected SQLAlterStatement alterTableAfterName(SQLAlterTableStatement stmt) {
          _for:
          for (; ; ) {
              switch (lexer.token) {
                  case DROP:
                      parseAlterDrop(stmt);
                      break;
                  case DISABLE:
                      alterTableDisable(stmt);
                      break;
                  case ENABLE:
                      alterTableEnable(stmt);
                      break;
                  case ALTER:
                      alterTableAlter(stmt);
                      break;
                  case DELETE:
                      alterTableDelete(stmt);
                      break;
                  case WITH:
                      alterTableWith(stmt);
                      break;
                  case SET:
                      alterTableSet(stmt);
                      break;
                  case PARTITION:
                      alterTablePartition(stmt);
                      break;
                  case TRUNCATE:
                      alterTableTruncate(stmt);
                      break;
                  case REPLACE:
                      stmt.addItem(
                              parseAlterTableReplaceColumn()
                      );
                      break;
                  default:
                      if (lexer.identifierEquals(Constants.ADD)) {
                          alterTableAdd(stmt);
                      } else if (lexer.nextIfIdentifier("UNSET")) {
                          alterTableUnset(stmt);
                      } else if (lexer.identifierEquals("VALIDATE")) {
                          lexer.nextToken();
                          accept(Token.CONSTRAINT);
                          SQLAlterTableValidateConstraint item = new SQLAlterTableValidateConstraint();
                          item.setConstraintName(this.exprParser.name());
                          stmt.addItem(item);
                      } else if (lexer.identifierEquals("ATTACH")) {
                          alterTableAttach(stmt);
                      } else if (lexer.identifierEquals("DETACH")) {
                          alterTableDetach(stmt);
                      } else if (lexer.identifierEquals(Constants.CHANGE)) {
                          alterTableChange(stmt);
                      } else if (lexer.identifierEquals(Constants.EXCHANGE)) {
                          alterTableExchange(stmt);
                      } else if (lexer.identifierEquals("RENAME")) {
                          stmt.addItem(parseAlterTableRename());
                      } else if (lexer.identifierEquals("TOUCH")) {
                          alterTableTouch(stmt);
                      } else if (lexer.identifierEquals(Constants.ARCHIVE)) {
                          alterTableAchive(stmt);
                      } else if (lexer.identifierEquals(Constants.UNARCHIVE)) {
                          alterTableUnarchive(stmt);
                      } else if (lexer.identifierEquals(Constants.SUBPARTITION_AVAILABLE_PARTITION_NUM)) {
                          lexer.nextToken();
                          accept(Token.EQ);
                          SQLIntegerExpr num = this.exprParser.integerExpr();
                          SQLAlterTableSubpartitionAvailablePartitionNum item = new SQLAlterTableSubpartitionAvailablePartitionNum();
                          item.setNumber(num);
                          stmt.addItem(item);
                      } else if (lexer.identifierEquals("CHANGEOWNER")) {
                          alterTableChangeOwner(stmt);
                      } else if ((stmt.getClusteredBy().size() > 0 || stmt.getSortedBy().size() > 0) && lexer.token == Token.INTO) {
                          lexer.nextToken();
  
                          int num;
                          if (lexer.token() == Token.LITERAL_INT) {
                              num = lexer.integerValue().intValue();
                              lexer.nextToken();
                          } else {
                              throw new ParserException("into buckets must be integer. " + lexer.info());
                          }
  
                          if (lexer.identifierEquals(Constants.BUCKETS)) {
                              stmt.setBuckets(num);
                              lexer.nextToken();
                          } else {
                              acceptIdentifier("SHARDS");
                              stmt.setShards(num);
                          }
                      } else {
                          if (alterTableAfterNameRest(stmt)) {
                              break _for;
                          }
                      }
                      break;
              }
          }
  
          return stmt;
      }
  
      private void alterViewUnset(SQLAlterViewStatement alterView) {
          acceptIdentifier("TBLPROPERTIES");
          accept(LPAREN);
          this.exprParser.names(alterView.getUnsetOptions(), alterView);
          accept(RPAREN);
      }
  
      protected void alterTableUnset(SQLAlterTableStatement stmt) {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected void alterTableSorted(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          accept(Token.BY);
  
          accept(Token.LPAREN);
          for (; ; ) {
              SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
              stmt.addSortedByItem(item);
              if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
          accept(Token.RPAREN);
      }
  
      protected void alterTableMerge(SQLAlterTableStatement stmt) {
          lexer.nextToken();
  
          boolean ifExists = false;
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              ifExists = true;
          }
  
          if (lexer.token == PARTITION) {
              SQLAlterTableMergePartition item = new SQLAlterTableMergePartition();
              for (; ; ) {
                  item.addPartition(
                          this.getExprParser().parsePartitionSpec()
                  );
                  if (lexer.token == COMMA) {
                      lexer.nextToken();
                  } else {
                      break;
                  }
              }
  
              accept(OVERWRITE);
              item.setOverwritePartition(
                      this.getExprParser().parsePartitionSpec()
              );
  
              if (ifExists) {
                  item.setIfExists(true);
              }
  
              if (lexer.identifierEquals(Constants.PURGE)) {
                  lexer.nextToken();
                  item.setPurge(true);
              }
  
              stmt.addItem(item);
          } else {
              acceptIdentifier("SMALLFILES");
              stmt.setMergeSmallFiles(true);
          }
      }
  
      private void alterTableUnarchive(SQLAlterTableStatement stmt) {
          lexer.nextToken();
  
          accept(Token.PARTITION);
  
          SQLAlterTableUnarchivePartition item = new SQLAlterTableUnarchivePartition();
          accept(Token.LPAREN);
          parseAssignItems(item.getPartitions(), item, false);
          accept(Token.RPAREN);
  
          stmt.addItem(item);
      }
  
      private void alterTableAchive(SQLAlterTableStatement stmt) {
          lexer.nextToken();
  
          if (lexer.token == PARTITION) {
              lexer.nextToken();
  
              SQLAlterTableArchivePartition item = new SQLAlterTableArchivePartition();
              accept(Token.LPAREN);
              parseAssignItems(item.getPartitions(), item, false);
              accept(Token.RPAREN);
              stmt.addItem(item);
          }
      }
  
      protected void alterTableOwner(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          accept(TO);
          SQLName name = this.exprParser.name();
          SQLAlterTableChangeOwner changeOwner = new SQLAlterTableChangeOwner();
          changeOwner.setOwner(name);
  
          stmt.addItem(changeOwner);
      }
  
      private void alterTableTouch(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          SQLAlterTableTouch item = new SQLAlterTableTouch();
  
          if (lexer.token == Token.PARTITION) {
              lexer.nextToken();
  
              accept(Token.LPAREN);
              parseAssignItems(item.getPartition(), item);
              accept(Token.RPAREN);
          }
  
          stmt.addItem(item);
      }
  
      protected void alterTableTruncate(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          if (lexer.nextIf(Token.PARTITION)) {
              SQLAlterTableTruncatePartition item = new SQLAlterTableTruncatePartition();
              if (lexer.nextIf(Token.FOR)) {
                  accept(Token.LPAREN);
                  do {
                      item.addPartitionValue(exprParser.expr());
                  }
                  while (lexer.nextIf(Token.COMMA));
                  accept(Token.RPAREN);
              } else {
                  item.addPartition(exprParser.name());
              }
              stmt.addItem(item);
          }
      }
  
      private void alterTablePartition(SQLAlterTableStatement stmt) {
          lexer.nextToken();
  
          SQLAlterTableRenamePartition renamePartition = new SQLAlterTableRenamePartition();
  
          accept(Token.LPAREN);
  
          parseAssignItems(renamePartition.getPartition(), renamePartition);
  
          accept(Token.RPAREN);
  
          if (lexer.token == Token.ENABLE) {
              lexer.nextToken();
              if (lexer.identifierEquals("LIFECYCLE")) {
                  lexer.nextToken();
              }
  
              SQLAlterTableEnableLifecycle enableLifeCycle = new SQLAlterTableEnableLifecycle();
              for (SQLAssignItem condition : renamePartition.getPartition()) {
                  enableLifeCycle.getPartition().add(condition);
                  condition.setParent(enableLifeCycle);
              }
              stmt.addItem(enableLifeCycle);
  
              return;
          }
  
          if (lexer.token == Token.DISABLE) {
              lexer.nextToken();
              if (lexer.identifierEquals("LIFECYCLE")) {
                  lexer.nextToken();
              }
  
              SQLAlterTableDisableLifecycle disableLifeCycle = new SQLAlterTableDisableLifecycle();
              for (SQLAssignItem condition : renamePartition.getPartition()) {
                  disableLifeCycle.getPartition().add(condition);
                  condition.setParent(disableLifeCycle);
              }
              stmt.addItem(disableLifeCycle);
  
              return;
          }
  
          if (lexer.identifierEquals("MERGE")) {
              alterTablePartitionMerge(stmt, renamePartition);
              return;
          }
          if (lexer.token == Token.SET) {
              alterTablePartitionSet(stmt, renamePartition);
              return;
          }
  
          if (lexer.identifierEquals("ARCHIVE")) {
              alterTablePartitionArchive(stmt, renamePartition);
              return;
          }
  
          if (lexer.identifierEquals(Constants.ADD)) {
              alterTableAdd(stmt);
              return;
          }
  
          acceptIdentifier("RENAME");
          accept(Token.TO);
          accept(Token.PARTITION);
  
          accept(Token.LPAREN);
  
          parseAssignItems(renamePartition.getTo(), renamePartition);
  
          accept(Token.RPAREN);
  
          stmt.addItem(renamePartition);
      }
  
      private static void alterTablePartitionArchive(SQLAlterTableStatement stmt,
                                                     SQLAlterTableRenamePartition renamePartition) {
          SQLAlterTableArchive archive = new SQLAlterTableArchive();
          for (SQLAssignItem condition : renamePartition.getPartition()) {
              archive.getPartition().add(condition);
              condition.setParent(archive);
          }
          stmt.addItem(archive);
      }
  
      private static void alterTablePartitionMerge(SQLAlterTableStatement stmt,
                                                   SQLAlterTableRenamePartition renamePartition) {
          SQLAlterTablePartition alterTablePartition = new SQLAlterTablePartition();
          for (SQLAssignItem condition : renamePartition.getPartition()) {
              alterTablePartition.getPartition().add(condition);
              condition.setParent(alterTablePartition);
          }
          stmt.addItem(alterTablePartition);
      }
  
      private void alterTablePartitionSet(SQLAlterTableStatement stmt, SQLAlterTableRenamePartition renamePartition) {
          SQLAlterTablePartitionSetProperties alterTablePartition = new SQLAlterTablePartitionSetProperties();
          for (SQLAssignItem condition : renamePartition.getPartition()) {
              alterTablePartition.getPartition().add(condition);
              condition.setParent(alterTablePartition);
          }
  
          accept(SET);
          if (lexer.nextIfIdentifier("LOCATION")) {
              alterTablePartition.setLocation(
                      exprParser.expr()
              );
          } else {
              acceptIdentifier("PARTITIONPROPERTIES");
              accept(LPAREN);
              this.parseAssignItems(alterTablePartition.getPartitionProperties(), alterTablePartition);
              accept(RPAREN);
          }
          stmt.addItem(alterTablePartition);
      }
  
      protected boolean alterTableSetRest(SQLAlterTableStatement stmt) {
          return true;
      }
      protected void alterTableSet(SQLAlterTableStatement stmt) {
          accept(SET);
          if (lexer.token == TABLESPACE) {
              lexer.nextToken();
              SQLAlterTableSetTableSpace setTableSapce = new SQLAlterTableSetTableSpace();
              setTableSapce.setTableSpaceName(this.exprParser.primary());
              stmt.addItem(setTableSapce);
          } else if (lexer.token == Token.SCHEMA) {
              lexer.nextToken();
              SQLAlterTableSetSchema setSchema = new SQLAlterTableSetSchema();
              setSchema.setSchemaName(this.exprParser.primary());
              stmt.addItem(setSchema);
          } else if (lexer.token == Token.COMMENT) {
              lexer.nextToken();
              SQLAlterTableSetComment setComment = new SQLAlterTableSetComment();
              setComment.setComment(this.exprParser.primary());
              stmt.addItem(setComment);
          } else if (lexer.identifierEquals(Constants.LIFECYCLE)) {
              lexer.nextToken();
              SQLAlterTableSetLifecycle setLifecycle = new SQLAlterTableSetLifecycle();
              setLifecycle.setLifecycle(this.exprParser.primary());
              stmt.addItem(setLifecycle);
          } else if (lexer.identifierEquals(Constants.LOCATION)) {
              lexer.nextToken();
              SQLAlterTableSetLocation setLocation = new SQLAlterTableSetLocation();
              setLocation.setLocation(this.exprParser.primary());
              stmt.addItem(setLocation);
          } else if (lexer.identifierEquals(Constants.TBLPROPERTIES)) {
              alterTableSetTblProperties(stmt);
          } else if (lexer.identifierEquals("SERDEPROPERTIES")) {
              alterTableSetSerdeProperties(stmt);
          } else if (lexer.identifierEquals(FnvHash.Constants.SERDE)) {
              lexer.nextToken();
              SQLAlterTableSetSerde setTableSerde = new SQLAlterTableSetSerde();
              setTableSerde.setSerde(this.exprParser.primary());
  
              if (lexer.token == Token.WITH) {
                  lexer.nextToken();
                  if (lexer.identifierEquals(FnvHash.Constants.SERDEPROPERTIES)) {
                      lexer.nextToken();
                      accept(Token.LPAREN);
                      for (; ; ) {
                          SQLAssignItem item = this.exprParser.parseAssignItem();
                          setTableSerde.addSerdeProperties(item);
                          if (lexer.token == Token.COMMA) {
                              lexer.nextToken();
                              continue;
                          }
                          break;
                      }
                      accept(Token.RPAREN);
                  }
              }
              stmt.addItem(setTableSerde);
          } else {
              if (alterTableSetRest(stmt)) {
                  throw new ParserException("TODO " + lexer.info());
              }
          }
      }
  
      protected void alterTableChangeOwner(SQLAlterTableStatement stmt) {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected void alterTableSetSerdeProperties(SQLAlterTableStatement stmt) {
          acceptIdentifier("SERDEPROPERTIES");
          SQLAlterTableSetSerdeProperties setOption = new SQLAlterTableSetSerdeProperties();
          accept(Token.LPAREN);
          for (; ; ) {
              SQLAssignItem item = this.exprParser.parseAssignItem();
              setOption.addSerdeProperties(item);
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
          accept(Token.RPAREN);
          stmt.addItem(setOption);
      }
  
      protected void alterTableSetTblProperties(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          SQLAlterTableSetOption setOption = new SQLAlterTableSetOption();
          accept(Token.LPAREN);
          for (; ; ) {
              SQLAssignItem item = this.exprParser.parseAssignItem();
              setOption.addOption(item);
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
          accept(Token.RPAREN);
          stmt.addItem(setOption);
      }
  
      protected void alterTableWith(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          acceptIdentifier("NOCHECK");
          acceptIdentifier("ADD");
          SQLConstraint check = this.exprParser.parseConstraint();
  
          SQLAlterTableAddConstraint addCheck = new SQLAlterTableAddConstraint();
          addCheck.setWithNoCheck(true);
          addCheck.setConstraint(check);
          stmt.addItem(addCheck);
      }
  
      private void alterTableExchange(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          accept(Token.PARTITION);
  
          SQLAlterTableExchangePartition item = new SQLAlterTableExchangePartition();
  
          accept(Token.LPAREN);
          for (; ; ) {
              SQLExpr partition = this.exprParser.name();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
                  SQLExpr value = this.exprParser.primary();
                  partition = new SQLAssignItem(partition, value);
              }
  
              item.addPartition(partition);
  
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
  
              break;
          }
          accept(Token.RPAREN);
  
          accept(Token.WITH);
          accept(Token.TABLE);
          SQLName table = this.exprParser.name();
          item.setTable(table);
  
          if (lexer.token == Token.WITH) {
              lexer.nextToken();
              acceptIdentifier("VALIDATION");
              item.setValidation(true);
          } else if (lexer.identifierEquals(Constants.WITHOUT)) {
              lexer.nextToken();
              acceptIdentifier("VALIDATION");
              item.setValidation(false);
          }
  
          stmt.addItem(item);
      }
  
      private void alterTableChange(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          if (lexer.token == Token.COLUMN) {
              lexer.nextToken();
          }
          SQLName columnName = this.exprParser.name();
  
          if (lexer.identifierEquals("RENAME")) {
              lexer.nextToken();
              accept(Token.TO);
              SQLName toName = this.exprParser.name();
              SQLAlterTableRenameColumn renameColumn = new SQLAlterTableRenameColumn();
  
              renameColumn.setColumn(columnName);
              renameColumn.setTo(toName);
  
              stmt.addItem(renameColumn);
          } else if (lexer.token == Token.COMMENT) {
              lexer.nextToken();
  
              SQLExpr comment;
              if (lexer.token == Token.LITERAL_ALIAS) {
                  String alias = 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);
                  lexer.nextToken();
              } else {
                  comment = this.exprParser.primary();
              }
  
              SQLColumnDefinition column = new SQLColumnDefinition();
              column.setDbType(dbType);
              column.setName(columnName);
              column.setComment(comment);
  
              SQLAlterTableAlterColumn changeColumn = new SQLAlterTableAlterColumn();
  
              changeColumn.setColumn(column);
  
              stmt.addItem(changeColumn);
          } else if (lexer.token == Token.NULL) {
              lexer.nextToken();
              stmt.addItem(
                      new SQLAlterTableAddConstraint(
                              new SQLNullConstraint()
                      )
              );
          } else if (lexer.nextIfIdentifier(Constants.FIRST)) {
              SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
              alterColumn.setOriginColumn(columnName);
              alterColumn.setFirst(true);
              stmt.addItem(alterColumn);
          } else {
              SQLColumnDefinition column = this.exprParser.parseColumn();
  
              SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
              alterColumn.setColumn(column);
              alterColumn.setOriginColumn(columnName);
  
              if (lexer.identifierEquals(Constants.AFTER)) {
                  lexer.nextToken();
                  alterColumn.setAfter(
                          this.exprParser.name()
                  );
              } else if (lexer.identifierEquals(Constants.FIRST)) {
                  lexer.nextToken();
                  alterColumn.setFirst(true);
              }
  
              stmt.addItem(alterColumn);
          }
      }
  
      private void alterTableDelete(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          if (lexer.token == Token.WHERE) {
              lexer.nextToken();
  
              SQLAlterTableDeleteByCondition alterColumn = new SQLAlterTableDeleteByCondition();
              alterColumn.setWhere(this.exprParser.expr());
              stmt.addItem(alterColumn);
          } else {
              throw new ParserException("TODO " + lexer.info());
          }
      }
  
      protected void alterTableAlterComma() {
      }
      private void alterTableAlter(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          if (lexer.token == Token.COLUMN) {
              SQLAlterTableAlterColumn alterColumn = parseAlterColumn();
              stmt.addItem(alterColumn);
              alterTableAlterComma();
          } else if (lexer.token == Token.LITERAL_ALIAS) {
              SQLAlterTableAlterColumn alterColumn = parseAlterColumn();
              stmt.addItem(alterColumn);
          } else {
              throw new ParserException("TODO " + lexer.info());
          }
      }
  
      private void alterTableAttach(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          accept(Token.PARTITION);
          SQLAlterTableAttachPartition item = new SQLAlterTableAttachPartition();
          item.setPartitionName(this.exprParser.name());
          if (lexer.token() == DEFAULT) {
              accept(Token.DEFAULT);
              item.setDefaultFlag(true);
          }
          stmt.addItem(item);
      }
  
      private void alterTableDetach(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          accept(Token.PARTITION);
          SQLAlterTableDetachPartition item = new SQLAlterTableDetachPartition();
          item.setPartitionName(this.exprParser.name());
          if (lexer.identifierEquals("CONCURRENTLY")) {
              lexer.nextToken();
              item.setConcurrently(true);
          }
          if (lexer.identifierEquals("FINALIZE")) {
              lexer.nextToken();
              item.setFinalize(true);
          }
          stmt.addItem(item);
      }
  
      private void alterTableEnable(SQLAlterTableStatement stmt) {
          lexer.nextToken();
          if (lexer.token == Token.CONSTRAINT) {
              lexer.nextToken();
              SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
              item.setConstraintName(this.exprParser.name());
              stmt.addItem(item);
          } else if (lexer.identifierEquals("LIFECYCLE")) {
              lexer.nextToken();
              SQLAlterTableEnableLifecycle item = new SQLAlterTableEnableLifecycle();
              stmt.addItem(item);
          } else {
              acceptIdentifier("KEYS");
              SQLAlterTableEnableKeys item = new SQLAlterTableEnableKeys();
              stmt.addItem(item);
          }
      }
  
      private void alterTableDisable(SQLAlterTableStatement stmt) {
          lexer.nextToken();
  
          if (lexer.token == Token.CONSTRAINT) {
              lexer.nextToken();
              SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
              item.setConstraintName(this.exprParser.name());
              stmt.addItem(item);
          } else if (lexer.identifierEquals("LIFECYCLE")) {
              lexer.nextToken();
              SQLAlterTableDisableLifecycle item = new SQLAlterTableDisableLifecycle();
              stmt.addItem(item);
          } else {
              acceptIdentifier("KEYS");
              SQLAlterTableDisableKeys item = new SQLAlterTableDisableKeys();
              stmt.addItem(item);
          }
      }
  
      protected void alterTableAdd(SQLAlterTableStatement stmt) {
          Lexer.SavePoint mark = lexer.mark();
  
          acceptIdentifier("ADD");
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
          }
  
          switch (lexer.token) {
              case COLUMN:
                  lexer.nextToken();
                  parseAlterTableAddColumn(stmt);
                  break;
              case INDEX:
              case KEY:
              case FULLTEXT:
                  lexer.reset(mark);
                  alterTableAddIndex(stmt);
                  break;
              case PRIMARY:
              case UNIQUE:
              case CHECK:
              case FOREIGN:
              case DEFAULT:
              case CONSTRAINT: {
                  lexer.reset(mark);
                  alterTableAddConstraint(stmt);
                  break;
              }
              case PARTITION: {
                  lexer.reset(mark);
                  alterTableAddPartition(stmt);
                  break;
              }
              default:
                  lexer.reset(mark);
                  alterTableAddRest(stmt);
          }
      }
  
      protected void alterTableAddIndex(SQLAlterTableStatement stmt) {
          acceptIdentifier("ADD");
          SQLAlterTableAddIndex item = new SQLAlterTableAddIndex();
          this.exprParser.parseIndex(item.getIndexDefinition());
          stmt.addItem(item);
      }
  
      protected void alterTableAddConstraint(SQLAlterTableStatement stmt) {
          acceptIdentifier("ADD");
          SQLConstraint constraint = this.exprParser.parseConstraint();
          SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(constraint);
          if (lexer.identifierEquals("NO")) {
              lexer.nextToken();
              acceptIdentifier("INHERIT");
              item.setNoInherit(true);
          }
          if (lexer.token == Token.NOT) {
              lexer.nextToken();
              acceptIdentifier("VALID");
              item.setNotValid(true);
          }
          stmt.addItem(item);
      }
  
      protected void alterTableAddRestSpecific(SQLAlterTableStatement stmt) {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected void alterTableAddRest(SQLAlterTableStatement stmt) {
          acceptIdentifier("ADD");
          if (lexer.token == Token.IDENTIFIER) {
              SQLAlterTableAddColumn item = parseAlterTableAddColumn();
              stmt.addItem(item);
              return;
          }
          alterTableAddRestSpecific(stmt);
      }
  
      protected void alterTableAddPartition(SQLAlterTableStatement stmt) {
          acceptIdentifier("ADD");
  
          boolean ifNotExists = false;
          if (lexer.nextIf(Token.IF)) {
              accept(Token.NOT);
              accept(Token.EXISTS);
              ifNotExists = true;
          }
  
          for (; ; ) {
              lexer.nextToken();
              SQLAlterTableAddPartition addPartition = new SQLAlterTableAddPartition();
  
              addPartition.setIfNotExists(ifNotExists);
  
              accept(Token.LPAREN);
  
              parseAssignItems(addPartition.getPartitions(), addPartition, false);
  
              accept(Token.RPAREN);
  
              if (lexer.identifierEquals(Constants.LOCATION)) {
                  lexer.nextToken();
                  SQLExpr location = this.exprParser.primary();
                  addPartition.setLocation(location);
              }
  
              stmt.addItem(addPartition);
  
              if (lexer.token == Token.PARTITION) {
                  continue;
              }
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
  
                  if (lexer.identifierEquals("ADD") || lexer.token == Token.PARTITION) {
                      continue;
                  }
              }
  
              break;
          }
      }
  
      protected SQLStatement alterFullText() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLStatement alterDatabase() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLStatement alterSchema() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLAlterTableItem parseAlterTableRename() {
          acceptIdentifier("RENAME");
  
          if (lexer.token == Token.COLUMN) {
              lexer.nextToken();
              SQLAlterTableRenameColumn renameColumn = new SQLAlterTableRenameColumn();
              renameColumn.setColumn(this.exprParser.name());
              accept(Token.TO);
              renameColumn.setTo(this.exprParser.name());
              return renameColumn;
          } else if (lexer.token == Token.CONSTRAINT) {
              lexer.nextToken();
              SQLAlterTableRenameConstraint renameConstraint = new SQLAlterTableRenameConstraint();
              renameConstraint.setConstraint(this.exprParser.name());
              accept(Token.TO);
              renameConstraint.setTo(this.exprParser.name());
              return renameConstraint;
          }
  
          if (lexer.token == Token.TO) {
              lexer.nextToken();
              SQLAlterTableRename item = new SQLAlterTableRename();
              item.setTo(this.exprParser.name());
              return item;
          }
  
          throw new ParserException("TODO " + lexer.info());
      }
  
      protected SQLAlterTableAlterColumn parseAlterColumn() {
          lexer.nextToken();
          SQLColumnDefinition column = this.exprParser.parseColumn();
  
          SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
          alterColumn.setColumn(column);
          return alterColumn;
      }
  
      public void parseAlterDropRest(SQLAlterTableStatement stmt, SQLAlterTableDropColumnItem item) {
          boolean paren = false;
          if (lexer.token == LPAREN) {
              lexer.nextToken();
              paren = true;
          }
          this.exprParser.names(item.getColumns());
          if (paren) {
              accept(RPAREN);
          }
  
          if (lexer.token == Token.CASCADE) {
              item.setCascade(true);
              lexer.nextToken();
          }
          if (RESTRICT == lexer.token) {
              item.setRestrict(true);
              lexer.nextToken();
          }
          stmt.addItem(item);
      }
      public void parseAlterDrop(SQLAlterTableStatement stmt) {
          lexer.nextToken();
  
          boolean ifExists = false;
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
  
              accept(Token.EXISTS);
              ifExists = true;
          }
  
          if (lexer.token == Token.CONSTRAINT) {
              lexer.nextToken();
              SQLAlterTableDropConstraint item = new SQLAlterTableDropConstraint();
              if (lexer.token == Token.IF) {
                  lexer.nextToken();
                  accept(Token.EXISTS);
                  item.setIfExists(true);
              }
              item.setConstraintName(this.exprParser.name());
              if (lexer.token == RESTRICT) {
                  lexer.nextToken();
                  item.setRestrict(true);
              } else if (lexer.token == CASCADE) {
                  lexer.nextToken();
                  item.setCascade(true);
              }
              stmt.addItem(item);
          } else if (lexer.token == Token.COLUMN || lexer.identifierEquals(Constants.COLUMNS)) {
              lexer.nextToken();
              SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
              if (lexer.token == Token.IF) {
                  lexer.nextToken();
                  accept(Token.EXISTS);
                  item.setIfExists(true);
              }
  
              parseAlterDropRest(stmt, item);
          } else if (lexer.token == Token.LITERAL_ALIAS) {
              SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
              this.exprParser.names(item.getColumns());
  
              if (lexer.token == Token.CASCADE) {
                  item.setCascade(true);
                  lexer.nextToken();
              }
  
              stmt.addItem(item);
          } else if (lexer.token == Token.PARTITION) {
              {
                  SQLAlterTableDropPartition dropPartition = parseAlterTableDropPartition(ifExists);
                  stmt.addItem(dropPartition);
              }
  
              while (lexer.token == COMMA) {
                  lexer.nextToken();
                  Lexer.SavePoint mark = lexer.mark();
                  if (lexer.token == Token.PARTITION) {
                      SQLAlterTableDropPartition dropPartition = parseAlterTableDropPartition(ifExists);
                      stmt.addItem(dropPartition);
                  } else {
                      lexer.reset(mark);
                  }
              }
  
          } else if (lexer.token == Token.INDEX) {
              lexer.nextToken();
              SQLName indexName = this.exprParser.name();
              SQLAlterTableDropIndex item = new SQLAlterTableDropIndex();
              item.setIndexName(indexName);
              stmt.addItem(item);
          } else if (lexer.token == Token.PRIMARY) {
              lexer.nextToken();
              accept(Token.KEY);
              SQLAlterTableDropPrimaryKey item = new SQLAlterTableDropPrimaryKey();
              stmt.addItem(item);
          } else {
              throw new ParserException("TODO " + lexer.info());
          }
      }
  
      protected SQLAlterTableDropPartition parseAlterTableDropPartition(boolean ifExists) {
          lexer.nextToken();
          SQLAlterTableDropPartition dropPartition = new SQLAlterTableDropPartition();
  
          dropPartition.setIfExists(ifExists);
  
          if (lexer.token == Token.LPAREN) {
              accept(Token.LPAREN);
              this.exprParser.exprList(dropPartition.getPartitions(), dropPartition);
              accept(Token.RPAREN);
  
              if (lexer.identifierEquals("PURGE")) {
                  lexer.nextToken();
                  dropPartition.setPurge(true);
              }
          } else {
              for (; ; ) {
                  SQLExpr partition = this.exprParser.expr();
                  dropPartition.addPartition(partition);
                  if (lexer.token == COMMA) {
                      lexer.nextToken();
                      continue;
                  }
                  break;
              }
              dropPartition.getAttributes().put("SIMPLE", true);
          }
  
          return dropPartition;
      }
  
      protected SQLAlterTableDropSubpartition parseAlterTableDropSubpartition() {
          lexer.nextToken();
          SQLAlterTableDropSubpartition item = new SQLAlterTableDropSubpartition();
  
          if (lexer.token() == Token.LITERAL_INT) {
              for (; ; ) {
                  item.getPartitionIds().add(this.exprParser.integerExpr());
                  String pidStr = lexer.stringVal();
                  accept(Token.VARIANT);
                  String s = pidStr.replaceAll(":", "");
                  if (StringUtils.isEmpty(s)) {
                      item.getSubpartitionIds().add(exprParser.integerExpr());
                  } else {
                      item.getSubpartitionIds().add(new SQLIntegerExpr(Integer.valueOf(s)));
                  }
  
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
  
                  break;
              }
          }
  
          return item;
      }
  
      public SQLStatement parseRename() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      public SQLStatement parseList() {
          if (lexer.identifierEquals(Constants.LIST)) {
              this.lexer.nextToken();
              if (lexer.identifierEquals(Constants.RESOURCE)) {
                  this.lexer.nextToken();
                  if (lexer.identifierEquals(Constants.GROUP) || lexer.token == GROUP) {
                      this.lexer.nextToken();
                      return new SQLListResourceGroupStatement();
                  }
              }
          }
  
          return null;
      }
  
      protected SQLDropTableStatement parseDropTable(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          SQLDropTableStatement stmt = new SQLDropTableStatement(getDbType());
  
          if (lexer.identifierEquals(FnvHash.Constants.TEMPORARY)) {
              lexer.nextToken();
              stmt.setTemporary(true);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.PARTITIONED)) {
              lexer.nextToken();
              stmt.setDropPartition(true);
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
              lexer.nextToken();
              stmt.setExternal(true);
          }
  
          if (lexer.token == TABLE) {
              lexer.nextToken();
          } else if (lexer.identifierEquals(Constants.TABLES) && dialectFeatureEnabled(ParseDropTableTables)) {
              lexer.nextToken();
          } else {
              throw new ParserException("expected token: TABLE.");
          }
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          for (; ; ) {
              SQLName name = this.exprParser.name();
              SQLExprTableSource tab;
              if (lexer.token == Token.AS) {
                  lexer.nextToken();
                  String alias = this.exprParser.name().getSimpleName();
                  tab = new SQLExprTableSource(name, alias);
              } else {
                  tab = new SQLExprTableSource(name);
              }
  
              stmt.addPartition(tab);
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
  
          for (; ; ) {
              if (lexer.identifierEquals("RESTRICT")) {
                  lexer.nextToken();
                  stmt.setRestrict(true);
                  continue;
              }
  
              if (lexer.identifierEquals(FnvHash.Constants.CASCADE) || lexer.token == CASCADE) {
                  lexer.nextToken();
                  stmt.setCascade(true);
  
                  if (lexer.identifierEquals("CONSTRAINTS")) { // for oracle
                      lexer.nextToken();
                  }
  
                  continue;
              }
  
              if (lexer.token == Token.PURGE || lexer.identifierEquals("PURGE")) {
                  lexer.nextToken();
                  stmt.setPurge(true);
                  continue;
              }
  
              break;
          }
  
          if (stmt.isDropPartition()) {
              if (lexer.token() == Token.WHERE) {
                  lexer.nextToken();
                  SQLExpr where = this.exprParser.expr();
                  stmt.setWhere(where);
              }
  
          }
  
          return stmt;
      }
  
      protected SQLDropSequenceStatement parseDropSequence(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          lexer.nextToken();
  
          SQLName name = this.exprParser.name();
  
          SQLDropSequenceStatement stmt = new SQLDropSequenceStatement(getDbType());
          stmt.setName(name);
          return stmt;
      }
  
      protected SQLDropTriggerStatement parseDropTrigger(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          lexer.nextToken();
          SQLDropTriggerStatement stmt = new SQLDropTriggerStatement(getDbType());
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
  
          stmt.setName(name);
          return stmt;
      }
  
      protected SQLDropViewStatement parseDropView(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          SQLDropViewStatement stmt = new SQLDropViewStatement(getDbType());
  
          accept(Token.VIEW);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          for (; ; ) {
              SQLName name = this.exprParser.name();
              stmt.addPartition(new SQLExprTableSource(name));
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
  
          if (lexer.identifierEquals("RESTRICT")) {
              lexer.nextToken();
              stmt.setRestrict(true);
          } else if (lexer.identifierEquals(FnvHash.Constants.CASCADE) || lexer.token == CASCADE) {
              lexer.nextToken();
  
              if (lexer.identifierEquals("CONSTRAINTS")) { // for oracle
                  lexer.nextToken();
              }
  
              stmt.setCascade(true);
          }
  
          return stmt;
      }
  
      protected SQLDropStatement parseDropSchema(boolean physical) {
          SQLDropStatement stmt = parseDropDatabaseOrSchema(false);
          if (physical) {
              ((SQLDropDatabaseStatement) stmt).setPhysical(physical);
          }
          return stmt;
      }
  
      protected SQLDropStatement parseDropDatabaseOrSchema(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          SQLDropDatabaseStatement stmt = new SQLDropDatabaseStatement(getDbType());
  
          if (lexer.token == Token.SCHEMA) {
              lexer.nextToken();
          } else {
              accept(Token.DATABASE);
          }
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setDatabase(name);
  
          if (lexer.identifierEquals(FnvHash.Constants.RESTRICT)) {
              lexer.nextToken();
              stmt.setRestrict(true);
          } else if (lexer.token == Token.CASCADE || lexer.identifierEquals(FnvHash.Constants.CASCADE)) {
              lexer.nextToken();
              stmt.setCascade(true);
          } else {
              stmt.setCascade(false);
          }
  
          return stmt;
      }
  
      protected void parserParameters(List<SQLParameter> parameters, SQLObject parent) {
          if (lexer.token() == Token.RPAREN) {
              return;
          }
  
          for (; ; ) {
              SQLParameter parameter = new SQLParameter();
  
              if (lexer.token() == Token.CURSOR) {
                  SQLDataTypeImpl dataType = new SQLDataTypeImpl();
                  lexer.computeRowAndColumn(dataType);
  
                  lexer.nextToken();
  
                  parameter.setName(this.exprParser.name());
  
                  accept(Token.IS);
                  SQLSelect select = this.createSQLSelectParser().select();
  
                  dataType.setName("CURSOR");
                  parameter.setDataType(dataType);
  
                  parameter.setDefaultValue(new SQLQueryExpr(select));
  
              } else if (lexer.token() == Token.IN || lexer.token() == Token.OUT || lexer.token() == Token.INOUT) {
                  if (lexer.token() == Token.IN) {
                      parameter.setParamType(ParameterType.IN);
                  } else if (lexer.token() == Token.OUT) {
                      parameter.setParamType(ParameterType.OUT);
                  } else if (lexer.token() == Token.INOUT) {
                      parameter.setParamType(ParameterType.INOUT);
                  }
                  lexer.nextToken();
  
                  parameter.setName(this.exprParser.name());
  
                  parameter.setDataType(this.exprParser.parseDataType());
              } else {
                  // default parameter type is in
                  parameter.setParamType(ParameterType.DEFAULT);
                  parameter.setName(this.exprParser.name());
                  if (lexer.token() != Token.COMMA && lexer.token() != Token.RPAREN) {
                      parameter.setDataType(this.exprParser.parseDataType());
                      if (lexer.token() == Token.COLONEQ) {
                          lexer.nextToken();
                          parameter.setDefaultValue(this.exprParser.expr());
                      }
                  }
              }
  
              parameters.add(parameter);
              if (lexer.token() == Token.COMMA || lexer.token() == Token.SEMI) {
                  lexer.nextToken();
              }
  
              if (lexer.token() != Token.BEGIN && lexer.token() != Token.RPAREN) {
                  continue;
              }
  
              break;
          }
      }
  
      protected SQLDropFunctionStatement parseDropFunction(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          SQLDropFunctionStatement stmt = new SQLDropFunctionStatement(getDbType());
  
          accept(Token.FUNCTION);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  //在这里要判断是否带函数声明
          if (lexer.token() == Token.LPAREN) { // match "("
              lexer.nextToken();
              stmt.setParameters(new ArrayList<>());
              parserParameters(stmt.getParameters(), stmt);
              accept(Token.RPAREN); // match ")"
          }
          return stmt;
      }
  
      protected SQLDropTableSpaceStatement parseDropTablespace(boolean acceptDrop) {
          SQLDropTableSpaceStatement stmt = new SQLDropTableSpaceStatement(getDbType());
  
          if (lexer.isKeepComments() && lexer.hasComment()) {
              stmt.addBeforeComment(lexer.readAndResetComments());
          }
  
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          accept(Token.TABLESPACE);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
              lexer.nextToken();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
              }
              SQLExpr engine = this.exprParser.primary();
              stmt.setEngine(engine);
          }
  
          return stmt;
      }
  
      protected SQLDropProcedureStatement parseDropProcedure(boolean acceptDrop) {
          if (acceptDrop) {
              accept(Token.DROP);
          }
  
          SQLDropProcedureStatement stmt = new SQLDropProcedureStatement(getDbType());
  
          accept(Token.PROCEDURE);
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          return stmt;
      }
  
      public SQLStatement parseTruncate() {
          accept(Token.TRUNCATE);
          if (!lexer.nextIf(Token.TABLE)) {
              lexer.nextIfIdentifier("TABLE");
          }
          SQLTruncateStatement stmt = new SQLTruncateStatement(getDbType());
  
          if (lexer.nextIf(Token.ONLY)) {
              stmt.setOnly(true);
          }
  
          if (lexer.nextIf(Token.IF)) {
              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 (lexer.token == Token.PARTITION) {
              lexer.nextToken();
  
              if (lexer.token == LPAREN) {
                  accept(Token.LPAREN);
                  for (; ; ) {
                      SQLAssignItem item = this.exprParser.parseAssignItem();
                      item.setParent(stmt);
                      stmt.getPartitions().add(item);
                      if (lexer.token == Token.COMMA) {
                          lexer.nextToken();
                          continue;
                      }
                      break;
                  }
                  accept(Token.RPAREN);
              } else { // for adb
                  if (lexer.token == ALL) {
                      lexer.nextToken();
                      stmt.setPartitionAll(true);
                  } else {
                      for (; ; ) {
                          stmt.getPartitionsForADB().add(exprParser.integerExpr());
                          if (lexer.token == COMMA) {
                              lexer.nextToken();
                              continue;
                          }
                          break;
                      }
                  }
              }
          }
  
          for (; ; ) {
              if (lexer.token == Token.PURGE) {
                  lexer.nextToken();
  
                  if (lexer.identifierEquals("SNAPSHOT")) {
                      lexer.nextToken();
                      acceptIdentifier("LOG");
                      stmt.setPurgeSnapshotLog(true);
                  } else {
                      throw new ParserException("TODO : " + lexer.token + " " + lexer.stringVal());
                  }
                  continue;
              }
  
              if (lexer.token == Token.RESTART) {
                  lexer.nextToken();
                  accept(Token.IDENTITY);
                  stmt.setRestartIdentity(Boolean.TRUE);
                  continue;
              } else if (lexer.token == Token.SHARE) {
                  lexer.nextToken();
                  accept(Token.IDENTITY);
                  stmt.setRestartIdentity(Boolean.FALSE);
                  continue;
              }
  
              if (lexer.token == Token.CASCADE) {
                  lexer.nextToken();
                  stmt.setCascade(Boolean.TRUE);
                  continue;
              } else if (lexer.token == Token.RESTRICT) {
                  lexer.nextToken();
                  stmt.setCascade(Boolean.FALSE);
                  continue;
              }
  
              if (lexer.token == Token.DROP) {
                  lexer.nextToken();
                  acceptIdentifier("STORAGE");
                  stmt.setDropStorage(true);
                  continue;
              }
  
              if (lexer.identifierEquals("REUSE")) {
                  lexer.nextToken();
                  acceptIdentifier("STORAGE");
                  stmt.setReuseStorage(true);
                  continue;
              }
  
              if (lexer.identifierEquals("IGNORE")) {
                  lexer.nextToken();
                  accept(Token.DELETE);
                  acceptIdentifier("TRIGGERS");
                  stmt.setIgnoreDeleteTriggers(true);
                  continue;
              }
  
              if (lexer.identifierEquals("RESTRICT")) {
                  lexer.nextToken();
                  accept(Token.WHEN);
                  accept(Token.DELETE);
                  acceptIdentifier("TRIGGERS");
                  stmt.setRestrictWhenDeleteTriggers(true);
                  continue;
              }
  
              if (lexer.token == Token.CONTINUE) {
                  lexer.nextToken();
                  accept(Token.IDENTITY);
                  continue;
              }
  
              if (lexer.identifierEquals("IMMEDIATE")) {
                  lexer.nextToken();
                  stmt.setImmediate(true);
                  continue;
              }
  
              break;
          }
  
          return stmt;
      }
  
      public SQLStatement parseInsert() {
          SQLInsertStatement stmt = new SQLInsertStatement();
  
          if (lexer.token == Token.INSERT) {
              accept(Token.INSERT);
          }
  
          parseInsert0(stmt);
          return stmt;
      }
  
      protected void parseInsert0(SQLInsertInto insertStatement) {
          parseInsert0(insertStatement, true);
      }
  
      /**
       * Impala dml support multiple place hint.
       * First hint_clause isInsert is true, second hint_clause isInsert is false.
       * ***************************************
       * [with_clause]
       *   INSERT [hint_clause] { INTO | OVERWRITE } [TABLE] table_name
       *   [(column_list)]
       *   [ PARTITION (partition_clause)]
       * {
       *     [hint_clause] select_statement
       *   | VALUES (value [, value ...]) [, (value [, value ...]) ...]
       * }
       * ***************************************
       */
      protected void parseInsert0Hints(SQLInsertInto insertStatement, boolean isInsert) {
      }
  
      protected void parseInsertOverwrite(SQLInsertInto insertStatement) {
          insertStatement.setOverwrite(true);
      }
      protected void parseInsert0(SQLInsertInto insertStatement, boolean acceptSubQuery) {
          if (lexer.nextIf(OVERWRITE) || lexer.nextIfIdentifier(Constants.OVERWRITE)) {
              parseInsertOverwrite(insertStatement);
          } else if (lexer.nextIf(INTO)) {
              insertStatement.setOverwrite(false);
          }
  
          SQLName tableName = this.exprParser.name();
          insertStatement.setTableName(tableName);
  
          if (lexer.token == Token.LITERAL_ALIAS) {
              insertStatement.setAlias(tableAlias());
          }
  
          parseInsert0Hints(insertStatement, false);
  
          if (lexer.token == Token.IDENTIFIER) {
              insertStatement.setAlias(lexer.stringVal());
              lexer.nextToken();
          }
  
          if (lexer.token == Token.PARTITION) {
              lexer.nextToken();
              accept(Token.LPAREN);
              for (; ; ) {
                  SQLAssignItem ptExpr = new SQLAssignItem();
                  ptExpr.setTarget(this.exprParser.name());
                  if (lexer.token == Token.EQ || lexer.token == Token.EQEQ) {
                      lexer.nextTokenValue();
                      SQLExpr ptValue = this.exprParser.expr();
                      ptExpr.setValue(ptValue);
                  }
                  insertStatement.addPartition(ptExpr);
                  if (!(lexer.token == (Token.COMMA))) {
                      break;
                  } else {
                      lexer.nextToken();
                  }
              }
              accept(Token.RPAREN);
          }
  
          if (lexer.token == (Token.LPAREN)) {
              lexer.nextToken();
              parseInsertColumns(insertStatement);
              accept(Token.RPAREN);
          }
  
          if (lexer.token == Token.VALUES) {
              lexer.nextToken();
              for (; ; ) {
                  if (lexer.token == Token.LPAREN) {
                      lexer.nextToken();
  
                      SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
                      this.exprParser.exprList(values.getValues(), values);
                      insertStatement.addValueCause(values);
                      accept(Token.RPAREN);
                  } else { // oracle
                      SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
                      SQLExpr value = this.exprParser.expr();
                      values.addValue(value);
                      insertStatement.addValueCause(values);
                  }
  
                  if (lexer.token == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  } else {
                      break;
                  }
              }
          } else if (acceptSubQuery && (lexer.token == WITH || lexer.token == Token.SELECT || lexer.token == Token.LPAREN)) {
              SQLSelect select = this.createSQLSelectParser().select();
              insertStatement.setQuery(select);
          } else if (lexer.identifierEquals(FnvHash.Constants.VALUE)) {
              throw new ParserException("'values' expected, but 'value'. " + lexer.info());
          }
      }
  
      protected void parseInsertColumns(SQLInsertInto insert) {
          this.exprParser.exprList(insert.getColumns(), insert);
      }
  
      public boolean parseStatementListDialect(List<SQLStatement> statementList) {
          return false;
      }
  
      public SQLDropUserStatement parseDropUser() {
          accept(Token.USER);
  
          SQLDropUserStatement stmt = new SQLDropUserStatement(getDbType());
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
          for (; ; ) {
              if (lexer.token == Token.IF) { // skip for adb
                  lexer.nextToken();
                  accept(Token.EXISTS);
              }
              SQLExpr expr = this.exprParser.expr();
              stmt.addUser(expr);
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
  
          if (lexer.token == CASCADE) {
              lexer.nextToken();
              stmt.setCascade(true);
          }
  
          return stmt;
      }
  
      public SQLStatement parseDropIndex() {
          accept(Token.INDEX);
          SQLDropIndexStatement stmt = new SQLDropIndexStatement(getDbType());
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.EXISTS);
              stmt.setIfExists(true);
          }
  
          stmt.setIndexName(this.exprParser.name());
  
          if (lexer.token == Token.ON) {
              lexer.nextToken();
              stmt.setTableName(this.exprParser.name());
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.ALGORITHM)) {
              lexer.nextToken();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
              }
              SQLExpr algorithm = this.exprParser.primary();
              stmt.setAlgorithm(algorithm);
          }
  
          if (lexer.token == Token.LOCK) {
              lexer.nextToken();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
              }
              SQLExpr option = this.exprParser.primary();
              stmt.setLockOption(option);
          }
          // for mysql
          return stmt;
      }
  
      public SQLCallStatement parseCall() {
          boolean brace = false;
          if (lexer.token == Token.LBRACE) {
              lexer.nextToken();
              brace = true;
          }
  
          SQLCallStatement stmt = new SQLCallStatement(getDbType());
  
          if (lexer.token == Token.QUES) {
              lexer.nextToken();
              accept(Token.EQ);
              stmt.setOutParameter(new SQLVariantRefExpr("?"));
          }
  
          acceptIdentifier("CALL");
  
          stmt.setProcedureName(exprParser.name());
  
          if (lexer.token == Token.LPAREN) {
              lexer.nextToken();
              exprParser.exprList(stmt.getParameters(), stmt);
              accept(Token.RPAREN);
          }
  
          if (brace) {
              accept(Token.RBRACE);
              stmt.setBrace(true);
          }
  
          return stmt;
      }
  
      public SQLStatement parseSet() {
          accept(Token.SET);
          SQLSetStatement stmt = new SQLSetStatement(getDbType());
  
          parseAssignItems(stmt.getItems(), stmt);
  
          return stmt;
      }
  
      public void parseAssignItems(List<? super SQLAssignItem> items, SQLObject parent) {
          parseAssignItems(items, parent, true);
      }
  
      public void parseAssignItems(List<? super SQLAssignItem> items, SQLObject parent, boolean variant) {
          exprParser.parseAssignItems(items, parent, variant);
      }
  
      public SQLPartitionRef parsePartitionRef() {
          accept(Token.PARTITION);
  
          SQLPartitionRef partitionRef = new SQLPartitionRef();
  
          accept(Token.LPAREN);
  
          for (; ; ) {
              SQLIdentifierExpr name = (SQLIdentifierExpr) this.exprParser.name();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
                  SQLExpr value = this.exprParser.expr();
                  partitionRef.addItem(name, value);
              } else {
                  partitionRef.addItem(new SQLPartitionRef.Item(name));
              }
  
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              } else {
                  break;
              }
          }
  
          accept(Token.RPAREN);
  
          return partitionRef;
      }
  
      public SQLStatement parseCreatePackage() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      public SQLStatement parseCreate() {
          Lexer.SavePoint mark = lexer.markOut();
          accept(Token.CREATE);
  
          createOptionSkip();
  
          switch (lexer.token) {
              case FOREIGN:
              case TABLE:
                  lexer.reset(mark);
                  return parseCreateTable();
              case INDEX:
              case UNIQUE:
                  lexer.reset(mark);
                  return parseCreateIndex();
              case SEQUENCE:
                  lexer.reset(mark);
                  return parseCreateSequence();
              case DATABASE:
                  lexer.nextToken();
                  if (lexer.identifierEquals("LINK")) {
                      lexer.reset(mark);
                      return parseCreateDbLink();
                  }
                  lexer.reset(mark);
                  return parseCreateDatabase();
              case SCHEMA:
                  lexer.reset(mark);
                  return parseCreateSchema();
              case USER:
                  lexer.reset(mark);
                  return parseCreateUser();
              case FUNCTION:
                  lexer.reset(mark);
                  return parseCreateFunction();
              case VIEW:
                  lexer.reset(mark);
                  return parseCreateView();
              case PROCEDURE:
                  lexer.reset(mark);
                  return parseCreateProcedure();
              case TRIGGER:
                  lexer.reset(mark);
                  return parseCreateTrigger();
              case TABLESPACE:
                  lexer.reset(mark);
                  return parseCreateTableSpace();
              default:
                  if (lexer.identifierEquals(Constants.PUBLIC)) {
                      lexer.nextToken();
                      if (lexer.identifierEquals("SYNONYM")) {
                          lexer.reset(mark);
                          return parseCreateSynonym();
                      } else {
                          lexer.reset(mark);
                          return parseCreateDbLink();
                      }
                  } else if (lexer.identifierEquals("SHARE")) {
                      lexer.reset(mark);
                      return parseCreateDbLink();
                  } else if (lexer.identifierEquals("SYNONYM")) {
                      lexer.reset(mark);
                      return parseCreateSynonym();
                  } else if (lexer.identifierEquals(Constants.BITMAP)) {
                      lexer.reset(mark);
                      return parseCreateIndex();
                  } else if (lexer.identifierEquals(Constants.MATERIALIZED)) {
                      lexer.reset(mark);
                      return parseCreateMaterializedView();
                  } else if (lexer.identifierEquals(Constants.TYPE)) {
                      lexer.reset(mark);
                      return parseCreateType();
                  } else if (lexer.identifierEquals(Constants.TABLEGROUP)) {
                      lexer.reset(mark);
                      return parseCreateTableGroup();
                  } else if (lexer.identifierEquals(Constants.SQL) && dialectFeatureEnabled(ParseCreateSql)) {
                      lexer.reset(mark);
                      return parseCreateFunction();
                  } else if (lexer.identifierEquals(Constants.DIMENSION)) {
                      lexer.reset(mark);
                      return parseCreateTable();
                  } else if (lexer.identifierEquals(Constants.ROLE)) {
                      lexer.reset(mark);
                      return parseCreateRole();
                  } else if (lexer.identifierEquals(Constants.RESOURCE)) {
                      lexer.reset(mark);
                      return createResource();
                  } else if (lexer.identifierEquals(Constants.PACKAGE)) {
                      lexer.reset(mark);
                      return parseCreatePackage();
                  } else if (lexer.identifierEquals(Constants.EVENT)) {
                      lexer.reset(mark);
                      return parseCreateEvent();
                  } else if (lexer.identifierEquals(Constants.SERVER)) {
                      lexer.reset(mark);
                      return parseCreateServer();
                  } else if (lexer.identifierEquals(Constants.OUTLINE)) {
                      lexer.reset(mark);
                      return parseCreateOutline();
                  } else if (lexer.identifierEquals(Constants.EXTERNAL)) {
                      lexer.nextToken();
                      if (lexer.identifierEquals(Constants.CATALOG)) {
                          lexer.reset(mark);
                          return parseCreateExternalCatalog();
                      }
                      if (lexer.token == TABLE) {
                          lexer.reset(mark);
                          return parseCreateTable();
                      }
                      throw new ParserException("parse create error, " + lexer.info());
                  } else if (lexer.identifierEquals(FnvHash.Constants.SCAN)) {
                      lexer.reset(mark);
                      return parseCreateScan();
                  } else if (lexer.identifierEquals(FnvHash.Constants.TABLE)) {
                      lexer.reset(mark);
                      return parseCreateTable();
                  } else if (lexer.identifierEquals(Constants.SET) || lexer.identifierEquals(Constants.MULTISET) || lexer.identifierEquals(Constants.VOLATILE)) {
                      lexer.reset(mark);
                      return parseCreateTable();
                  } else if (lexer.identifierEquals(Constants.GLOBAL)) {
                      lexer.nextToken();
                      if (lexer.identifierEquals(Constants.TEMPORARY)) {
                          lexer.reset(mark);
                          return parseCreateTable();
                      }
                  } else if (lexer.identifierEquals(Constants.MODEL)) {
                      lexer.reset(mark);
                      return parseCreateModel();
                  }
  
                  SQLStatement stmt = createTableRest(mark);
                  if (stmt == null) {
                      throw new ParserException("parse create error, " + lexer.info());
                  }
                  return stmt;
          }
      }
  
      protected void createOptionSkip() {
          if (lexer.nextIf(OR)) {
              accept(REPLACE);
              lexer.nextIfIdentifier(Constants.FORCE);
          }
  
          lexer.nextIfIdentifier(Constants.GLOBAL);
  
          if (lexer.identifierEquals(Constants.TEMPORARY)
                  || lexer.token == Token.TEMPORARY
                  || lexer.identifierEquals("TEMP")
          ) {
              lexer.nextToken();
          }
          lexer.nextIfIdentifier("TRANSACTIONAL");
          lexer.nextIfIdentifier(Constants.NONCLUSTERED);
      }
  
      protected SQLStatement createTableRest(Lexer.SavePoint mark) {
          return null;
      }
  
      public SQLStatement parseCreateScan() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      protected SQLStatement parseCreateModel() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      public SQLStatement parseCreateRole() {
          accept(Token.CREATE);
          acceptIdentifier("ROLE");
          SQLName name = this.exprParser.name();
  
          SQLCreateRoleStatement stmt = new SQLCreateRoleStatement(dbType);
          stmt.setName(name);
  
          return stmt;
      }
  
      protected SQLStatement parseCreateTableSpace() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      protected SQLStatement parseCreateOutline() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      protected SQLStatement parseCreateServer() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      protected SQLStatement parseCreateEvent() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      public SQLStatement parseCreateType() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      public SQLStatement parseCreateTableGroup() {
          accept(Token.CREATE);
          acceptIdentifier("TABLEGROUP");
  
          SQLCreateTableGroupStatement stmt = new SQLCreateTableGroupStatement();
  
          if (lexer.identifierEquals(Constants.IF)) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
              stmt.setIfNotExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          if (lexer.token == Token.PARTITION || lexer.identifierEquals("PARTITION")) {
              lexer.nextToken();
              acceptIdentifier("NUM");
              SQLExpr num = this.exprParser.expr();
              stmt.setPartitionNum(num);
          }
  
          return stmt;
      }
  
      public SQLStatement parseCreateUser() {
          accept(Token.CREATE);
          accept(Token.USER);
  
          SQLCreateUserStatement stmt = new SQLCreateUserStatement();
          stmt.setUser(this.exprParser.name());
  
          acceptIdentifier("IDENTIFIED");
          accept(Token.BY);
          stmt.setPassword(this.exprParser.primary());
  
          return stmt;
      }
  
      public SQLCreateFunctionStatement parseCreateFunction() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      protected void parseCreateMaterializedViewRest(SQLCreateMaterializedViewStatement stmt) {
          if (lexer.token == Token.PARTITION) {
              SQLPartitionBy partitionBy = this.exprParser.parsePartitionBy();
              stmt.setPartitionBy(partitionBy);
          }
      }
      public SQLStatement parseCreateMaterializedView() {
          accept(Token.CREATE);
          acceptIdentifier("MATERIALIZED");
          accept(Token.VIEW);
  
          SQLCreateMaterializedViewStatement stmt = new SQLCreateMaterializedViewStatement();
  
          if (lexer.token == IF) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(EXISTS);
              stmt.setIfNotExists(true);
          }
  
          stmt.setName(this.exprParser.name());
  
          parseCreateMaterializedViewRest(stmt);
  
          for (; ; ) {
              if (exprParser instanceof OracleExprParser) {
                  ((OracleExprParser) exprParser).parseSegmentAttributes(stmt);
              }
  
              if (lexer.identifierEquals("REFRESH")) {
                  lexer.nextToken();
                  boolean refresh = false;
                  for (; ; ) {
                      if (lexer.identifierEquals("FAST")) {
                          lexer.nextToken();
                          stmt.setRefreshFast(true);
  
                          refresh = true;
                      } else if (lexer.identifierEquals("COMPLETE")) {
                          lexer.nextToken();
                          stmt.setRefreshComplete(true);
  
                          refresh = true;
                      } else if (lexer.identifierEquals(Constants.FORCE)) {
                          lexer.nextToken();
                          stmt.setRefreshForce(true);
  
                          refresh = true;
                      } else if (lexer.token == Token.ON) {
                          lexer.nextToken();
                          if (lexer.token == Token.COMMIT || lexer.identifierEquals(Constants.COMMIT)) {
                              lexer.nextToken();
                              stmt.setRefreshOnCommit(true);
                              refresh = true;
                          } else if (lexer.identifierEquals(Constants.OVERWRITE)) {
                              lexer.nextToken();
                              stmt.setRefreshOnOverWrite(true);
  
                              refresh = true;
                          } else {
                              acceptIdentifier("DEMAND");
                              stmt.setRefreshOnDemand(true);
  
                              refresh = true;
                          }
                      } else if (lexer.identifierEquals(Constants.START)) {
                          lexer.nextToken();
                          accept(Token.WITH);
                          SQLExpr startWith = this.exprParser.expr();
                          stmt.setStartWith(startWith);
                          stmt.setRefreshStartWith(true);
  
                          refresh = true;
                      } else if (lexer.identifierEquals(Constants.NEXT)) {
                          lexer.nextToken();
                          SQLExpr next = this.exprParser.expr();
                          stmt.setNext(next);
                          stmt.setRefreshNext(true);
  
                          refresh = true;
                      } else {
                          break;
                      }
                  }
  
                  if (!refresh) {
                      throw new ParserException("refresh clause is empty. " + lexer.info());
                  }
  
              } else if (lexer.identifierEquals("BUILD")) {
                  lexer.nextToken();
  
                  if (lexer.identifierEquals("IMMEDIATE") || lexer.token == Token.IMMEDIATE) {
                      lexer.nextToken();
                      stmt.setBuildImmediate(true);
                  } else {
                      accept(Token.DEFERRED);
                      stmt.setBuildDeferred(true);
                  }
              } else if (lexer.identifierEquals("PARALLEL")) {
                  lexer.nextToken();
                  stmt.setParallel(true);
                  if (lexer.token == Token.LITERAL_INT) {
                      stmt.setParallelValue(lexer.integerValue().intValue());
                      lexer.nextToken();
                  }
              } else if (lexer.identifierEquals(Constants.NOCACHE) || lexer.token == Token.NOCACHE) {
                  lexer.nextToken();
                  stmt.setCache(false);
              } else if (lexer.identifierEquals(Constants.NOPARALLEL)) {
                  lexer.nextToken();
                  stmt.setParallel(false);
              } else if (lexer.token == Token.WITH) {
                  lexer.nextToken();
                  acceptIdentifier("ROWID");
                  stmt.setWithRowId(true);
              } else {
                  break;
              }
          }
  
          Boolean enableQueryRewrite = null;
          if (lexer.token == Token.ENABLE) {
              lexer.nextToken();
              enableQueryRewrite = true;
          }
  
          if (lexer.token == Token.DISABLE) {
              lexer.nextToken();
              enableQueryRewrite = false;
          }
  
          if (enableQueryRewrite != null) {
              acceptIdentifier("QUERY");
              acceptIdentifier("REWRITE");
              stmt.setEnableQueryRewrite(enableQueryRewrite);
          }
  
          accept(Token.AS);
          SQLSelect select = this.createSQLSelectParser().select();
          stmt.setQuery(select);
  
          return stmt;
      }
  
      public SQLStatement parseCreateDbLink() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      public SQLStatement parseCreateSynonym() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      public SQLStatement parseCreateExternalCatalog() {
          MySqlCreateExternalCatalogStatement stmt = new MySqlCreateExternalCatalogStatement();
  
          if (lexer.token == Token.CREATE) {
              lexer.nextToken();
          }
  
          acceptIdentifier("EXTERNAL");
          acceptIdentifier("CATALOG");
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
              stmt.setIfNotExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          acceptIdentifier("PROPERTIES");
          accept(Token.LPAREN);
  
          for (; ; ) {
              SQLName key = this.exprParser.name();
              accept(Token.EQ);
              SQLName value = this.exprParser.name();
  
              stmt.getProperties().put(key, value);
  
              if (lexer.token == Token.RPAREN) {
                  accept(Token.RPAREN);
                  break;
              }
          }
  
          if (lexer.token() == Token.COMMENT) {
              lexer.nextToken();
              SQLName comment = this.exprParser.name();
              stmt.setComment(comment);
          }
  
          return stmt;
      }
  
      public SQLCreateStatement parseCreateTrigger() {
          SQLCreateTriggerStatement stmt = new SQLCreateTriggerStatement(getDbType());
  
          if (lexer.token == Token.CREATE) {
              lexer.nextToken();
  
              if (lexer.token == Token.OR) {
                  lexer.nextToken();
                  accept(Token.REPLACE);
  
                  stmt.setOrReplace(true);
              }
          }
  
          if (lexer.identifierEquals(Constants.DEFINER)) {
              lexer.nextToken();
              accept(Token.EQ);
              SQLName definer = ((MySqlExprParser) this.exprParser).userName();
              stmt.setDefiner(definer);
  
              if (lexer.token == Token.LPAREN) {
                  lexer.nextToken();
                  accept(Token.RPAREN);
              }
          }
  
          accept(Token.TRIGGER);
  
          stmt.setName(this.exprParser.name());
  
          if (lexer.identifierEquals(Constants.BEFORE)) {
              stmt.setTriggerType(TriggerType.BEFORE);
              lexer.nextToken();
          } else if (lexer.identifierEquals(Constants.AFTER)) {
              stmt.setTriggerType(TriggerType.AFTER);
              lexer.nextToken();
          } else if (lexer.identifierEquals(Constants.INSTEAD)) {
              lexer.nextToken();
              accept(Token.OF);
              stmt.setTriggerType(TriggerType.INSTEAD_OF);
          }
  
          for (; ; ) {
              if (lexer.token == Token.INSERT) {
                  lexer.nextToken();
                  stmt.setInsert(true);
              } else if (lexer.token == Token.UPDATE) {
                  lexer.nextToken();
                  stmt.setUpdate(true);
  
                  if (lexer.token == Token.OF) {
                      lexer.nextToken();
                      this.exprParser.names(stmt.getUpdateOfColumns(), stmt);
                  }
              } else if (lexer.token == Token.DELETE) {
                  lexer.nextToken();
                  stmt.setDelete(true);
              }
  
              if (lexer.token == Token.COMMA
                      || lexer.token == Token.OR) {
                  lexer.nextToken();
                  continue;
              }
  
              break;
          }
  
          accept(Token.ON);
          stmt.setOn(this.exprParser.name());
  
          if (lexer.token == Token.FOR) {
              lexer.nextToken();
              acceptIdentifier("EACH");
              accept(Token.ROW);
              stmt.setForEachRow(true);
          }
  
          if (lexer.token == Token.WHEN) {
              lexer.nextToken();
              SQLExpr condition = this.exprParser.expr();
              stmt.setWhen(condition);
          }
  
          //for postgresql https://www.postgresql.org/docs/current/sql-createtrigger.html
          if (lexer.identifierEquals("EXECUTE")) {
              lexer.nextToken();
              String executeType = lexer.stringVal();
              stmt.setExecuteType(executeType);
              lexer.nextToken();
              SQLExpr executeFunc = this.exprParser.expr();
              stmt.setExecuteFunc(executeFunc);
              return stmt;
          }
          List<SQLStatement> body = new ArrayList<>();
          this.parseStatementList(body, 1);
          if (body.isEmpty()) {
              throw new ParserException("syntax error");
          }
          stmt.setBody(body.get(0));
          return stmt;
      }
  
      public SQLStatement parseBlock() {
          throw new ParserException("TODO " + lexer.info());
      }
  
      public SQLStatement parseCreateSchema() {
          return parseCreateDatabase();
      }
  
      public void parseCreateTableSupportSchema() {
          accept(Token.DATABASE);
      }
      public SQLStatement parseCreateDatabase() {
          accept(CREATE);
          SQLCreateDatabaseStatement stmt = new SQLCreateDatabaseStatement(dbType);
  
          if (lexer.hasComment() && lexer.isKeepComments()) {
              stmt.addBeforeComment(lexer.readAndResetComments());
          }
  
          if (lexer.token == Token.CREATE) {
              lexer.nextToken();
          }
  
          parseCreateTableSupportSchema();
  
          if (lexer.token == Token.IF || lexer.identifierEquals("IF")) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
              stmt.setIfNotExists(true);
          }
  
          stmt.setName(this.exprParser.name());
  
          if (lexer.token == Token.COMMENT) {
              lexer.nextToken();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
              }
              SQLExpr comment = this.exprParser.expr();
              stmt.setComment(comment);
          }
  
          if (lexer.identifierEquals(Constants.LOCATION)) {
              lexer.nextToken();
              SQLExpr location = this.exprParser.expr();
              stmt.setLocation(location);
          }
  
          if (lexer.token == Token.WITH) {
              lexer.nextToken();
  
              if (lexer.identifierEquals(Constants.DBPROPERTIES)) {
                  lexer.nextToken();
                  accept(Token.LPAREN);
                  for (; ; ) {
                      SQLAssignItem assignItem = this.exprParser.parseAssignItem();
                      assignItem.setParent(stmt);
                      stmt.getDbProperties().add(assignItem);
  
                      if (lexer.token == Token.COMMA) {
                          lexer.nextToken();
                          continue;
                      }
                      break;
                  }
                  accept(Token.RPAREN);
              } else {
                  throw new ParserException("TODO " + lexer.info());
              }
          }
  
          if (lexer.token == Token.COMMENT) {
              lexer.nextToken();
              if (lexer.token == Token.EQ) {
                  lexer.nextToken();
              }
              SQLExpr comment = this.exprParser.expr();
              stmt.setComment(comment);
          }
  
          return stmt;
      }
  
      public SQLCreateProcedureStatement parseCreateProcedure() {
          throw new ParserException("TODO " + lexer.token);
      }
  
      public SQLCreateStatement parseCreateSequence() {
          accept(Token.CREATE);
          SQLCreateSequenceStatement stmt = new SQLCreateSequenceStatement();
  
          if (lexer.token == Token.GROUP) {
              lexer.nextToken();
              stmt.setGroup(true);
          } else if (lexer.identifierEquals(Constants.SIMPLE)) {
              lexer.nextToken();
              stmt.setSimple(true);
              if (lexer.token == Token.WITH) {
                  lexer.nextToken();
                  accept(Token.CACHE);
                  stmt.setWithCache(true);
              }
          } else if (lexer.identifierEquals(Constants.TIME)) {
              lexer.nextToken();
              stmt.setTime(true);
          }
  
          accept(Token.SEQUENCE);
  
          stmt.setDbType(dbType);
          stmt.setName(this.exprParser.name());
  
          for (; ; ) {
              if (lexer.token() == Token.START || lexer.identifierEquals(Constants.START)) {
                  lexer.nextToken();
                  accept(Token.WITH);
                  stmt.setStartWith(this.exprParser.expr());
                  continue;
              } else if (lexer.identifierEquals(Constants.INCREMENT)) {
                  lexer.nextToken();
                  accept(Token.BY);
                  stmt.setIncrementBy(this.exprParser.expr());
                  continue;
              } else if (lexer.token() == Token.CACHE || lexer.identifierEquals(Constants.CACHE)) {
                  lexer.nextToken();
                  stmt.setCache(Boolean.TRUE);
  
                  if (lexer.token() == Token.LITERAL_INT) {
                      stmt.setCacheValue(this.exprParser.primary());
                  }
                  continue;
              } else if (lexer.token == Token.WITH) {
                  lexer.nextToken();
                  accept(Token.CACHE);
                  stmt.setCache(true);
                  continue;
              } else if (lexer.token() == Token.NOCACHE || lexer.identifierEquals(Constants.NOCACHE)) {
                  lexer.nextToken();
                  stmt.setCache(Boolean.FALSE);
                  continue;
              } else if (lexer.token() == Token.ORDER) {
                  lexer.nextToken();
                  stmt.setOrder(Boolean.TRUE);
                  continue;
              } else if (lexer.identifierEquals("NOORDER")) {
                  lexer.nextToken();
                  stmt.setOrder(Boolean.FALSE);
                  continue;
              } else if (lexer.identifierEquals("CYCLE")) {
                  lexer.nextToken();
                  stmt.setCycle(Boolean.TRUE);
                  continue;
              } else if (lexer.identifierEquals(Constants.NOCYCLE)) {
                  lexer.nextToken();
                  stmt.setCycle(Boolean.FALSE);
                  continue;
              } else if (lexer.identifierEquals("MINVALUE")) {
                  lexer.nextToken();
                  stmt.setMinValue(this.exprParser.expr());
                  continue;
              } else if (lexer.identifierEquals("MAXVALUE")) {
                  lexer.nextToken();
                  stmt.setMaxValue(this.exprParser.expr());
                  continue;
              } else if (lexer.identifierEquals("NOMAXVALUE")) {
                  lexer.nextToken();
                  stmt.setNoMaxValue(true);
                  continue;
              } else if (lexer.identifierEquals("NOMINVALUE")) {
                  lexer.nextToken();
                  stmt.setNoMinValue(true);
                  continue;
              }
              break;
          }
  
          if (lexer.identifierEquals(Constants.UNIT)) {
              lexer.nextToken();
  
              if (lexer.identifierEquals(Constants.COUNT)) {
                  lexer.nextToken();
                  SQLExpr unitCount = this.exprParser.primary();
                  stmt.setUnitCount(unitCount);
              }
  
              if (lexer.token == Token.INDEX) {
                  lexer.nextToken();
                  SQLExpr unitIndex = this.exprParser.primary();
                  stmt.setUnitIndex(unitIndex);
              }
  
              if (lexer.hashLCase() == Constants.STEP) {
                  lexer.nextToken();
                  SQLExpr step = this.exprParser.primary();
                  stmt.setStep(step);
              }
  
          }
  
          return stmt;
      }
  
      public SQLCreateIndexStatement parseCreateIndex() {
          accept(Token.CREATE);
          SQLCreateIndexStatement stmt = new SQLCreateIndexStatement(getDbType());
          if (lexer.token == Token.UNIQUE) {
              lexer.nextToken();
              if (lexer.identifierEquals("CLUSTERED")) {
                  lexer.nextToken();
                  stmt.setType("UNIQUE CLUSTERED");
              } else if (lexer.identifierEquals("NONCLUSTERED")) {
                  stmt.setType("UNIQUE NONCLUSTERED");
                  lexer.nextToken();
              } else {
                  stmt.setType("UNIQUE");
              }
          } else if (lexer.token() == Token.FULLTEXT) {
              stmt.setType("FULLTEXT");
              lexer.nextToken();
          } else if (lexer.identifierEquals("NONCLUSTERED")) {
              stmt.setType("NONCLUSTERED");
              lexer.nextToken();
          }
  
          accept(Token.INDEX);
  
          stmt.setName(this.exprParser.name());
  
          accept(Token.ON);
  
          stmt.setTable(this.exprParser.name());
  
          accept(Token.LPAREN);
  
          for (; ; ) {
              SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
              item.setParent(stmt);
              stmt.addItem(item);
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
          accept(Token.RPAREN);
  
          return stmt;
      }
  
      public SQLCreateTableParser getSQLCreateTableParser() {
          return new SQLCreateTableParser(this.exprParser);
      }
  
      public SQLStatement parseSelect() {
          SQLSelectParser selectParser = createSQLSelectParser();
          SQLSelect select = selectParser.select();
          return new SQLSelectStatement(select, getDbType());
      }
  
      public SQLSelectParser createSQLSelectParser() {
          return new SQLSelectParser(this.exprParser, selectListCache);
      }
  
      public SQLSelectParser createSQLSelectParser(SQLExprParser exprParser) {
          return new SQLSelectParser(exprParser);
      }
  
      public void parseUpdateStatementPartition(SQLUpdateStatement updateStatement) {
      }
      public SQLUpdateStatement parseUpdateStatement() {
          SQLUpdateStatement updateStatement = createUpdateStatement();
  
          if (lexer.nextIf(UPDATE)) {
              SQLTableSource tableSource = this.exprParser.createSelectParser().parseTableSource();
              updateStatement.setTableSource(tableSource);
          }
  
          parseUpdateStatementPartition(updateStatement);
  
          parseUpdateSet(updateStatement);
  
          if (lexer.nextIf(FROM)) {
              updateStatement.setFrom(this.createSQLSelectParser().parseTableSource());
          }
  
          if (lexer.token == (Token.WHERE)) {
              lexer.nextToken();
              updateStatement.setWhere(this.exprParser.expr());
          }
          updateStatement.setLimit(this.exprParser.parseLimit());
          return updateStatement;
      }
  
      protected void parseUpdateSetComma() {
      }
      protected void parseUpdateSet(SQLUpdateStatement update) {
          accept(Token.SET);
  
          parseUpdateSetComma();
  
          for (; ; ) {
              SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
              update.addItem(item);
  
              if (lexer.token != Token.COMMA) {
                  break;
              }
  
              lexer.nextToken();
              if (lexer.token == WHERE) {
                  break;
              }
          }
      }
  
      protected SQLUpdateStatement createUpdateStatement() {
          return new SQLUpdateStatement(getDbType());
      }
  
      public SQLDeleteStatement parseDeleteStatement() {
          SQLDeleteStatement deleteStatement = new SQLDeleteStatement(getDbType());
  
          if (lexer.nextIf(Token.DELETE)) {
              lexer.nextIf(Token.FROM);
  
              if (lexer.token == Token.COMMENT) {
                  lexer.nextToken();
              }
  
              SQLName tableName = exprParser.name();
  
              deleteStatement.setTableName(tableName);
              deleteStatement.setAlias(
                      tableAlias()
              );
  
              if (lexer.token == Token.FROM) {
                  lexer.nextToken();
                  SQLTableSource tableSource = createSQLSelectParser().parseTableSource();
                  deleteStatement.setFrom(tableSource);
              }
          }
  
          if (lexer.token == (Token.WHERE)) {
              lexer.nextToken();
              SQLExpr where = this.exprParser.expr();
              deleteStatement.setWhere(where);
          }
  
          return deleteStatement;
      }
  
      public SQLCreateTableStatement parseCreateTable() {
          return getSQLCreateTableParser()
                  .parseCreateTable();
      }
  
      public void parseCreateViewAfterName(SQLCreateViewStatement createView) {
      }
      public void parseCreateViewAtDataType(SQLColumnDefinition column, SQLName expr) {
      }
      public SQLCreateViewStatement parseCreateView() {
          SQLCreateViewStatement createView = new SQLCreateViewStatement(getDbType());
  
          if (lexer.token == Token.CREATE) {
              lexer.nextToken();
          }
  
          if (lexer.token == Token.OR) {
              lexer.nextToken();
              accept(Token.REPLACE);
              createView.setOrReplace(true);
          }
  
          if (lexer.identifierEquals("ALGORITHM")) {
              lexer.nextToken();
              accept(Token.EQ);
              String algorithm = lexer.stringVal();
              createView.setAlgorithm(algorithm);
              lexer.nextToken();
          }
  
          if (lexer.identifierEquals(Constants.DEFINER)) {
              lexer.nextToken();
              accept(Token.EQ);
              SQLName definer = (SQLName) ((MySqlExprParser) this.exprParser).userName();
              createView.setDefiner(definer);
          }
  
          if (lexer.identifierEquals(Constants.SQL)) {
              lexer.nextToken();
              acceptIdentifier("SECURITY");
              String sqlSecurity = lexer.stringVal();
              createView.setSqlSecurity(sqlSecurity);
              lexer.nextToken();
          }
  
          if (lexer.identifierEquals(Constants.FORCE)) {
              lexer.nextToken();
              createView.setForce(true);
          }
  
          lexer.nextIfIdentifier(Constants.GLOBAL);
  
          if (lexer.nextIfIdentifier(Constants.TEMPORARY) || lexer.nextIfIdentifier("TEMP")) {
              createView.setTemporary(true);
          }
  
          this.accept(Token.VIEW);
  
          if (lexer.token == Token.IF || lexer.identifierEquals("IF")) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
              createView.setIfNotExists(true);
          }
  
          createView.setName(exprParser.name());
          parseCreateViewAfterName(createView);
  
          if (lexer.token == Token.LPAREN) {
              lexer.nextToken();
  
              for (; ; ) {
                  if (lexer.token == Token.CONSTRAINT) {
                      SQLTableConstraint constraint = (SQLTableConstraint) this.exprParser.parseConstraint();
                      createView.addColumn(constraint);
                  } else if (lexer.token == Token.RPAREN) {
                      break;
                  } else {
                      SQLColumnDefinition column = new SQLColumnDefinition();
                      column.setDbType(dbType);
                      SQLName expr = this.exprParser.name();
                      column.setName(expr);
  
                      parseCreateViewAtDataType(column, expr);
  
                      this.exprParser.parseColumnRest(column);
  
                      if (lexer.token == Token.COMMENT) {
                          lexer.nextToken();
  
                          SQLExpr comment;
                          if (lexer.token == Token.LITERAL_ALIAS) {
                              String alias = 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);
                              lexer.nextToken();
                          } else {
                              comment = this.exprParser.primary();
                          }
                          column.setComment(comment);
                      }
  
                      column.setParent(createView);
                      createView.addColumn(column);
                  }
  
                  if (lexer.token == Token.COMMA) {
                      lexer.nextToken();
                  } else {
                      break;
                  }
              }
  
              accept(Token.RPAREN);
          }
  
          if (lexer.identifierEquals("RETURNS")) {
              lexer.nextToken();
              SQLVariantRefExpr varRef = (SQLVariantRefExpr) this.exprParser.expr();
              createView.setReturns(varRef);
              createView.setReturnsDataType(
                      (SQLTableDataType) this.exprParser.parseDataType()
              );
          }
  
          if (lexer.token == Token.COMMENT) {
              lexer.nextToken();
              SQLCharExpr comment = (SQLCharExpr) exprParser.primary();
              createView.setComment(comment);
          }
  
          createViewAs(createView);
  
          if (lexer.token == Token.WITH) {
              lexer.nextToken();
  
              if (lexer.identifierEquals("CASCADED")) {
                  createView.setWithCascaded(true);
                  lexer.nextToken();
              } else if (lexer.identifierEquals("LOCAL")) {
                  createView.setWithLocal(true);
                  lexer.nextToken();
              } else if (lexer.identifierEquals("READ")) {
                  lexer.nextToken();
                  accept(Token.ONLY);
                  createView.setWithReadOnly(true);
              }
  
              if (lexer.token == Token.CHECK) {
                  lexer.nextToken();
                  acceptIdentifier("OPTION");
                  createView.setWithCheckOption(true);
              }
          }
  
          return createView;
      }
  
      protected void createViewAs(SQLCreateViewStatement createView) {
          accept(Token.AS);
  
          if (lexer.identifierEquals(Constants.BEGIN)) {
              SQLBlockStatement block = (SQLBlockStatement) this.parseBlock();
              createView.setScript(block);
              return;
          }
          SQLSelectParser selectParser = this.createSQLSelectParser();
          SQLSelect select;
          if (lexer.token == VALUES) {
              SQLTableSource tablesource = selectParser.parseTableSource();
              if (tablesource instanceof SQLSelectQuery) {
                  select = new SQLSelect((SQLSelectQuery) tablesource);
              } else {
                  SQLSelectQueryBlock queryBlock = selectParser.createSelectQueryBlock();
                  queryBlock.addSelectItem(new SQLAllColumnExpr());
                  queryBlock.setFrom(tablesource);
                  select = new SQLSelect(queryBlock);
              }
          } else {
              select = selectParser.select();
          }
          createView.setSubQuery(select);
      }
  
      public SQLCommentStatement parseComment() {
          accept(Token.COMMENT);
          SQLCommentStatement stmt = new SQLCommentStatement();
  
          accept(Token.ON);
  
          if (lexer.token == Token.TABLE) {
              stmt.setType(SQLCommentStatement.Type.TABLE);
              lexer.nextToken();
          } else if (lexer.token == Token.COLUMN) {
              stmt.setType(SQLCommentStatement.Type.COLUMN);
              lexer.nextToken();
          } else if (lexer.token == INDEX) {
              stmt.setType(SQLCommentStatement.Type.INDEX);
              lexer.nextToken();
          } else if (lexer.token == VIEW) {
              stmt.setType(SQLCommentStatement.Type.VIEW);
              lexer.nextToken();
          }
  
          stmt.setOn(this.exprParser.name());
  
          accept(Token.IS);
          stmt.setComment(this.exprParser.expr());
  
          return stmt;
      }
  
      protected void parseAlterTableAddColumn(SQLAlterTableStatement stmt) {
          stmt.addItem(
                  parseAlterTableAddColumn()
          );
      }
  
      protected boolean parseAlterTableAddColumnBefore(SQLAlterTableAddColumn x) {
          return false;
      }
  
      protected SQLAlterTableAddColumn parseAlterTableAddColumn() {
          SQLAlterTableAddColumn item = new SQLAlterTableAddColumn();
          boolean columns = parseAlterTableAddColumnBefore(item);
  
          for (; ; ) {
              SQLColumnDefinition columnDef = this.exprParser.parseColumn();
              item.addColumn(columnDef);
  
              if (lexer.token == Token.WITH) {
                  Lexer.SavePoint mark = lexer.mark();
                  lexer.nextToken();
                  if (lexer.token == Token.DEFAULT) {
                      lexer.nextToken();
                      SQLExpr defaultExpr = this.exprParser.expr();
                      columnDef.setDefaultExpr(defaultExpr);
                  } else {
                      lexer.reset(mark);
                  }
              }
  
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  if (lexer.identifierEquals("ADD") || lexer.token() == Token.ALTER) {
                      break;
                  }
                  continue;
              }
              break;
          }
  
          if (columns) {
              accept(Token.RPAREN);
          }
  
          if (lexer.identifierEquals(Constants.RESTRICT)) {
              lexer.nextToken();
              item.setRestrict(true);
          } else if (lexer.token() == Token.CASCADE || lexer.identifierEquals(Constants.CASCADE)) {
              lexer.nextToken();
              item.setCascade(true);
          } else {
              item.setCascade(false);
          }
  
          return item;
      }
  
      protected SQLAlterTableReplaceColumn parseAlterTableReplaceColumn() {
          accept(Token.REPLACE);
          acceptIdentifier("COLUMNS");
  
          SQLAlterTableReplaceColumn item = new SQLAlterTableReplaceColumn();
  
          accept(Token.LPAREN);
          for (; ; ) {
              SQLColumnDefinition columnDef = this.exprParser.parseColumn();
              item.addColumn(columnDef);
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  if (lexer.identifierEquals(Constants.ADD)) {
                      break;
                  }
                  continue;
              }
              break;
          }
          accept(Token.RPAREN);
  
          return item;
      }
  
      public SQLStatement parseStatement() {
          final SQLStatement ret = parseStatement0();
  
          if (END_TOKEN_CHECKING_ENABLED) {
              checkEndToken();
          }
  
          if (lexer.nextIf(SEMI)) {
              ret.setAfterSemi(true);
          }
  
          return ret;
      }
  
      protected SQLStatement parseStatement0() {
          final SQLStatement ret;
          if (lexer.token == Token.SELECT) {
              ret = this.parseSelect();
          } else if (lexer.token == Token.INSERT) {
              ret = this.parseInsert();
          } else if (lexer.token == Token.UPDATE) {
              ret = this.parseUpdateStatement();
          } else if (lexer.token == Token.DELETE) {
              ret = this.parseDeleteStatement();
          } else {
              final List<SQLStatement> list = new ArrayList<SQLStatement>(1);
              this.parseStatementList(list, 1, null);
              ret = list.get(0);
          }
          return ret;
      }
  
      private void checkEndToken() {
          if (lexer.token != Token.EOF
                  && lexer.token != Token.SEMI
                  && lexer.token != expectedNextToken) {
              // keep exception format consistent with parseStatementList method.
              throw new ParserException(UNSUPPORT_TOKEN_MSG_PREFIX + lexer.info());
          }
      }
  
      public void parseExplainFormatPartition(SQLExplainStatement explain) {
      }
  
      public void parseExplainFormatType(SQLExplainStatement explain) {
      }
      public SQLExplainStatement parseExplain() {
          accept(Token.EXPLAIN);
          if (lexer.identifierEquals("PLAN")) {
              lexer.nextToken();
          }
  
          if (lexer.token == Token.FOR) {
              lexer.nextToken();
          }
  
          SQLExplainStatement explain = new SQLExplainStatement(dbType);
  
          if (lexer.token == Token.ANALYZE || lexer.identifierEquals(Constants.ANALYZE)) {
              lexer.nextToken();
              explain.setType("ANALYZE");
          }
  
          if (lexer.token == Token.HINT) {
              explain.setHints(this.exprParser.parseHints());
          }
  
          if (lexer.identifierEquals(Constants.EXTENDED)) {
              lexer.nextToken();
              explain.setExtended(true);
          }
  
          if (lexer.identifierEquals(Constants.DEPENDENCY)) {
              lexer.nextToken();
              explain.setDependency(true);
          }
  
          if (lexer.identifierEquals(Constants.AUTHORIZATION)) {
              lexer.nextToken();
              explain.setAuthorization(true);
          }
  
          parseExplainFormatPartition(explain);
          parseExplainFormatType(explain);
          explain.setStatement(parseStatement());
          return explain;
      }
  
      protected SQLAlterTableAddClusteringKey parseAlterTableAddClusteringKey() {
          lexer.nextToken();
          SQLAlterTableAddClusteringKey item = new SQLAlterTableAddClusteringKey();
          accept(Token.KEY);
          item.setName(exprParser.name());
          accept(LPAREN);
          for (; ; ) {
              item.getColumns().add(exprParser.name());
              if (lexer.token == COMMA) {
                  lexer.nextToken();
                  continue;
              }
              break;
          }
          accept(RPAREN);
          return item;
      }
  
      /*
      protected SQLAlterTableAddIndex parseAlterTableAddIndex() {
          SQLAlterTableAddIndex item = new SQLAlterTableAddIndex();
  
          if (lexer.token() == Token.FULLTEXT) {
              lexer.nextToken();
              item.setType("FULLTEXT");
          } else if (lexer.identifierEquals(Constants.SPATIAL)) {
              lexer.nextToken();
              item.setType("SPATIAL");
          } else if (lexer.identifierEquals(Constants.CLUSTERED)) {
              lexer.nextToken();
              item.setType("CLUSTERED");
          } else if (lexer.identifierEquals(Constants.ANN)) {
              lexer.nextToken();
              item.setType("ANN");
          }
  
          if (lexer.identifierEquals(Constants.GLOBAL)) {
              item.setGlobal(true);
              lexer.nextToken();
          }
  
          if (lexer.token == Token.UNIQUE) {
              item.setUnique(true);
              lexer.nextToken();
              if (lexer.token == Token.INDEX) {
                  lexer.nextToken();
              } else if (lexer.token == Token.KEY) {
                  item.setKey(true);
                  lexer.nextToken();
              }
          } else {
              if (lexer.token == Token.INDEX) {
                  accept(Token.INDEX);
              } else if (lexer.token == Token.KEY) {
                  item.setKey(true);
                  accept(Token.KEY);
              }
          }
  
          if (lexer.token != Token.LPAREN) {
              item.setName(this.exprParser.name());
  
              if (DbType.mysql == dbType) {
                  if (lexer.identifierEquals("HASHMAP")) {
                      lexer.nextToken();
                      item.setHashMapType(true);
                  }
                  else if (lexer.identifierEquals(Constants.USING)) {
                      lexer.nextToken();
                      String indexType = lexer.stringVal;
                      item.setType(indexType);
                      accept(Token.IDENTIFIER);
                  }
              }
  
          }
  
          this.exprParser.parseIndexRest(item);
  
          if (dbType == DbType.mysql) {
              for (; ; ) {
                  if (lexer.identifierEquals(Constants.DISTANCEMEASURE)
                          || lexer.identifierEquals(Constants.ALGORITHM)
                          || lexer.token == Token.LOCK) {
                      String name = lexer.stringVal();
                      lexer.nextToken();
                      if (lexer.token() == Token.EQ) {
                          lexer.nextToken();
                      }
                      SQLExpr option = this.exprParser.primary();
                      item.addOption(name, option);
                  } else {
                      break;
                  }
              }
          }
  
          if (DbType.mysql == dbType) {
              if (lexer.identifierEquals(Constants.USING)) {
                  lexer.nextToken();
                  String indexType = lexer.stringVal;
                  item.setType(indexType);
                  accept(Token.IDENTIFIER);
              }
  
              if (lexer.identifierEquals(Constants.DBPARTITION)) {
                  SQLPartitionBy partitionClause = this.getSQLCreateTableParser().parsePartitionBy();
                  item.setDbPartitionBy(partitionClause);
              }
  
              if (lexer.identifierEquals(Constants.TBPARTITION)) {
                  lexer.nextToken();
                  accept(Token.BY);
                  SQLExpr expr = this.exprParser.expr();
                  if (lexer.identifierEquals(Constants.STARTWITH)) {
                      lexer.nextToken();
                      SQLExpr start = this.exprParser.primary();
                      acceptIdentifier("ENDWITH");
                      SQLExpr end = this.exprParser.primary();
                      expr = new SQLBetweenExpr(expr, start, end);
                  }
                  item.setTablePartitionBy(expr);
              }
  
              if (lexer.identifierEquals(Constants.TBPARTITIONS)) {
                  lexer.nextToken();
                  SQLExpr tbPartitions = this.exprParser.primary();
                  item.setTablePartitions(tbPartitions);
              }
          }
  
          for (;;) {
              if (lexer.token == Token.COMMENT) {
                  lexer.nextToken();
                  SQLExpr comment = this.exprParser.primary();
                  item.setComment(comment);
              } else if (DbType.mysql == dbType) {
                  if (lexer.identifierEquals(Constants.KEY_BLOCK_SIZE)) {
                      lexer.nextToken();
                      if (lexer.token() == Token.EQ) {
                          lexer.nextToken();
                      }
                      SQLExpr keyBlockSize = this.exprParser.primary();
                      item.setKeyBlockSize(keyBlockSize);
                  } else if (lexer.token() == Token.WITH) {
                      lexer.nextToken();
                      acceptIdentifier("PARSER");
                      item.setParserName(lexer.stringVal);
                      accept(Token.IDENTIFIER);
                  } else if (lexer.identifierEquals(Constants.USING)) {
                      // Or index_type in index_option.
                      lexer.nextToken();
                      item.setType(lexer.stringVal);
                      accept(Token.IDENTIFIER);
                  } else {
                      break;
                  }
              } else {
                  break;
              }
          }
  
          return item;
      }
      */
  
      /**
       * Parses the SQL OPEN statement and returns the corresponding SQLOpenStatement object.
       *
       * @return the parsed SQLOpenStatement object representing the SQL OPEN statement
       * @throws ParserException if an error occurs during parsing
       */
      public SQLOpenStatement parseOpen() {
          SQLOpenStatement stmt = new SQLOpenStatement();
          accept(Token.OPEN);
  
          final SQLName cursorName;
          if (lexer.token == Token.QUES) {
              lexer.nextToken();
              cursorName = new SQLIdentifierExpr("?");
          } else {
              cursorName = exprParser.name();
          }
          stmt.setCursorName(cursorName);
  
          if (lexer.token == Token.LPAREN) {
              lexer.nextToken();
              this.exprParser.exprList(stmt.getColumns(), stmt);
              accept(Token.RPAREN);
          }
  
          if (lexer.token == Token.FOR) {
              lexer.nextToken();
              if (lexer.token == Token.SELECT) {
                  SQLSelectParser selectParser = createSQLSelectParser();
                  SQLSelect select = selectParser.select();
                  SQLQueryExpr queryExpr = new SQLQueryExpr(select);
                  stmt.setFor(queryExpr);
              } else if (lexer.token == Token.LITERAL_CHARS) {
                  String chars = lexer.stringVal;
                  SQLExprParser exprParser = SQLParserUtils.createExprParser(chars, dbType);
                  SQLSelectParser selectParser = this.createSQLSelectParser(exprParser);
                  SQLSelect select = selectParser.select();
                  SQLQueryExpr queryExpr = new SQLQueryExpr(select);
                  stmt.setFor(queryExpr);
  
                  lexer.nextToken();
              } else if (lexer.token == Token.QUES) {
                  lexer.nextToken();
                  stmt.setFor(new SQLVariantRefExpr("?"));
              } else {
                  throw new ParserException("TODO " + lexer.info());
              }
          }
  
          if (lexer.token == Token.USING) {
              lexer.nextToken();
              this.exprParser.exprList(stmt.getUsing(), stmt);
          }
  
          accept(Token.SEMI);
          stmt.setAfterSemi(true);
          return stmt;
      }
  
      public SQLFetchStatement parseFetch() {
          accept(Token.FETCH);
  
          SQLFetchStatement stmt = new SQLFetchStatement();
          stmt.setCursorName(this.exprParser.name());
  
          if (lexer.identifierEquals("BULK")) {
              lexer.nextToken();
              acceptIdentifier("COLLECT");
              stmt.setBulkCollect(true);
          }
  
          accept(Token.INTO);
          for (; ; ) {
              stmt.getInto().add(this.exprParser.name());
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
  
              break;
          }
  
          if (lexer.token == Token.LIMIT) {
              SQLLimit limit = this.exprParser.parseLimit();
              stmt.setLimit(limit);
          }
  
          return stmt;
      }
  
      public SQLStatement parseClose() {
          SQLCloseStatement stmt = new SQLCloseStatement();
          accept(Token.CLOSE);
          stmt.setCursorName(exprParser.name());
          accept(Token.SEMI);
          stmt.setAfterSemi(true);
          return stmt;
      }
  
      public boolean isParseCompleteValues() {
          return parseCompleteValues;
      }
  
      public void setParseCompleteValues(boolean parseCompleteValues) {
          this.parseCompleteValues = parseCompleteValues;
      }
  
      public int getParseValuesSize() {
          return parseValuesSize;
      }
  
      public void setParseValuesSize(int parseValuesSize) {
          this.parseValuesSize = parseValuesSize;
      }
  
      public SQLStatement parseMerge() {
          if (lexer.identifierEquals(Constants.MERGE)) {
              lexer.nextToken();
          } else {
              accept(Token.MERGE);
          }
  
          SQLMergeStatement stmt = new SQLMergeStatement();
          stmt.setDbType(dbType);
  
          parseHints(stmt.getHints());
  
          mergeBeforeName();
  
          if (lexer.token == Token.LPAREN) {
              lexer.nextToken();
              SQLSelect select = this.createSQLSelectParser().select();
              SQLSubqueryTableSource tableSource = SQLSubqueryTableSource.fixParenthesized(new SQLSubqueryTableSource(select));
              stmt.setInto(tableSource);
              accept(Token.RPAREN);
          } else {
              stmt.setInto(exprParser.name());
          }
  
          stmt.getInto().setAlias(tableAlias());
  
          if (lexer.identifierEquals(Constants.USING)) {
              lexer.nextToken();
          } else {
              accept(Token.USING);
          }
  
          SQLTableSource using = this.createSQLSelectParser().parseTableSource();
          stmt.setUsing(using);
  
          accept(Token.ON);
          stmt.setOn(exprParser.expr());
  
          for (; ; ) {
              if (!parseMergeWhen(stmt)) {
                  break;
              }
          }
  
          SQLErrorLoggingClause errorClause = parseErrorLoggingClause();
          stmt.setErrorLoggingClause(errorClause);
  
          return stmt;
      }
  
      protected boolean parseMergeWhen(SQLMergeStatement stmt) {
          if (!lexer.nextIf(WHEN)) {
              return false;
          }
  
          boolean not = lexer.nextIf(NOT);
          accept(MATCHED);
  
          SQLName by = null;
          if (lexer.nextIf(BY)) {
              by = this.exprParser.name();
          }
  
          SQLExpr where = null;
          if (lexer.nextIf(Token.AND)) {
              where = this.exprParser.expr();
          }
  
          accept(THEN);
  
          if (lexer.nextIf(DELETE)) {
              stmt.addWhen(new SQLMergeStatement.WhenDelete(not, by, where));
              return true;
          }
  
          if (lexer.nextIf(UPDATE)) {
              accept(SET);
              SQLMergeStatement.WhenUpdate updateClause = new SQLMergeStatement.WhenUpdate(not, by, where);
              for (; ; ) {
                  SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
  
                  updateClause.addItem(item);
                  item.setParent(updateClause);
  
                  if (lexer.nextIf(Token.COMMA)) {
                      continue;
                  }
  
                  break;
              }
  
              if (lexer.nextIf(Token.WHERE)) {
                  updateClause.setWhere(exprParser.expr());
              }
  
              stmt.addWhen(updateClause);
  
              if (lexer.nextIf(DELETE)) {
                  SQLMergeStatement.WhenDelete whenDelete = new SQLMergeStatement.WhenDelete();
                  if (lexer.nextIf(Token.WHERE)) {
                      whenDelete.setWhere(exprParser.expr());
                  }
                  stmt.addWhen(whenDelete);
              }
              return true;
          }
  
          accept(INSERT);
          SQLMergeStatement.WhenInsert insertClause = new SQLMergeStatement.WhenInsert(not, by, where);
  
          if (lexer.token == Token.LPAREN) {
              accept(Token.LPAREN);
              exprParser.exprList(insertClause.getColumns(), insertClause);
              accept(Token.RPAREN);
          }
          if (lexer.nextIfIdentifier("ROW") || lexer.nextIfIdentifier("*") || lexer.nextIf(Token.STAR)) {
              insertClause.setInsertRow(true);
          } else {
              accept(Token.VALUES);
              accept(Token.LPAREN);
              exprParser.exprList(insertClause.getValues(), insertClause);
              accept(Token.RPAREN);
              insertClause.setInsertRow(false);
          }
  
          if (lexer.token == Token.WHERE) {
              lexer.nextToken();
              insertClause.setWhere(exprParser.expr());
          }
  
          stmt.addWhen(insertClause);
          return true;
      }
  
      protected void mergeBeforeName() {
          accept(Token.INTO);
      }
  
      protected SQLErrorLoggingClause parseErrorLoggingClause() {
          if (lexer.identifierEquals("LOG")) {
              SQLErrorLoggingClause errorClause = new SQLErrorLoggingClause();
  
              lexer.nextToken();
              accept(Token.ERRORS);
              if (lexer.token == Token.INTO) {
                  lexer.nextToken();
                  errorClause.setInto(exprParser.name());
              }
  
              if (lexer.token == Token.LPAREN) {
                  lexer.nextToken();
                  errorClause.setSimpleExpression(exprParser.expr());
                  accept(Token.RPAREN);
              }
  
              if (lexer.token == Token.REJECT) {
                  lexer.nextToken();
                  accept(Token.LIMIT);
                  errorClause.setLimit(exprParser.expr());
              }
  
              return errorClause;
          }
          return null;
      }
  
      public void parseHints(List<SQLHint> hints) {
          this.getExprParser().parseHints(hints);
      }
  
      public SQLStatement parseDescribe() {
          if (lexer.token == Token.DESC || lexer.identifierEquals("DESCRIBE")) {
              lexer.nextToken();
          } else {
              throw new ParserException("expect DESC, actual " + lexer.token);
          }
  
          SQLDescribeStatement stmt = new SQLDescribeStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == Token.DATABASE) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.DATABASE);
          } else if (lexer.token == Token.SCHEMA) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.SCHEMA);
          } else if (lexer.token == FUNCTION) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.FUNCTION);
          } else if (lexer.identifierEquals("ROLE")) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.ROLE);
          } else if (lexer.identifierEquals("PACKAGE")) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.PACKAGE);
          } else if (lexer.identifierEquals("INSTANCE")) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.INSTANCE);
          } else if (lexer.token == TABLE) {
              lexer.nextToken();
              stmt.setObjectType(SQLObjectType.TABLE);
          }
  
          if (lexer.identifierEquals(Constants.EXTENDED)) {
              lexer.nextToken();
              stmt.setExtended(true);
          }
  
          if (lexer.identifierEquals(Constants.FORMATTED)) {
              lexer.nextToken();
              stmt.setFormatted(true);
          }
  
          stmt.setObject(this.exprParser.name());
  
          if (lexer.token == Token.IDENTIFIER) {
              SQLName column = this.exprParser.name();
              stmt.setColumn(column);
          }
  
          if (lexer.token == Token.PARTITION) {
              lexer.nextToken();
              this.accept(Token.LPAREN);
              for (; ; ) {
                  stmt.getPartition().add(this.exprParser.expr());
                  if (lexer.token == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
                  if (lexer.token == Token.RPAREN) {
                      lexer.nextToken();
                      break;
                  }
              }
          }
  
          if (lexer.token == Token.IDENTIFIER && stmt.getColumn() == null) {
              SQLName column = this.exprParser.name();
              stmt.setColumn(column);
          }
  
          return stmt;
      }
  
      protected void parseWithQuerySkip() {
      }
  
      public SQLWithSubqueryClause parseWithQuery() {
          SQLWithSubqueryClause withQueryClause = new SQLWithSubqueryClause();
          if (lexer.hasComment() && lexer.isKeepComments()) {
              withQueryClause.addBeforeComment(lexer.readAndResetComments());
          }
  
          accept(Token.WITH);
  
          if (lexer.token == Token.RECURSIVE || lexer.identifierEquals("RECURSIVE")) {
              lexer.nextToken();
              withQueryClause.setRecursive(true);
          }
  
          for (; ; ) {
              SQLWithSubqueryClause.Entry entry = new SQLWithSubqueryClause.Entry();
              entry.setParent(withQueryClause);
  
              if (lexer.hasComment() && lexer.isKeepComments()) {
                  entry.addBeforeComment(lexer.readAndResetComments());
              }
  
              String alias = this.lexer.stringVal();
              lexer.nextToken();
              entry.setAlias(alias);
  
              if (lexer.token == Token.LPAREN) {
                  lexer.nextToken();
  
                  for (; ; ) {
                      if (lexer.token == RPAREN) {
                          break;
                      }
  
                      SQLName name = exprParser.name();
                      parseWithQuerySkip();
                      name.setParent(entry);
                      entry.getColumns().add(name);
                      if (lexer.token == COMMA) {
                          lexer.nextToken();
                          continue;
                      }
                  }
  
                  accept(Token.RPAREN);
              }
  
              accept(Token.AS);
              accept(Token.LPAREN);
  
              switch (lexer.token) {
                  case VALUES:
                  case WITH:
                  case SELECT:
                  case LPAREN:
                  case FROM:
                      entry.setSubQuery(
                              this.createSQLSelectParser()
                                      .select());
                      break;
                  case INSERT:
                      entry.setReturningStatement(
                              this.parseInsert()
                      );
                      break;
                  case UPDATE:
                      entry.setReturningStatement(
                              this.parseUpdateStatement()
                      );
                      break;
                  case DELETE:
                      entry.setReturningStatement(
                              this.parseDeleteStatement()
                      );
                      break;
                  default:
                      break;
              }
  
              accept(Token.RPAREN);
  
              withQueryClause.addEntry(entry);
  
              if (lexer.token == Token.COMMA) {
                  lexer.nextToken();
                  continue;
              }
  
              break;
          }
  
          return withQueryClause;
      }
  
      public SQLStatement parseEnd() {
          throw new ParserException("TODO. " + lexer.info());
      }
  
      public SQLStatement parseWith() {
          SQLWithSubqueryClause with = this.parseWithQuery();
  
          SQLStatement stmt = null;
          if (lexer.token == Token.SELECT || lexer.token == Token.LPAREN) {
              SQLSelectParser selectParser = createSQLSelectParser();
              SQLSelect select = selectParser.select();
              select.setWithSubQuery(with);
              stmt = new SQLSelectStatement(select, dbType);
          } else if (lexer.token == Token.INSERT) {
              SQLInsertStatement insert = (SQLInsertStatement) this.parseInsert();
              insert.setWith(with);
              stmt = insert;
          } else if (lexer.token == Token.UPSERT) {
              SQLInsertStatement insert = (SQLInsertStatement) this.parseUpsert();
              insert.setWith(with);
              stmt = insert;
          } else if (lexer.token == Token.FROM) {
              HiveMultiInsertStatement insert = (HiveMultiInsertStatement) this.parseInsert();
              insert.setWith(with);
              stmt = insert;
          } else if (lexer.token == UPDATE) {
              SQLUpdateStatement update = this.parseUpdateStatement();
              update.setWith(with);
              stmt = update;
          }
  
          if (stmt != null) {
              if (lexer.nextIf(SEMI)) {
                  stmt.setAfterSemi(true);
              }
              return stmt;
          }
  
          throw new ParserException("TODO. " + lexer.info());
      }
  
      protected void parseValueClause(
              List<SQLInsertStatement.ValuesClause> valueClauseList,
              int columnSize,
              SQLObject parent) {
          parseValueClause(valueClauseList, null, 0, parent);
      }
  
      protected void parseValueClauseNative(
              List<SQLInsertStatement.ValuesClause> valueClauseList,
              List<SQLColumnDefinition> columnDefinitionList,
              int columnSize,
              SQLObject parent
      ) {
          final TimeZone timeZone = lexer.getTimeZone();
          SQLInsertStatement.ValuesClause values;
          for (int i = 0; ; ++i) {
              int startPos = lexer.pos - 1;
  
              if (lexer.token != Token.LPAREN) {
                  throw new ParserException("syntax error, expect ')', " + lexer.info());
              }
  //            lexer.nextTokenValue();
  
              if (lexer.ch == '\'') { // for performance
                  lexer.bufPos = 0;
                  lexer.scanString();
              } else if (lexer.ch == '0') {
                  lexer.bufPos = 0;
                  if (lexer.charAt(lexer.pos + 1) == 'x') {
                      lexer.scanChar();
                      lexer.scanChar();
                      lexer.scanHexaDecimal();
                  } else {
                      lexer.scanNumber();
                  }
              } else if (lexer.ch > '0' && lexer.ch <= '9') {
                  lexer.bufPos = 0;
                  lexer.scanNumber();
              } else if (lexer.ch == '-' && lexer.charAt(lexer.pos + 1) != '-') {
                  lexer.scanNumber();
              } else {
                  lexer.nextTokenValue();
              }
  
              if (lexer.token() != Token.RPAREN) {
                  List valueExprList;
                  if (columnSize > 0) {
                      valueExprList = new ArrayList(columnSize);
                  } else {
                      valueExprList = new ArrayList();
                  }
                  values = new SQLInsertStatement.ValuesClause(valueExprList, parent);
  
                  int funcExecCount = 0;
                  for (int j = 0; ; ++j) {
                      SQLExpr expr = null;
                      Object value = null;
  
                      SQLColumnDefinition columnDefinition = null;
                      if (columnDefinitionList != null && j < columnDefinitionList.size()) {
                          columnDefinition = columnDefinitionList.get(j);
                      }
  
                      SQLDataType dataType = null;
                      if (columnDefinition != null) {
                          dataType = columnDefinition.getDataType();
                      }
  
                      switch (lexer.token) {
                          case LITERAL_INT: {
                              Number integerValue = lexer.integerValue();
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  expr = new SQLIntegerExpr(integerValue, values);
                                  expr = this.exprParser.exprRest(expr);
                                  expr.setParent(values);
                              } else {
                                  value = integerValue;
                              }
                              break;
                          }
                          case LITERAL_CHARS: {
                              String strVal = lexer.stringVal();
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  expr = new SQLCharExpr(strVal, values);
                                  expr = this.exprParser.exprRest(expr);
                                  expr.setParent(values);
                              } else {
                                  value = strVal;
                              }
                              break;
                          }
                          case LITERAL_NCHARS: {
                              String strVal = lexer.stringVal();
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  expr = new SQLNCharExpr(strVal, values);
                                  expr = this.exprParser.exprRest(expr);
                                  expr.setParent(values);
                              } else {
                                  value = strVal;
                              }
                              break;
                          }
                          case LITERAL_FLOAT: {
                              BigDecimal number = lexer.decimalValue();
  
                              if (dataType != null
                                      && dataType.nameHashCode64() == Constants.DECIMAL) {
                                  int precision = 0, scale = 0;
                                  List<SQLExpr> arguments = dataType.getArguments();
                                  if (arguments.size() > 0) {
                                      SQLExpr arg0 = arguments.get(0);
                                      if (arg0 instanceof SQLIntegerExpr) {
                                          precision = ((SQLIntegerExpr) arg0).getNumber().intValue();
                                      }
                                  }
                                  if (arguments.size() > 1) {
                                      SQLExpr arg0 = arguments.get(1);
                                      if (arg0 instanceof SQLIntegerExpr) {
                                          scale = ((SQLIntegerExpr) arg0).getNumber().intValue();
                                      }
                                  }
  
                                  if (number instanceof BigDecimal) {
                                      number = MySqlUtils.decimal(number, precision, scale);
                                  }
                              }
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  expr = new SQLDecimalExpr(number);
                                  expr = this.exprParser.exprRest(expr);
                                  expr.setParent(values);
                              } else {
                                  value = number;
                              }
                              break;
                          }
                          case NULL: {
                              lexer.nextTokenCommaValue();
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  expr = new SQLNullExpr(parent);
                                  expr = this.exprParser.exprRest(expr);
                                  expr.setParent(values);
                              } else {
                                  value = null;
                              }
                              break;
                          }
                          case IDENTIFIER: {
                              long hash = lexer.hashLCase();
                              if (hash == Constants.DATE) {
                                  lexer.nextTokenValue();
                                  String strVal = lexer.stringVal();
                                  value = java.sql.Date.valueOf(strVal);
                                  lexer.nextTokenComma();
                              } else if (hash == Constants.TIMESTAMP && timeZone != null) {
                                  lexer.nextTokenValue();
                                  String strVal = lexer.stringVal();
                                  value = new java.sql.Timestamp(MySqlUtils.parseDate(strVal, timeZone)
                                          .getTime());
                                  lexer.nextTokenComma();
                              } else if (hash == Constants.CURDATE
                                      || hash == Constants.CUR_DATE
                                      || hash == Constants.CURRENT_DATE) {
                                  lexer.nextTokenValue();
  
                                  if (lexer.token == Token.LPAREN) {
                                      lexer.nextToken();
                                      accept(Token.RPAREN);
                                  }
  
                                  if (now == null) {
                                      now = new java.sql.Timestamp(System.currentTimeMillis());
                                  }
  
                                  if (currentDate == null) {
                                      currentDate = new java.sql.Date(now.getTime());
                                  }
                                  value = currentDate;
                                  funcExecCount++;
                              } else if ((hash == Constants.SYSDATE
                                      || hash == Constants.NOW
                                      || hash == Constants.CURRENT_TIMESTAMP)
                                      && timeZone != null) {
                                  lexer.nextTokenValue();
  
                                  if (lexer.token == Token.LPAREN) {
                                      lexer.nextToken();
                                      accept(Token.RPAREN);
                                  }
  
                                  if (now == null) {
                                      now = new java.sql.Timestamp(System.currentTimeMillis());
                                  }
                                  value = now;
                                  funcExecCount++;
                              } else if (hash == Constants.UUID) {
                                  lexer.nextTokenLParen();
                                  accept(Token.LPAREN);
                                  accept(Token.RPAREN);
                                  value = UUID.randomUUID().toString();
                                  funcExecCount++;
                              } else {
                                  value = null;
                                  Lexer.SavePoint mark = lexer.mark();
                                  expr = exprParser.expr();
                                  if (expr instanceof SQLName) {
                                      lexer.reset(mark);
                                      lexer.info();
                                      throw new ParserException("insert value error, token " + lexer.stringVal() + ", line " + lexer.posLine + ", column " + lexer.posColumn, lexer.posLine, lexer.posColumn);
                                  }
                                  expr.setParent(values);
                              }
                              break;
                          }
                          default:
                              value = null;
                              expr = exprParser.expr();
                              expr.setParent(values);
                              break;
                      }
  
                      if (expr != null) {
                          expr.setParent(values);
                          value = expr;
                      }
  
                      if (lexer.token == Token.COMMA) {
                          valueExprList.add(value);
  
                          if (lexer.ch == '\'') { // for performance
                              lexer.bufPos = 0;
                              lexer.scanString();
                          } else if (lexer.ch == '0') {
                              lexer.bufPos = 0;
                              if (lexer.charAt(lexer.pos + 1) == 'x') {
                                  lexer.scanChar();
                                  lexer.scanChar();
                                  lexer.scanHexaDecimal();
                              } else {
                                  lexer.scanNumber();
                              }
                          } else if (lexer.ch > '0' && lexer.ch <= '9') {
                              lexer.bufPos = 0;
                              lexer.scanNumber();
                          } else if (lexer.ch == '-' && lexer.charAt(lexer.pos + 1) != '-') {
                              lexer.bufPos = 0;
                              lexer.scanNumber();
                          } else {
                              lexer.nextTokenValue();
                          }
                          continue;
                      } else if (lexer.token == Token.RPAREN) {
                          valueExprList.add(value);
                          break;
                      } else {
                          expr = this.exprParser.primaryRest(expr);
                          if (lexer.token != Token.COMMA && lexer.token() != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                          }
                          expr.setParent(values);
  
                          valueExprList.add(expr);
                          if (lexer.token == Token.COMMA) {
                              lexer.nextTokenValue();
                              continue;
                          } else {
                              break;
                          }
                      }
                  }
  
                  if (funcExecCount == 0 && lexer.isEnabled(SQLParserFeature.KeepInsertValueClauseOriginalString)) {
                      int endPos = lexer.pos();
                      String orginalString = lexer.subString(startPos, endPos - startPos);
                      values.setOriginalString(orginalString);
                  }
              } else {
                  values = new SQLInsertStatement.ValuesClause(new ArrayList<SQLExpr>(0));
              }
  
              valueClauseList.add(values);
  
              if (lexer.token != Token.RPAREN) {
                  throw new ParserException("syntax error. " + lexer.info());
              }
  
              if (!parseCompleteValues && valueClauseList.size() >= parseValuesSize) {
                  lexer.skipToEOF();
                  break;
              }
  
              lexer.nextTokenComma();
              while (lexer.token == Token.HINT) {
                  this.exprParser.parseHints();
              }
              if (lexer.token == Token.COMMA) {
                  lexer.nextTokenLParen();
                  if (values != null) {
                      columnSize = values.getValues().size();
                  }
                  continue;
              } else {
                  break;
              }
          }
      }
  
      public void parseValueClause(SQLInsertValueHandler valueHandler) throws SQLException {
          for (; ; ) {
              if (lexer.token != Token.LPAREN) {
                  throw new ParserException("syntax error, expect ')', " + lexer.info());
              }
  
              if (lexer.ch == '\'') { // for performance
                  lexer.bufPos = 0;
                  lexer.scanString();
              } else if (lexer.ch == '0') {
                  lexer.bufPos = 0;
                  if (lexer.charAt(lexer.pos + 1) == 'x') {
                      lexer.scanChar();
                      lexer.scanChar();
                      lexer.scanHexaDecimal();
                  } else {
                      lexer.scanNumber();
                  }
              } else if (lexer.ch > '0' && lexer.ch <= '9') {
                  lexer.bufPos = 0;
                  lexer.scanNumber();
              } else if (lexer.ch == '-' && lexer.charAt(lexer.pos + 1) != '-') {
                  lexer.scanNumber();
              } else {
                  lexer.nextTokenValue();
              }
  
              if (lexer.token() != Token.RPAREN) {
                  Object row = valueHandler.newRow();
  
                  for (int j = 0; ; ++j) {
                      switch (lexer.token) {
                          case LITERAL_INT: {
                              Number number = lexer.integerValue();
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  throw new ParserException("insert value error, " + lexer.info());
                              }
  
                              valueHandler.processInteger(row, j, number);
                              break;
                          }
                          case LITERAL_CHARS:
                          case LITERAL_NCHARS: {
                              String strVal = lexer.stringVal();
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  throw new ParserException("insert value error, " + lexer.info());
                              }
  
                              valueHandler.processString(row, j, strVal);
                              break;
                          }
                          case LITERAL_FLOAT: {
                              BigDecimal number = lexer.decimalValue();
  
                              if (lexer.ch == ',') {
                                  lexer.ch = lexer.charAt(++lexer.pos);
                                  lexer.token = COMMA;
                              } else {
                                  lexer.nextTokenCommaValue();
                              }
  
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  throw new ParserException("insert value error, " + lexer.info());
                              }
  
                              valueHandler.processDecimal(row, j, number);
                              break;
                          }
                          case NULL: {
                              lexer.nextTokenCommaValue();
                              if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                                  throw new ParserException("insert value error, " + lexer.info());
                              }
  
                              valueHandler.processNull(row, j);
                              break;
                          }
                          case TRUE:
                              valueHandler.processBoolean(row, j, true);
                              lexer.nextTokenComma();
                              break;
                          case FALSE:
                              valueHandler.processBoolean(row, j, false);
                              lexer.nextTokenComma();
                              break;
                          case IDENTIFIER: {
                              long hash = lexer.hashLCase();
                              if (hash == Constants.DATE) {
                                  lexer.nextTokenValue();
                                  String strVal = lexer.stringVal();
                                  valueHandler.processDate(row, j, strVal);
                                  lexer.nextTokenComma();
                              } else if (hash == Constants.TIMESTAMP) {
                                  lexer.nextTokenValue();
                                  String strVal = lexer.stringVal();
                                  valueHandler.processTimestamp(row, j, strVal);
                                  lexer.nextTokenComma();
                              } else if (hash == Constants.TIME) {
                                  lexer.nextTokenValue();
                                  String strVal = lexer.stringVal();
                                  valueHandler.processTime(row, j, strVal);
                                  lexer.nextTokenComma();
                              } else if (hash == Constants.DECIMAL) {
                                  lexer.nextTokenValue();
                                  String strVal = lexer.stringVal();
                                  BigDecimal decimal = new BigDecimal(strVal);
                                  valueHandler.processDecimal(row, j, decimal);
                                  lexer.nextTokenComma();
                              } else if (hash == Constants.CURDATE
                                      || hash == Constants.CUR_DATE
                                      || hash == Constants.CURRENT_DATE
                                      || hash == Constants.SYSDATE) {
                                  lexer.nextTokenLParen();
  
                                  if (lexer.token == Token.LPAREN) {
                                      lexer.nextToken();
                                      accept(Token.RPAREN);
                                  }
  
                                  if (currentDate == null) {
                                      currentDate = new java.sql.Date(now.getTime());
                                  }
  
                                  valueHandler.processDate(row, j, currentDate);
                              } else if (hash == Constants.NOW
                                      || hash == Constants.CURRENT_TIMESTAMP) {
                                  lexer.nextTokenLParen();
  
                                  if (lexer.token == Token.LPAREN) {
                                      lexer.nextToken();
                                      accept(Token.RPAREN);
                                  }
  
                                  if (now == null) {
                                      now = new java.sql.Timestamp(System.currentTimeMillis());
                                  }
  
                                  valueHandler.processTimestamp(row, j, now);
                              } else if (hash == Constants.UUID) {
                                  String funcName = lexer.stringVal();
                                  lexer.nextTokenLParen();
  
                                  if (lexer.token == Token.LPAREN) {
                                      lexer.nextToken();
                                      accept(Token.RPAREN);
                                  } else {
                                      throw new ParserException("insert value error, " + lexer.info());
                                  }
  
                                  if (now == null) {
                                      now = new java.sql.Timestamp(System.currentTimeMillis());
                                  }
  
                                  valueHandler.processFunction(row, j, funcName, hash);
                              } else if (hash == Constants.STR_TO_DATE || hash == Constants.DATE_PARSE) {
                                  String funcName = lexer.stringVal();
                                  lexer.nextTokenLParen();
  
                                  String strVal, format;
                                  if (lexer.token == Token.LPAREN) {
                                      lexer.nextTokenValue();
                                      strVal = lexer.stringVal();
                                      lexer.nextTokenComma();
                                      accept(Token.COMMA);
                                      format = lexer.stringVal();
                                      lexer.nextTokenValue();
                                      accept(Token.RPAREN);
                                  } else {
                                      throw new ParserException("insert value error, " + lexer.info());
                                  }
  
                                  valueHandler.processFunction(row, j, funcName, hash, strVal, format);
                              } else if (Constants.CLOTHES_FEATURE_EXTRACT_V1 == hash ||
                                      Constants.CLOTHES_ATTRIBUTE_EXTRACT_V1 == hash ||
                                      Constants.GENERIC_FEATURE_EXTRACT_V1 == hash ||
                                      Constants.TEXT_FEATURE_EXTRACT_V1 == hash ||
                                      Constants.FACE_FEATURE_EXTRACT_V1 == hash) {
                                  String funcName = lexer.stringVal();
                                  lexer.nextTokenLParen();
  
                                  String urlVal;
                                  if (Token.LPAREN == lexer.token) {
                                      lexer.nextTokenValue();
                                      urlVal = lexer.stringVal();
                                      lexer.nextToken();
                                      accept(Token.RPAREN);
                                  } else {
                                      throw new ParserException("insert value error, " + lexer.info());
                                  }
  
                                  valueHandler.processFunction(row, j, funcName, hash, urlVal);
                              } else {
                                  throw new ParserException("insert value error, " + lexer.info());
                              }
                              break;
                          }
                          default:
                              throw new ParserException("insert value error, " + lexer.info());
                      }
  
                      if (lexer.token == Token.COMMA) {
                          if (lexer.ch == '\'') { // for performance
                              lexer.bufPos = 0;
                              lexer.scanString();
                          } else if (lexer.ch == '0') {
                              lexer.bufPos = 0;
                              if (lexer.charAt(lexer.pos + 1) == 'x') {
                                  lexer.scanChar();
                                  lexer.scanChar();
                                  lexer.scanHexaDecimal();
                              } else {
                                  lexer.scanNumber();
                              }
                          } else if (lexer.ch > '0' && lexer.ch <= '9') {
                              lexer.bufPos = 0;
                              lexer.scanNumber();
                          } else if (lexer.ch == '-' && lexer.charAt(lexer.pos + 1) != '-') {
                              lexer.bufPos = 0;
                              lexer.scanNumber();
                          } else {
                              lexer.nextTokenValue();
                          }
                          continue;
                      } else if (lexer.token == Token.RPAREN) {
                          break;
                      } else {
                          throw new ParserException("insert value error, " + lexer.info());
  //
  //                        if (lexer.token == Token.COMMA) {
  //                            lexer.nextTokenValue();
  //                            continue;
  //                        } else {
  //                            break;
  //                        }
                      }
                  } // for j
  
                  valueHandler.processRow(row);
              }
  
              if (lexer.token != Token.RPAREN) {
                  throw new ParserException("syntax error. " + lexer.info());
              }
  
              lexer.nextTokenComma();
              while (lexer.token == Token.HINT) {
                  this.exprParser.parseHints();
              }
              if (lexer.token == Token.COMMA) {
                  lexer.nextTokenLParen();
                  continue;
              } else {
                  valueHandler.processComplete();
                  break;
              }
          }
      }
  
      protected void parseValueClause(
              List<SQLInsertStatement.ValuesClause> valueClauseList,
              List<SQLColumnDefinition> columnDefinitionList,
              int columnSize,
              SQLObject parent
      ) {
          final boolean optimizedForParameterized = lexer.isEnabled(SQLParserFeature.OptimizedForForParameterizedSkipValue);
  
          SQLInsertStatement.ValuesClause values;
          for (int i = 0; ; ++i) {
              int startPos = lexer.pos - 1;
  
              if (lexer.token == Token.ROW) {
                  lexer.nextToken();
              }
              if (lexer.token != Token.LPAREN) {
                  throw new ParserException("syntax error, expect ')', " + lexer.info());
              }
  //            lexer.nextTokenValue();
  
              if (lexer.ch == '\'') { // for performance
                  lexer.bufPos = 0;
                  lexer.scanString();
              } else if (lexer.ch == '0') {
                  lexer.bufPos = 0;
                  if (lexer.charAt(lexer.pos + 1) == 'x') {
                      lexer.scanChar();
                      lexer.scanChar();
                      lexer.scanHexaDecimal();
                  } else {
                      lexer.scanNumber();
                  }
              } else if (lexer.ch > '0' && lexer.ch <= '9') {
                  lexer.bufPos = 0;
                  lexer.scanNumber();
              } else if (lexer.ch == '-' && lexer.charAt(lexer.pos + 1) != '-') {
                  lexer.bufPos = 0;
                  lexer.scanNumber();
              } else {
                  lexer.nextTokenValue();
              }
  
              if (lexer.token() != Token.RPAREN) {
                  List valueExprList;
                  if (columnSize > 0) {
                      valueExprList = new ArrayList(columnSize);
                  } else {
                      valueExprList = new ArrayList();
                  }
                  values = new SQLInsertStatement.ValuesClause(valueExprList, parent);
  
                  for (int j = 0; ; ++j) {
                      SQLExpr expr;
  
                      SQLColumnDefinition columnDefinition = null;
                      if (columnDefinitionList != null && j < columnDefinitionList.size()) {
                          columnDefinition = columnDefinitionList.get(j);
                      }
  
                      SQLDataType dataType = null;
                      if (columnDefinition != null) {
                          dataType = columnDefinition.getDataType();
                      }
  
                      if (lexer.token == Token.LITERAL_INT) {
                          if (optimizedForParameterized) {
                              expr = new SQLVariantRefExpr("?", values);
                              values.incrementReplaceCount();
                          } else {
                              expr = new SQLIntegerExpr(lexer.integerValue(), values);
                          }
                          if (lexer.ch == ',') {
                              lexer.ch = lexer.charAt(++lexer.pos);
                              lexer.token = COMMA;
                          } else {
                              lexer.nextTokenCommaValue();
                          }
  
                          if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                              expr.setParent(values);
                          }
                      } else if (lexer.token == Token.LITERAL_CHARS) {
                          if (optimizedForParameterized) {
                              expr = new SQLVariantRefExpr("?", values);
                              values.incrementReplaceCount();
                          } else {
                              expr = new SQLCharExpr(lexer.stringVal(), values);
                          }
  
                          if (lexer.ch == ',') {
                              lexer.ch = lexer.charAt(++lexer.pos);
                              lexer.token = COMMA;
                          } else {
                              lexer.nextTokenCommaValue();
                          }
  
                          if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                              expr.setParent(values);
                          }
                      } else if (lexer.token == Token.LITERAL_NCHARS) {
                          if (optimizedForParameterized) {
                              expr = new SQLVariantRefExpr("?", values);
                              values.incrementReplaceCount();
                          } else {
                              expr = new SQLNCharExpr(lexer.stringVal(), values);
                          }
  
                          if (lexer.ch == ',') {
                              lexer.ch = lexer.charAt(++lexer.pos);
                              lexer.token = COMMA;
                          } else {
                              lexer.nextTokenCommaValue();
                          }
  
                          if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                              expr.setParent(values);
                          }
                      } else if (lexer.token == Token.LITERAL_FLOAT) {
                          if (optimizedForParameterized) {
                              expr = new SQLVariantRefExpr("?", values);
                              values.incrementReplaceCount();
                          } else {
                              SQLNumberExpr numberExpr = lexer.numberExpr(values);
  
                              if (dataType != null
                                      && dataType.nameHashCode64() == Constants.DECIMAL) {
                                  Number number = numberExpr.getNumber();
  
                                  int precision = 0, scale = 0;
                                  List<SQLExpr> arguments = dataType.getArguments();
                                  if (arguments.size() > 0) {
                                      SQLExpr arg0 = arguments.get(0);
                                      if (arg0 instanceof SQLIntegerExpr) {
                                          precision = ((SQLIntegerExpr) arg0).getNumber().intValue();
                                      }
                                  }
                                  if (arguments.size() > 1) {
                                      SQLExpr arg0 = arguments.get(1);
                                      if (arg0 instanceof SQLIntegerExpr) {
                                          scale = ((SQLIntegerExpr) arg0).getNumber().intValue();
                                      }
                                  }
  
                                  if (number instanceof BigDecimal) {
                                      number = MySqlUtils.decimal((BigDecimal) number, precision, scale);
                                      numberExpr.setNumber(number);
                                  }
                              }
  
                              expr = numberExpr;
                          }
  
                          if (lexer.ch == ',') {
                              lexer.ch = lexer.charAt(++lexer.pos);
                              lexer.token = COMMA;
                          } else {
                              lexer.nextTokenCommaValue();
                          }
  
                          if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                              expr.setParent(values);
                          }
                      } else if (lexer.token == Token.NULL) {
                          if (optimizedForParameterized) {
                              expr = new SQLVariantRefExpr("?", parent);
                              values.incrementReplaceCount();
                          } else {
                              expr = new SQLNullExpr(parent);
                          }
                          lexer.nextTokenCommaValue();
                          if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                              expr.setParent(values);
                          }
                      } else {
                          expr = exprParser.expr();
                          expr.setParent(values);
                      }
  
                      if (lexer.token == Token.COMMA) {
                          valueExprList.add(expr);
  
                          if (lexer.ch == '\'') { // for performance
                              lexer.bufPos = 0;
                              lexer.scanString();
                          } else if (lexer.ch == '0') {
                              lexer.bufPos = 0;
                              if (lexer.charAt(lexer.pos + 1) == 'x') {
                                  lexer.scanChar();
                                  lexer.scanChar();
                                  lexer.scanHexaDecimal();
                              } else {
                                  lexer.scanNumber();
                              }
                          } else if (lexer.ch > '0' && lexer.ch <= '9') {
                              lexer.bufPos = 0;
                              lexer.scanNumber();
                          } else if (lexer.ch == '-' && lexer.charAt(lexer.pos + 1) != '-') {
                              lexer.bufPos = 0;
                              lexer.scanNumber();
                          } else {
                              lexer.nextTokenValue();
                          }
                          continue;
                      } else if (lexer.token == Token.RPAREN) {
                          valueExprList.add(expr);
                          break;
                      } else {
                          expr = this.exprParser.primaryRest(expr);
                          if (lexer.token != Token.COMMA && lexer.token() != Token.RPAREN) {
                              expr = this.exprParser.exprRest(expr);
                          }
                          expr.setParent(values);
  
                          valueExprList.add(expr);
                          if (lexer.token == Token.COMMA) {
                              lexer.nextTokenValue();
                              continue;
                          } else {
                              break;
                          }
                      }
                  }
  
                  if (lexer.isEnabled(SQLParserFeature.KeepInsertValueClauseOriginalString)) {
                      int endPos = lexer.pos();
                      String orginalString = lexer.subString(startPos, endPos - startPos);
                      values.setOriginalString(orginalString);
                  }
              } else {
                  values = new SQLInsertStatement.ValuesClause(new ArrayList<SQLExpr>(0));
              }
  
              valueClauseList.add(values);
  
              if (lexer.token != Token.RPAREN) {
                  throw new ParserException("syntax error. " + lexer.info());
              }
  
              if (!parseCompleteValues && valueClauseList.size() >= parseValuesSize) {
                  lexer.skipToEOF();
                  break;
              }
  
              lexer.nextTokenComma();
              while (lexer.token == Token.HINT) {
                  this.exprParser.parseHints();
              }
              if (lexer.token == Token.COMMA) {
                  lexer.nextTokenLParen();
                  if (values != null) {
                      columnSize = values.getValues().size();
                  }
                  continue;
              } else {
                  break;
              }
          }
      }
  
      public SQLSelectListCache getSelectListCache() {
          return selectListCache;
      }
  
      public void setSelectListCache(SQLSelectListCache selectListCache) {
          this.selectListCache = selectListCache;
      }
  
      protected HiveInsertStatement parseHiveInsertStmt() {
          HiveInsertStatement insert = new HiveInsertStatement();
          insert.setDbType(dbType);
  
          if (lexer.isKeepComments() && lexer.hasComment()) {
              insert.addInsertBeforeComment(lexer.readAndResetComments());
          }
  
          SQLSelectParser selectParser = createSQLSelectParser();
  
          accept(Token.INSERT);
  
          if (lexer.token == Token.INTO) {
              lexer.nextToken();
          } else {
              accept(Token.OVERWRITE);
              insert.setOverwrite(true);
          }
  
          if (lexer.token == Token.TABLE) {
              lexer.nextToken();
          }
          insert.setTableSource(this.exprParser.name());
  
          boolean columnsParsed = false;
  
          if (lexer.token == (Token.LPAREN)) {
              Lexer.SavePoint mark = lexer.mark();
              lexer.nextToken();
              if (lexer.token == Token.SELECT || lexer.token == WITH) {
                  lexer.reset(mark);
              } else {
                  parseInsertColumns(insert);
                  columnsParsed = true;
                  accept(Token.RPAREN);
              }
          }
  
          if (lexer.token == Token.PARTITION) {
              lexer.nextToken();
              accept(Token.LPAREN);
              for (; ; ) {
                  SQLAssignItem ptExpr = new SQLAssignItem();
                  ptExpr.setTarget(this.exprParser.name());
                  if (lexer.token == Token.EQ || lexer.token == Token.EQEQ) {
                      lexer.nextTokenValue();
                      SQLExpr ptValue = this.exprParser.expr();
                      ptExpr.setValue(ptValue);
                  }
                  insert.addPartition(ptExpr);
                  if (!(lexer.token == (Token.COMMA))) {
                      break;
                  } else {
                      lexer.nextToken();
                  }
              }
              accept(Token.RPAREN);
          }
  
          if (!columnsParsed && lexer.token == Token.LPAREN) {
              Lexer.SavePoint m1 = lexer.mark();
  
              lexer.nextToken();
              boolean select;
              if (lexer.token == LPAREN) {
                  Lexer.SavePoint m2 = lexer.mark();
                  lexer.nextToken();
                  select = lexer.token == SELECT;
                  lexer.reset(m2);
              } else {
                  select = lexer.token == SELECT || lexer.token == WITH;
              }
              if (!select) {
                  parseInsertColumns(insert);
                  accept(Token.RPAREN);
              } else {
                  lexer.reset(m1);
              }
          }
  
          if (lexer.token == Token.IF) {
              lexer.nextToken();
              accept(Token.NOT);
              accept(Token.EXISTS);
              insert.setIfNotExists(true);
          }
  
          if (lexer.token == Token.VALUES) {
              lexer.nextToken();
  
              for (; ; ) {
                  if (lexer.token == Token.LPAREN) {
                      lexer.nextToken();
  
                      SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
                      this.exprParser.exprList(values.getValues(), values);
                      insert.addValueCause(values);
                      accept(Token.RPAREN);
                  }
  
                  if (lexer.token == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  } else {
                      break;
                  }
              }
          } else {
              SQLSelect query = selectParser.select();
              insert.setQuery(query);
          }
  
          return insert;
      }
  
      protected HiveInsert parseHiveInsert() {
          HiveInsert insert = new HiveInsert();
  
          if (lexer.isKeepComments() && lexer.hasComment()) {
              insert.addBeforeComment(lexer.readAndResetComments());
          }
  
          SQLSelectParser selectParser = createSQLSelectParser();
  
          accept(Token.INSERT);
  
          if (lexer.token == Token.INTO) {
              lexer.nextToken();
          } else {
              accept(Token.OVERWRITE);
              insert.setOverwrite(true);
          }
  
          if (lexer.token == Token.TABLE) {
              lexer.nextToken();
          }
          insert.setTableSource(this.exprParser.name());
  
          if (lexer.token == Token.PARTITION) {
              lexer.nextToken();
              accept(Token.LPAREN);
              for (; ; ) {
                  SQLAssignItem ptExpr = new SQLAssignItem();
                  ptExpr.setTarget(this.exprParser.name());
                  if (lexer.token == Token.EQ) {
                      lexer.nextToken();
                      SQLExpr ptValue = this.exprParser.expr();
                      ptExpr.setValue(ptValue);
                  }
                  insert.addPartition(ptExpr);
                  if (lexer.token != Token.COMMA) {
                      break;
                  } else {
                      lexer.nextToken();
                  }
              }
              accept(Token.RPAREN);
          }
  
          if (lexer.token == LPAREN) {
              lexer.nextToken();
              this.exprParser.exprList(insert.getColumns(), insert);
              accept(RPAREN);
          }
  
          if (lexer.token == Token.VALUES) {
              lexer.nextToken();
  
              for (; ; ) {
                  if (lexer.token == Token.LPAREN) {
                      lexer.nextToken();
  
                      SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
                      this.exprParser.exprList(values.getValues(), values);
                      insert.addValueCause(values);
                      accept(Token.RPAREN);
                  }
  
                  if (lexer.token == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  } else {
                      break;
                  }
              }
          } else {
              SQLSelect query = selectParser.select();
              insert.setQuery(query);
          }
  
          return insert;
      }
  
      protected SQLShowDatabasesStatement parseShowDatabases(boolean isPhysical) {
          SQLShowDatabasesStatement stmt = new SQLShowDatabasesStatement();
  
          stmt.setPhysical(isPhysical);
          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);
          }
  
          if (lexer.identifierEquals(Constants.EXTRA)) {
              lexer.nextToken();
              stmt.setExtra(true);
          }
  
          return stmt;
      }
  
      protected SQLShowTableGroupsStatement parseShowTableGroups() {
          SQLShowTableGroupsStatement stmt = new SQLShowTableGroupsStatement();
  
          if (lexer.token == Token.IN) {
              lexer.nextToken();
              SQLName db = exprParser.name();
              stmt.setDatabase(db);
          }
  
          return stmt;
      }
  
      protected SQLShowTablesStatement parseShowTables() {
          SQLShowTablesStatement stmt = new SQLShowTablesStatement();
  
          if (lexer.identifierEquals(Constants.SHOW)) {
              lexer.nextToken();
          }
  
          if (lexer.identifierEquals(Constants.TABLES)) {
              lexer.nextToken();
          }
  
          if (lexer.identifierEquals(Constants.EXTENDED)) {
              lexer.nextToken();
              stmt.setExtended(true);
          }
  
          if (lexer.token == Token.FROM || lexer.token == Token.IN) {
              lexer.nextToken();
              SQLName database = exprParser.name();
              if (lexer.token == Token.SUB && database instanceof SQLIdentifierExpr) {
                  lexer.mark();
                  lexer.nextToken();
                  String strVal = lexer.stringVal();
                  lexer.nextToken();
                  if (database instanceof SQLIdentifierExpr) {
                      SQLIdentifierExpr ident = (SQLIdentifierExpr) database;
                      database = new SQLIdentifierExpr(ident.getName() + "-" + strVal);
                  }
              }
              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;
      }
  
      protected SQLShowColumnsStatement parseShowColumns() {
          SQLShowColumnsStatement stmt = new SQLShowColumnsStatement();
  
          if (lexer.token == Token.FROM) {
              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);
              }
          } else if (lexer.token == Token.IN) {
              lexer.nextToken();
              SQLName table = exprParser.name();
              stmt.setTable(table);
          }
  
          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;
      }
  
      protected SQLStatement alterIndex() {
          accept(Token.ALTER);
          lexer.nextToken();
          SQLAlterIndexStatement stmt = new SQLAlterIndexStatement();
          stmt.setName(this.exprParser.name());
  
          if (lexer.identifierEquals("RENAME")) {
              lexer.nextToken();
              accept(Token.TO);
              stmt.setRenameTo(this.exprParser.name());
          }
  
          if (lexer.token == Token.ON) {
              lexer.nextToken();
              if (lexer.token == Token.TABLE) {
                  lexer.nextToken();
              }
              SQLName table = this.exprParser.name();
              stmt.setTable(table);
          }
  
          if (lexer.token == Token.PARTITION) {
              lexer.nextToken();
              accept(Token.LPAREN);
              parseAssignItems(stmt.getPartitions(), stmt);
              accept(Token.RPAREN);
          }
  
          if (lexer.identifierEquals(Constants.DBPARTITION)) {
              SQLPartitionBy partitionClause = this.getSQLCreateTableParser().parsePartitionBy();
              stmt.setDbPartitionBy(partitionClause);
          }
  
          if (lexer.token == Token.ENABLE) {
              lexer.nextToken();
              stmt.setEnable(true);
          }
  
          if (lexer.token == Token.DISABLE) {
              lexer.nextToken();
              stmt.setEnable(false);
          }
          if (lexer.hashLCase == Constants.UNUSABLE) {
              lexer.nextToken();
              stmt.setUnusable(true);
          }
  
          for (; ; ) {
              if (lexer.identifierEquals("rebuild")) {
                  lexer.nextToken();
  
                  SQLAlterIndexStatement.Rebuild rebuild = new SQLAlterIndexStatement.Rebuild();
                  stmt.setRebuild(rebuild);
                  continue;
              } else if (lexer.identifierEquals("MONITORING")) {
                  lexer.nextToken();
                  acceptIdentifier("USAGE");
                  stmt.setMonitoringUsage(Boolean.TRUE);
                  continue;
              } else if (lexer.identifierEquals("PARALLEL")) {
                  lexer.nextToken();
                  stmt.setParallel(this.exprParser.expr());
              }
              break;
          }
  
          return stmt;
      }
  
      protected SQLStatement parseAnalyze() {
          lexer.nextToken();
          accept(Token.TABLE);
  
          SQLAnalyzeTableStatement stmt = new SQLAnalyzeTableStatement();
  
          SQLName table = this.exprParser.name();
          stmt.setTable(table);
  
          if (lexer.token() == Token.PARTITION) {
              stmt.setPartition(parsePartitionRef());
          }
  
          accept(Token.COMPUTE);
          acceptIdentifier("STATISTICS");
          stmt.setComputeStatistics(true);
  
          if (lexer.token == Token.FOR) {
              lexer.nextToken();
              acceptIdentifier("COLUMNS");
              stmt.setForColums(true);
  
              if (lexer.token == LPAREN) {
                  lexer.nextToken();
                  this.exprParser.names(stmt.getColumns(), stmt);
                  accept(RPAREN);
              }
          }
  
          if (lexer.identifierEquals(Constants.CACHE)) {
              lexer.nextToken();
              acceptIdentifier("METADATA");
              stmt.setCacheMetadata(true);
          }
  
          if (lexer.identifierEquals(Constants.NOSCAN)) {
              lexer.nextToken();
              stmt.setNoscan(true);
          }
  
          return stmt;
      }
  
      public SQLAlterSequenceStatement alterSequence() {
          accept(Token.ALTER);
  
          accept(Token.SEQUENCE);
  
          SQLAlterSequenceStatement stmt = new SQLAlterSequenceStatement();
          stmt.setDbType(dbType);
          stmt.setName(this.exprParser.name());
  
          if (lexer.identifierEquals(Constants.CHANGE)) {
              lexer.nextToken();
              accept(Token.TO);
  
              if (lexer.identifierEquals(Constants.SIMPLE)) {
                  stmt.setChangeToSimple(true);
                  lexer.nextToken();
                  if (lexer.hashLCase() == Constants.WITH) {
                      lexer.nextToken();
                      accept(Token.CACHE);
                      stmt.setWithCache(true);
                  }
              } else if (lexer.token == Token.GROUP) {
                  stmt.setChangeToGroup(true);
                  lexer.nextToken();
              } else if (lexer.identifierEquals(Constants.TIME)) {
                  stmt.setChangeToTime(true);
                  lexer.nextToken();
              } else {
                  throw new ParserException("TODO " + lexer.info());
              }
          }
  
          for (; ; ) {
              if (lexer.token() == Token.START || lexer.identifierEquals(Constants.START)) {
                  lexer.nextToken();
                  accept(Token.WITH);
                  stmt.setStartWith(this.exprParser.expr());
                  continue;
              } else if (lexer.identifierEquals("INCREMENT")) {
                  lexer.nextToken();
                  accept(Token.BY);
                  stmt.setIncrementBy(this.exprParser.expr());
                  continue;
              } else if (lexer.token() == Token.CACHE) {
                  lexer.nextToken();
                  stmt.setCache(Boolean.TRUE);
  
                  if (lexer.token() == Token.LITERAL_INT || lexer.token() == Token.QUES) {
                      stmt.setCacheValue(this.exprParser.primary());
                  }
  
                  continue;
              } else if (lexer.token() == Token.NOCACHE) {
                  lexer.nextToken();
                  stmt.setCache(Boolean.FALSE);
                  continue;
              } else if (lexer.token() == Token.ORDER) {
                  lexer.nextToken();
                  stmt.setOrder(Boolean.TRUE);
                  continue;
              } else if (lexer.identifierEquals(Constants.RESTART)) {
                  lexer.nextToken();
                  stmt.setRestart(true);
  
                  if (lexer.token == Token.WITH || lexer.token == Token.EQ) {
                      lexer.nextToken();
                      stmt.setRestartWith(this.exprParser.primary());
                  } else if (lexer.token == LITERAL_INT) {
                      stmt.setRestartWith(this.exprParser.primary());
                  }
  
                  continue;
              } else if (lexer.identifierEquals("NOORDER")) {
                  lexer.nextToken();
                  stmt.setOrder(Boolean.FALSE);
                  continue;
              } else if (lexer.identifierEquals("CYCLE")) {
                  lexer.nextToken();
                  stmt.setCycle(Boolean.TRUE);
                  continue;
              } else if (lexer.identifierEquals("NOCYCLE")) {
                  lexer.nextToken();
                  stmt.setCycle(Boolean.FALSE);
                  continue;
              } else if (lexer.identifierEquals("MINVALUE")) {
                  lexer.nextToken();
                  stmt.setMinValue(this.exprParser.expr());
                  continue;
              } else if (lexer.identifierEquals("MAXVALUE")) {
                  lexer.nextToken();
                  stmt.setMaxValue(this.exprParser.expr());
                  continue;
              } else if (lexer.identifierEquals("NOMAXVALUE")) {
                  lexer.nextToken();
                  stmt.setNoMaxValue(true);
                  continue;
              } else if (lexer.identifierEquals("NOMINVALUE")) {
                  lexer.nextToken();
                  stmt.setNoMinValue(true);
                  continue;
              }
              break;
          }
  
          return stmt;
      }
  
      protected SQLStatement parseMsck() {
          lexer.nextToken();
          if (lexer.identifierEquals("REPAIR")) {
              lexer.nextToken();
          }
  
          HiveMsckRepairStatement stmt = new HiveMsckRepairStatement();
  
          if (lexer.token() == Token.DATABASE || lexer.token() == Token.SCHEMA) {
              lexer.nextToken();
              SQLName name = this.exprParser.name();
              stmt.setDatabase(name);
          }
  
          if (lexer.token() == Token.TABLE) {
              lexer.nextToken();
  
              SQLExpr tableExpr = this.exprParser.expr();
              stmt.setTable(tableExpr);
          }
  
          if (lexer.identifierEquals(Constants.ADD)) {
              lexer.nextToken();
              acceptIdentifier("PARTITIONS");
              stmt.setAddPartitions(true);
          }
  
          return stmt;
      }
  
      protected SQLStatement createResource() {
          accept(Token.CREATE);
          acceptIdentifier("RESOURCE");
          accept(Token.GROUP);
  
          SQLCreateResourceGroupStatement stmt = new SQLCreateResourceGroupStatement();
          stmt.setName(this.exprParser.name());
  
          for (; ; ) {
              if (lexer.token() == Token.SEMI || lexer.token() == Token.EOF) {
                  break;
              }
              if (lexer.token() == Token.ENABLE) {
                  lexer.nextToken();
                  stmt.setEnable(true);
              } else if (lexer.token() == Token.DISABLE) {
                  lexer.nextToken();
                  stmt.setEnable(false);
              }
  
              Lexer.SavePoint m = lexer.mark();
              String name = lexer.stringVal();
              lexer.nextToken();
              if (lexer.token() == Token.EQ) {
                  lexer.nextToken();
                  SQLExpr value = this.exprParser.expr();
                  if (lexer.token() == Token.COMMA) {
                      SQLListExpr list = new SQLListExpr();
                      list.addItem(value);
                      while (lexer.token() == Token.COMMA) {
                          lexer.nextToken();
                          list.addItem(this.exprParser.expr());
                      }
                      stmt.addProperty(name, list);
                  } else {
                      stmt.addProperty(name, value);
                  }
              } else {
                  lexer.reset(m);
                  break;
              }
          }
  
          return stmt;
      }
  
      protected SQLStatement alterResourceGroup() {
          accept(ALTER);
          acceptIdentifier("RESOURCE");
          accept(Token.GROUP);
  
          SQLAlterResourceGroupStatement stmt = new SQLAlterResourceGroupStatement();
          stmt.setName(this.exprParser.name());
  
          for (; ; ) {
              if (lexer.token() == Token.SEMI || lexer.token() == Token.EOF) {
                  break;
              }
  
              if (lexer.token() == Token.ENABLE) {
                  lexer.nextToken();
                  stmt.setEnable(true);
              } else if (lexer.token() == Token.DISABLE) {
                  lexer.nextToken();
                  stmt.setEnable(false);
              }
  
              Lexer.SavePoint m = lexer.mark();
              String name = lexer.stringVal();
              lexer.nextToken();
              if (lexer.token() == Token.EQ) {
                  lexer.nextToken();
                  SQLExpr value = this.exprParser.expr();
                  if (lexer.token() == Token.COMMA) {
                      SQLListExpr list = new SQLListExpr();
                      list.addItem(value);
                      while (lexer.token() == Token.COMMA) {
                          lexer.nextToken();
                          list.addItem(this.exprParser.expr());
                      }
                      stmt.addProperty(name, list);
                  } else {
                      stmt.addProperty(name, value);
                  }
              } else {
                  lexer.reset(m);
                  break;
              }
  
          }
  
          return stmt;
      }
  
      public SQLStatement alterMaterialized() {
          SQLAlterMaterializedViewStatement stmt = new SQLAlterMaterializedViewStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == ALTER) {
              lexer.nextToken();
          }
  
          acceptIdentifier("MATERIALIZED");
          accept(Token.VIEW);
  
          stmt.setName(this.exprParser.name());
  
          if (lexer.token() == Token.EOF) {
              throw new ParserException("syntax error. " + lexer.info());
          }
  
          for (; ; ) {
              if (lexer.identifierEquals("REFRESH")) {
                  lexer.nextToken();
  
                  if (lexer.token() == Token.EOF) {
                      throw new ParserException("syntax error. " + lexer.info());
                  }
  
                  boolean refresh = false;
                  for (; ; ) {
                      if (lexer.identifierEquals("FAST")) {
                          lexer.nextToken();
                          stmt.setRefreshFast(true);
  
                          refresh = true;
                      } else if (lexer.identifierEquals("COMPLETE")) {
                          lexer.nextToken();
                          stmt.setRefreshComplete(true);
  
                          refresh = true;
                      } else if (lexer.identifierEquals(Constants.FORCE)) {
                          lexer.nextToken();
                          stmt.setRefreshForce(true);
  
                          refresh = true;
                      } else if (lexer.token == Token.ON) {
                          lexer.nextToken();
                          if (lexer.token == Token.COMMIT || lexer.identifierEquals(Constants.COMMIT)) {
                              lexer.nextToken();
                              stmt.setRefreshOnCommit(true);
                          } else if (lexer.identifierEquals(Constants.OVERWRITE)) {
                              lexer.nextToken();
                              stmt.setRefreshOnOverWrite(true);
                          } else {
                              acceptIdentifier("DEMAND");
                              stmt.setRefreshOnDemand(true);
                          }
  
                          refresh = true;
                      } else if (lexer.identifierEquals(Constants.START)) {
                          lexer.nextToken();
                          accept(Token.WITH);
                          SQLExpr startWith = this.exprParser.expr();
                          stmt.setStartWith(startWith);
                          stmt.setRefreshStartWith(true);
  
                          refresh = true;
                      } else if (lexer.identifierEquals(Constants.NEXT)) {
                          lexer.nextToken();
                          SQLExpr next = this.exprParser.expr();
                          stmt.setNext(next);
                          stmt.setRefreshNext(true);
  
                          refresh = true;
                      } else {
                          break;
                      }
                  }
                  if (!refresh) {
                      throw new ParserException("refresh clause is empty. " + lexer.info());
                  }
              } else if (lexer.identifierEquals("REBUILD")) {
                  lexer.nextToken();
                  stmt.setRebuild(true);
                  continue;
              } else {
                  break;
              }
          }
  
          Boolean enableQueryRewrite = null;
          if (lexer.token == Token.ENABLE) {
              lexer.nextToken();
              enableQueryRewrite = true;
          }
  
          if (lexer.token == Token.DISABLE) {
              lexer.nextToken();
              enableQueryRewrite = false;
          }
  
          if (enableQueryRewrite != null) {
              acceptIdentifier("QUERY");
              acceptIdentifier("REWRITE");
              stmt.setEnableQueryRewrite(enableQueryRewrite);
          }
  
          if (lexer.token == PARTITION) {
              lexer.nextToken();
              accept(LPAREN);
              this.exprParser.exprList(stmt.getPartitions(), stmt);
              accept(RPAREN);
          }
  
          return stmt;
      }
  
      public SQLCreateFunctionStatement parseHiveCreateFunction() {
          HiveCreateFunctionStatement stmt = new HiveCreateFunctionStatement();
          stmt.setDbType(dbType);
  
          if (lexer.token == CREATE) {
              lexer.nextToken();
          }
  
          if (lexer.token == OR) {
              lexer.nextToken();
              accept(REPLACE);
              stmt.setOrReplace(true);
          }
  
          if (lexer.identifierEquals(Constants.TEMPORARY)) {
              lexer.nextToken();
              stmt.setTemporary(true);
          }
  
          boolean sql = false; // TODO for odps
          if (lexer.identifierEquals(Constants.SQL)) {
              lexer.nextToken();
              sql = true;
          }
  
          accept(Token.FUNCTION);
  
          if (lexer.token == IF) {
              lexer.nextToken();
              accept(NOT);
              accept(EXISTS);
              stmt.setIfNotExists(true);
          }
  
          SQLName name = this.exprParser.name();
          stmt.setName(name);
  
          parameters(stmt.getParameters(), stmt);
  
          if (lexer.identifierEquals(Constants.RETURNS)) {
              lexer.nextToken();
              if (lexer.token == VARIANT) {
                  lexer.nextToken(); // TODO
              }
              SQLDataType returnDataType = this.exprParser.parseDataType();
              stmt.setReturnDataType(returnDataType);
          }
  
          if (lexer.token == IDENTIFIER && lexer.stringVal().toUpperCase().startsWith("RETURNS@")) {
              lexer.nextToken();
              SQLDataType returnDataType = this.exprParser.parseDataType();
              stmt.setReturnDataType(returnDataType);
          }
  
          if (lexer.token() == Token.AS) {
              lexer.setToken(Token.IDENTIFIER);
              lexer.nextToken();
              if (lexer.token != BEGIN && !lexer.identifierEquals(Constants.BEGIN)) {
                  SQLExpr className = this.exprParser.expr();
                  stmt.setClassName(className);
              }
          }
  
          if (lexer.identifierEquals(Constants.LOCATION)) {
              lexer.nextToken();
              SQLExpr location = this.exprParser.primary();
              stmt.setLocation(location);
          }
  
          if (lexer.identifierEquals(Constants.SYMBOL)) {
              lexer.nextToken();
              accept(Token.EQ);
              SQLExpr symbol = this.exprParser.primary();
              stmt.setSymbol(symbol);
          }
  
          if (lexer.token() == Token.USING || lexer.hashLCase() == Constants.USING) {
              lexer.nextToken();
  
              if (lexer.identifierEquals(Constants.JAR)) {
                  lexer.nextToken();
                  stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.JAR);
              } else if (lexer.identifierEquals(Constants.ARCHIVE)) {
                  lexer.nextToken();
                  stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.ARCHIVE);
              } else if (lexer.identifierEquals(Constants.FILE)) {
                  lexer.nextToken();
                  stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.FILE);
              } else if (lexer.token == Token.CODE) {
                  stmt.setCode(lexer.stringVal());
                  lexer.nextToken();
                  stmt.setResourceType(HiveCreateFunctionStatement.ResourceType.CODE);
                  return stmt;
              }
  
              SQLExpr location = this.exprParser.primary();
              stmt.setLocation(location);
          }
  
          return stmt;
      }
  
      protected void parameters(List<SQLParameter> parameters, SQLObject parent) {
          if (lexer.token == LPAREN) {
              lexer.nextToken();
              while (lexer.token != RPAREN) {
                  SQLParameter param = new SQLParameter();
                  param.setName(this.exprParser.name());
                  param.setDataType(this.exprParser.parseDataType());
                  if (lexer.token == COMMA) {
                      lexer.nextToken();
                  }
                  parameters.add(param);
                  param.setParent(parent);
              }
              accept(RPAREN);
          }
      }
  
      protected SQLShowCreateTableStatement parseShowCreateTable() {
          lexer.nextToken();
          accept(Token.TABLE);
  
          SQLShowCreateTableStatement stmt = new SQLShowCreateTableStatement();
          stmt.setDbType(dbType);
          stmt.setName(this.exprParser.name());
  
          if (lexer.token() == Token.LIKE) {
              lexer.nextToken();
              if (lexer.identifierEquals(Constants.MAPPING)) {
                  lexer.nextToken();
                  accept(Token.LPAREN);
                  SQLName name = this.exprParser.name();
                  stmt.setLikeMapping(name);
                  accept(Token.RPAREN);
              }
          }
          return stmt;
      }
  
      public SQLShowVariantsStatement parseShowVariants() {
          SQLShowVariantsStatement stmt = new SQLShowVariantsStatement();
          stmt.setDbType(dbType);
          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;
      }
  
      protected SQLExceptionStatement parseException() {
          accept(Token.EXCEPTION);
          SQLExceptionStatement stmt = new SQLExceptionStatement();
  
          for (; ; ) {
              accept(Token.WHEN);
              SQLExceptionStatement.Item item = new SQLExceptionStatement.Item();
              item.setWhen(this.exprParser.expr());
              accept(Token.THEN);
  
              this.parseStatementList(item.getStatements(), -1, item);
  
              stmt.addItem(item);
  
              if (lexer.token() == Token.SEMI) {
                  lexer.nextToken();
              }
  
              if (lexer.token() != Token.WHEN) {
                  break;
              }
          }
          return stmt;
      }
  
      public SQLResetStatement parseReset() {
          acceptIdentifier("RESET");
          SQLResetStatement stmt = new SQLResetStatement();
          for (; ; ) {
              if (lexer.token() == Token.IDENTIFIER) {
                  if (lexer.identifierEquals("QUERY")) {
                      lexer.nextToken();
                      accept(Token.CACHE);
                      stmt.getOptions().add("QUERY CACHE");
                  } else {
                      stmt.getOptions().add(lexer.stringVal());
                      lexer.nextToken();
                  }
  
                  if (lexer.token() == Token.COMMA) {
                      lexer.nextToken();
                      continue;
                  }
              }
              break;
          }
  
          return stmt;
      }
      public SQLComputeIncrementalStatsStatement parseCompute() {
          accept(COMPUTE);
          acceptIdentifier("INCREMENTAL");
          acceptIdentifier("STATS");
          SQLComputeIncrementalStatsStatement stmt = new SQLComputeIncrementalStatsStatement();
          stmt.setName(this.exprParser.expr());
          if (lexer.nextIf(PARTITION)) {
              stmt.setPartition(this.exprParser.expr());
          }
          return stmt;
      }
      public SQLStartTransactionStatement parseStart() {
          acceptIdentifier("START");
          acceptIdentifier("TRANSACTION");
  
          SQLStartTransactionStatement stmt = new SQLStartTransactionStatement(dbType);
  
          if (lexer.token() == WITH) {
              lexer.nextToken();
              acceptIdentifier("CONSISTENT");
              acceptIdentifier("SNAPSHOT");
              stmt.setConsistentSnapshot(true);
          }
  
          if (lexer.token() == Token.BEGIN) {
              lexer.nextToken();
              stmt.setBegin(true);
              if (lexer.identifierEquals("WORK")) {
                  lexer.nextToken();
                  stmt.setWork(true);
              }
          }
  
          if (lexer.token() == Token.HINT) {
              stmt.setHints(this.exprParser.parseHints());
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.ISOLATION)) {
              lexer.nextToken();
              acceptIdentifier("LEVEL");
  
              if (lexer.identifierEquals(FnvHash.Constants.READ)) {
                  lexer.nextToken();
                  if (lexer.identifierEquals(FnvHash.Constants.UNCOMMITTED)) {
                      lexer.nextToken();
                      stmt.setIsolationLevel(SQLStartTransactionStatement.IsolationLevel.READ_UNCOMMITTED);
                  } else if (lexer.identifierEquals(FnvHash.Constants.COMMITTED)) {
                      lexer.nextToken();
                      stmt.setIsolationLevel(SQLStartTransactionStatement.IsolationLevel.READ_COMMITTED);
                  } else {
                      throw new ParserException(lexer.info());
                  }
              } else if (lexer.identifierEquals(FnvHash.Constants.REPEATABLE)) {
                  lexer.nextToken();
                  acceptIdentifier("READ");
                  stmt.setIsolationLevel(SQLStartTransactionStatement.IsolationLevel.REPEATABLE_READ);
              } else if (lexer.identifierEquals(FnvHash.Constants.SERIALIZABLE)) {
                  lexer.nextToken();
                  stmt.setIsolationLevel(SQLStartTransactionStatement.IsolationLevel.SERIALIZABLE);
              } else {
                  throw new ParserException(lexer.info());
              }
          }
  
          if (lexer.identifierEquals(FnvHash.Constants.READ)) {
              lexer.nextToken();
              acceptIdentifier("ONLY");
              stmt.setReadOnly(true);
          }
  
          return stmt;
      }
      public SQLStatement parseClone() {
          throw new ParserException("TODO " + lexer.info());
      }
  }
  