package com.chenyang.druid.sql.parser;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLDataType;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLHint;
import com.chenyang.druid.sql.ast.SQLLimit;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLPartitionBy;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.SQLStatementImpl;
import com.chenyang.druid.sql.ast.expr.SQLAllColumnExpr;
import com.chenyang.druid.sql.ast.expr.SQLCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLDecimalExpr;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLIntegerExpr;
import com.chenyang.druid.sql.ast.expr.SQLListExpr;
import com.chenyang.druid.sql.ast.expr.SQLNCharExpr;
import com.chenyang.druid.sql.ast.expr.SQLNullExpr;
import com.chenyang.druid.sql.ast.expr.SQLNumberExpr;
import com.chenyang.druid.sql.ast.expr.SQLPropertyExpr;
import com.chenyang.druid.sql.ast.expr.SQLQueryExpr;
import com.chenyang.druid.sql.ast.expr.SQLVariantRefExpr;
import com.chenyang.druid.sql.ast.statement.SQLAlterIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterSequenceStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterSetDateFormatStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterSetNlsDateLanguageStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterSystemStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddClusteringKey;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAlterColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableArchivePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDeleteByCondition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDisableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDisableKeys;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDisableLifecycle;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropColumnItem;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropIndex;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropPartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropPrimaryKey;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDropSubpartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableEnableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableEnableKeys;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableEnableLifecycle;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableExchangePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableItem;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableMergePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablePartitionSetProperties;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRecoverPartitions;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRename;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRenameColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRenamePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableReplaceColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableSetComment;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableSetLifecycle;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableSetLocation;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableSetOption;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableSubpartitionAvailablePartitionNum;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableTouch;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableUnarchivePartition;
import com.chenyang.druid.sql.ast.statement.SQLAlterViewRenameStatement;
import com.chenyang.druid.sql.ast.statement.SQLAnalyzeTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLAssignItem;
import com.chenyang.druid.sql.ast.statement.SQLBlockStatement;
import com.chenyang.druid.sql.ast.statement.SQLCallStatement;
import com.chenyang.druid.sql.ast.statement.SQLCheck;
import com.chenyang.druid.sql.ast.statement.SQLCloseStatement;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCommentStatement;
import com.chenyang.druid.sql.ast.statement.SQLCommitStatement;
import com.chenyang.druid.sql.ast.statement.SQLConstraint;
import com.chenyang.druid.sql.ast.statement.SQLCreateContextStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateDirectoryStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateDomainStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateRoleStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateSequenceStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableGroupStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTablespaceStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateTriggerStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateUserStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDeleteStatement;
import com.chenyang.druid.sql.ast.statement.SQLDescribeStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropCatalogStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropContextStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropDirectoryStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropDomainStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropEventStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropLogFileGroupStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropOutlineStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropPackageStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropProcedureStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropResourceGroupStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropResourceStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropRoleStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropSequenceStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropServerStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropSynonymStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableGroupStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableSpaceStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropTriggerStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropUserStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLDumpStatement;
import com.chenyang.druid.sql.ast.statement.SQLErrorLoggingClause;
import com.chenyang.druid.sql.ast.statement.SQLExplainStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLFetchStatement;
import com.chenyang.druid.sql.ast.statement.SQLForStatement;
import com.chenyang.druid.sql.ast.statement.SQLGrantStatement;
import com.chenyang.druid.sql.ast.statement.SQLInsertInto;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLMergeStatement;
import com.chenyang.druid.sql.ast.statement.SQLObjectType;
import com.chenyang.druid.sql.ast.statement.SQLOpenStatement;
import com.chenyang.druid.sql.ast.statement.SQLPartitionRef;
import com.chenyang.druid.sql.ast.statement.SQLPrimaryKey;
import com.chenyang.druid.sql.ast.statement.SQLPrivilegeItem;
import com.chenyang.druid.sql.ast.statement.SQLPurgeLogsStatement;
import com.chenyang.druid.sql.ast.statement.SQLPurgeRecyclebinStatement;
import com.chenyang.druid.sql.ast.statement.SQLPurgeTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLRefreshMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLReleaseSavePointStatement;
import com.chenyang.druid.sql.ast.statement.SQLReturnStatement;
import com.chenyang.druid.sql.ast.statement.SQLRevokeStatement;
import com.chenyang.druid.sql.ast.statement.SQLRollbackStatement;
import com.chenyang.druid.sql.ast.statement.SQLSavePointStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.chenyang.druid.sql.ast.statement.SQLSelectStatement;
import com.chenyang.druid.sql.ast.statement.SQLSetSchemaStatement;
import com.chenyang.druid.sql.ast.statement.SQLSetStatement;
import com.chenyang.druid.sql.ast.statement.SQLSetTimeZoneStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowColumnsStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowCreateTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowDatabasesStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowMaterializedViewStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowTableGroupsStatement;
import com.chenyang.druid.sql.ast.statement.SQLShowTablesStatement;
import com.chenyang.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLTableElement;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTruncateStatement;
import com.chenyang.druid.sql.ast.statement.SQLUnique;
import com.chenyang.druid.sql.ast.statement.SQLUpdateSetItem;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.ast.statement.SQLUseStatement;
import com.chenyang.druid.sql.ast.statement.SQLValuesTableSource;
import com.chenyang.druid.sql.ast.statement.SQLWhoamiStatement;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.db2.ast.stmt.DB2DropSessionStatement;
import com.chenyang.druid.sql.dialect.db2.ast.stmt.DB2LockTableStatement;
import com.chenyang.druid.sql.dialect.dm.ast.clause.DmPartitionExtensionClause;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmInsertStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmSelectTableReference;
import com.chenyang.druid.sql.dialect.gauss.ast.stmt.GaussCommonStatement;
import com.chenyang.druid.sql.dialect.hive.ast.HiveInsert;
import com.chenyang.druid.sql.dialect.hive.ast.HiveMultiInsertStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveCreateFunctionStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveInsertStatement;
import com.chenyang.druid.sql.dialect.hive.ast.stmt.HiveMsckRepairStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.FullTextType;
import com.chenyang.druid.sql.dialect.mysql.ast.MySqlKey;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlCreateExternalCatalogStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlExplainPlanCacheStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlManageInstanceGroupStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlUpdatePlanCacheStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MysqlDropFullTextStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.SQLAlterResourceGroupStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.SQLCreateResourceGroupStatement;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.SQLListResourceGroupStatement;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlExprParser;
import com.chenyang.druid.sql.dialect.oracle.parser.OracleExprParser;
import com.chenyang.druid.sql.repository.SchemaRepository;
import com.chenyang.druid.util.FnvHash;
import com.chenyang.druid.util.JdbcUtils;
import com.chenyang.druid.util.MySqlUtils;
import com.chenyang.druid.util.StringUtils;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import java.util.UUID;

public class SQLStatementParser extends SQLParser {
   protected SchemaRepository repository;
   protected SQLExprParser exprParser;
   protected boolean parseCompleteValues;
   protected int parseValuesSize;
   protected SQLSelectListCache selectListCache;
   protected InsertColumnsCache insertColumnsCache;
   protected Timestamp now;
   protected Date currentDate;

   public SQLStatementParser(String sql) {
      this((String)sql, (DbType)null);
   }

   public SQLStatementParser(String sql, DbType dbType) {
      this(new SQLExprParser(sql, dbType, new SQLParserFeature[0]));
   }

   public SQLStatementParser(SQLExprParser exprParser) {
      super(exprParser.getLexer(), exprParser.getDbType());
      this.parseCompleteValues = true;
      this.parseValuesSize = 3;
      this.selectListCache = null;
      this.insertColumnsCache = null;
      this.exprParser = exprParser;
      this.dbType = exprParser.dbType;
   }

   public void setExprParser(SQLExprParser exprParser) {
      this.exprParser = exprParser;
   }

   protected SQLStatementParser(Lexer lexer, DbType dbType) {
      super(lexer, dbType);
      this.parseCompleteValues = true;
      this.parseValuesSize = 3;
      this.selectListCache = null;
      this.insertColumnsCache = null;
   }

   public boolean isKeepComments() {
      return this.lexer.isKeepComments();
   }

   public void setKeepComments(boolean keepComments) {
      this.lexer.setKeepComments(keepComments);
   }

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

   public SchemaRepository getRepository() {
      return this.repository;
   }

   public void setRepository(SchemaRepository repository) {
      this.repository = repository;
   }

   public List<SQLStatement> parseStatementList() {
      List<SQLStatement> statementList = new ArrayList();
      this.parseStatementList(statementList, -1, (SQLObject)null);
      return statementList;
   }

   public List<SQLStatement> parseStatementList(SQLObject parent) {
      List<SQLStatement> statementList = new ArrayList();
      this.parseStatementList(statementList, -1, parent);
      return statementList;
   }

   public void parseStatementList(List<SQLStatement> statementList) {
      this.parseStatementList(statementList, -1, (SQLObject)null);
   }

   public void parseStatementList(List<SQLStatement> statementList, int max) {
      this.parseStatementList(statementList, max, (SQLObject)null);
   }

   public void parseStatementList(List<SQLStatement> statementList, int max, SQLObject parent) {
      if ("select @@session.tx_read_only".equals(this.lexer.text) && this.lexer.token == Token.SELECT) {
         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);
         this.lexer.reset(29, '\u001a', Token.EOF);
      } else {
         boolean semi = false;

         for(int i = 0; max == -1 || statementList.size() < max; ++i) {
            while(this.lexer.token == Token.MULTI_LINE_COMMENT || this.lexer.token == Token.LINE_COMMENT) {
               this.lexer.nextToken();
            }

            if (this.lexer.token == Token.END && this.dbType.equals("gauss")) {
               SQLStatement stmt = this.parseCommonStatement();
               statementList.add(stmt);
               return;
            }

            if (this.lexer.token == Token.LOCK && this.dbType.equals("db2")) {
               SQLStatement stmt = this.parseDB2LockTable();
               statementList.add(stmt);
               return;
            }

            switch (this.lexer.token) {
               case EOF:
               case END:
               case UNTIL:
               case ELSE:
               case ELSEIF:
               case WHEN:
                  if (this.lexer.isKeepComments() && this.lexer.hasComment() && statementList.size() > 0) {
                     SQLStatement stmt = (SQLStatement)statementList.get(statementList.size() - 1);
                     stmt.addAfterComment(this.lexer.readAndResetComments());
                  }

                  return;
               case SEMI: {
                   int line0 = this.lexer.getLine();
                   this.lexer.nextToken();
                   int line1 = this.lexer.getLine();
                   if (statementList.size() > 0) {
                       SQLStatement lastStmt = (SQLStatement) statementList.get(statementList.size() - 1);
                       lastStmt.setAfterSemi(true);
                       if (this.lexer.isKeepComments()) {
                           SQLStatement stmt = (SQLStatement) statementList.get(statementList.size() - 1);
                           if (line1 - line0 <= 1) {
                               stmt.addAfterComment(this.lexer.readAndResetComments());
                           }
                       }
                   }

                   semi = true;
                   break;
               }
               case WITH: {
                  SQLStatement stmt = this.parseWith();
                  stmt.setParent(parent);
                  statementList.add(stmt);
                  break;
                  }
               case TABLE:{
                   MySqlHintStatement hintStatement = null;
                   if (i == 1 && statementList.size() > 0 && statementList.get(statementList.size() - i) instanceof MySqlHintStatement) {
                       hintStatement = (MySqlHintStatement) statementList.get(statementList.size() - i);
                   } else if (i > 0 && !semi) {
                       throw new ParserException("syntax error. " + this.lexer.info());
                   }

                   SQLStatement stmt = this.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;
                   break;
               }
               case SELECT: {
                   MySqlHintStatement hintStatement = null;
                   if (i == 1 && statementList.size() > 0 && statementList.get(statementList.size() - i) instanceof MySqlHintStatement) {
                       hintStatement = (MySqlHintStatement) statementList.get(statementList.size() - i);
                   } else if (i > 0 && !semi) {
                       throw new ParserException("syntax error. " + this.lexer.info());
                   }

                   SQLStatement stmt = this.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;
                   break;
               }
               case UPDATE: {
                   Lexer.SavePoint savePoint = this.lexer.mark();
                   this.lexer.nextToken();
                   if (this.dbType == DbType.mysql && this.lexer.identifierEquals("PLANCACHE")) {
                       this.lexer.nextToken();
                       if (this.lexer.token == Token.SELECT) {
                           MySqlUpdatePlanCacheStatement stmt = new MySqlUpdatePlanCacheStatement();
                           SQLSelect fromSelect = this.createSQLSelectParser().select();
                           this.accept(Token.TO);
                           SQLSelect toSelect = this.createSQLSelectParser().select();
                           stmt.setFormSelect(fromSelect);
                           stmt.setToSelect(toSelect);
                           statementList.add(stmt);
                           break;
                       }
                   }

                   this.lexer.reset(savePoint);
                   SQLStatement stmt = this.parseUpdateStatement();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case CREATE: {
                   SQLStatement stmt = this.parseCreate();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case INSERT: {
                   SQLStatement stmt = this.parseInsert();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case DELETE: {
                   SQLStatement stmt = this.parseDeleteStatement();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case EXPLAIN: {
                   this.lexer.computeRowAndColumn();
                   int sourceLine = this.lexer.posLine;
                   int sourceColumn = this.lexer.posColumn;
                   Lexer.SavePoint savePoint = this.lexer.mark();
                   this.lexer.nextToken();
                   if (this.lexer.identifierEquals("PLANCACHE")) {
                       this.lexer.nextToken();
                       MySqlExplainPlanCacheStatement stmt = new MySqlExplainPlanCacheStatement();
                       stmt.setSourceLine(sourceLine);
                       stmt.setSourceLine(sourceColumn);
                       statementList.add(stmt);
                   } else {
                       this.lexer.reset(savePoint);
                       SQLExplainStatement stmt = this.parseExplain();
                       stmt.setSourceLine(sourceLine);
                       stmt.setSourceLine(sourceColumn);
                       stmt.setParent(parent);
                       statementList.add(stmt);
                   }
                   break;
               }
               case SET: {
                   SQLStatement stmt = this.parseSet();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case ALTER: {
                  SQLStatement stmt = this.parseAlter();
                  stmt.setParent(parent);
                  statementList.add(stmt);
                  break;
                  }
               case TRUNCATE: {
                   SQLStatement stmt = this.parseTruncate();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case USE: {
                   SQLStatement stmt = this.parseUse();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case GRANT: {
                   SQLStatement stmt = this.parseGrant();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case REVOKE: {
                   SQLStatement stmt = this.parseRevoke();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case SHOW: {
                   SQLStatement stmt = this.parseShow();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case MERGE: {
                   SQLStatement stmt = this.parseMerge();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case REPEAT: {
                   SQLStatement stmt = this.parseRepeat();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case DECLARE: {
                   SQLStatement stmt = this.parseDeclare();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case WHILE: {
                   SQLStatement stmt = this.parseWhile();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case IF: {
                   SQLStatement stmt = this.parseIf();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case CASE: {
                   SQLStatement stmt = this.parseCase();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case OPEN: {
                   SQLStatement stmt = this.parseOpen();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case FETCH: {
                   SQLStatement stmt = this.parseFetch();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case DROP: {
                   SQLStatement stmt = this.parseDrop();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case COMMENT: {
                   SQLStatement stmt = this.parseComment();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case KILL: {
                   SQLStatement stmt = this.parseKill();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case CLOSE: {
                   SQLStatement stmt = this.parseClose();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case RETURN: {
                   SQLStatement stmt = this.parseReturn();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case UPSERT: {
                   SQLStatement stmt = this.parseUpsert();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case LEAVE: {
                   SQLStatement stmt = this.parseLeave();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case REPAIR: {
                   SQLStatement stmt = this.parseRepair();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               case CACHE: {
                   SQLStatement stmt = this.parseCacheIndex();
                   stmt.setParent(parent);
                   statementList.add(stmt);
                   break;
               }
               default: {
                   if (this.lexer.token != Token.LBRACE && !this.lexer.identifierEquals("CALL")) {
                       if (this.lexer.identifierEquals("UPSERT")) {
                           SQLStatement stmt = this.parseUpsert();
                           statementList.add(stmt);
                       } else {
                           if (this.lexer.identifierEquals("LIST")) {
                               Lexer.SavePoint mark = this.lexer.mark();
                               SQLStatement stmt = this.parseList();
                               if (stmt != null) {
                                   statementList.add(stmt);
                                   continue;
                               }

                               this.lexer.reset(mark);
                           }

                           if (this.lexer.identifierEquals("RENAME")) {
                               SQLStatement stmt = this.parseRename();
                               statementList.add(stmt);
                           } else if (this.lexer.identifierEquals("RELEASE")) {
                               SQLStatement stmt = this.parseReleaseSavePoint();
                               statementList.add(stmt);
                           } else if (this.lexer.identifierEquals("SAVEPOINT")) {
                               SQLStatement stmt = this.parseSavePoint();
                               statementList.add(stmt);
                           } else if (this.lexer.identifierEquals("REFRESH")) {
                               SQLStatement stmt = this.parseRefresh();
                               statementList.add(stmt);
                           } else if (this.lexer.identifierEquals(FnvHash.Constants.COPY)) {
                               SQLStatement stmt = this.parseCopy();
                               statementList.add(stmt);
                           } else if (this.lexer.token != Token.DESC && !this.lexer.identifierEquals(FnvHash.Constants.DESCRIBE)) {
                               if (this.lexer.identifierEquals("ROLLBACK") && !this.dbType.equals("gauss")) {
                                   SQLStatement stmt = this.parseRollback();
                                   statementList.add(stmt);
                                   if (parent instanceof SQLBlockStatement && DbType.mysql == this.dbType) {
                                       return;
                                   }
                               } else if (this.lexer.identifierEquals("DUMP")) {
                                   SQLStatement stmt = this.parseDump();
                                   statementList.add(stmt);
                               } else if (this.lexer.identifierEquals(FnvHash.Constants.COMMIT) && !this.dbType.equals("gauss")) {
                                   SQLStatement stmt = this.parseCommit();
                                   statementList.add(stmt);
                                   if (parent instanceof SQLBlockStatement && DbType.mysql == this.dbType) {
                                       return;
                                   }
                               } else if (this.lexer.identifierEquals(FnvHash.Constants.RETURN)) {
                                   SQLStatement stmt = this.parseReturn();
                                   statementList.add(stmt);
                               } else if (this.lexer.identifierEquals(FnvHash.Constants.PURGE)) {
                                   SQLStatement stmt = this.parsePurge();
                                   statementList.add(stmt);
                               } else if (this.lexer.identifierEquals(FnvHash.Constants.FLASHBACK)) {
                                   SQLStatement stmt = this.parseFlashback();
                                   statementList.add(stmt);
                               } else if (this.lexer.identifierEquals(FnvHash.Constants.WHO)) {
                                   SQLStatement stmt = this.parseWhoami();
                                   statementList.add(stmt);
                               } else if (this.lexer.token == Token.FOR) {
                                   SQLStatement stmt = this.parseFor();
                                   statementList.add(stmt);
                                   stmt.setParent(parent);
                               } else if (this.lexer.token == Token.LPAREN) {
                                   char markChar = this.lexer.current();
                                   int markBp = this.lexer.bp();

                                   do {
                                       this.lexer.nextToken();
                                   } while (this.lexer.token == Token.LPAREN);

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

                                   this.lexer.reset(markBp, markChar, Token.LPAREN);
                                   SQLStatement stmt = this.parseSelect();
                                   statementList.add(stmt);
                               } else if (this.lexer.token == Token.VALUES) {
                                   SQLValuesTableSource values = this.createSQLSelectParser().parseValues();
                                   SQLSelectStatement stmt = new SQLSelectStatement();
                                   stmt.setSelect(new SQLSelect(values));
                                   statementList.add(stmt);
                                   stmt.setParent(parent);
                               } else {
                                   int size = statementList.size();
                                   if (this.parseStatementListDialect(statementList)) {
                                       if (parent != null) {
                                           for (int j = size; j < statementList.size(); ++j) {
                                               SQLStatement dialectStmt = (SQLStatement) statementList.get(j);
                                               dialectStmt.setParent(parent);
                                           }
                                       }
                                   } else {
                                       if (!this.lexer.identifierEquals("SHUTDOWN") && !this.lexer.identifierEquals("SAVEPOINT") && !this.lexer.identifierEquals("ROLLBACK") && this.lexer.token != Token.COMMIT && this.lexer.token != Token.RESET && !this.lexer.identifierEquals("END") && !this.lexer.identifierEquals("CHECKPOINT") || !this.dbType.equals("gauss") && !this.dbType.equals("db2")) {
                                           if (this.lexer.throwIfStateEnd) {
                                               this.printError(this.lexer.token);
                                           }

                                           return;
                                       }

                                       SQLStatement stmt = this.parseCommonStatement();
                                       statementList.add(stmt);
                                   }
                               }
                           } else {
                               SQLStatement stmt = this.parseDescribe();
                               statementList.add(stmt);
                           }
                       }
                   } else {
                       SQLCallStatement stmt = this.parseCall();
                       statementList.add(stmt);
                   }
               }
            }
         }

      }
   }

   private SQLStatement parseDB2LockTable() {
      DB2LockTableStatement stmt = new DB2LockTableStatement();
      this.accept(Token.LOCK);
      this.accept(Token.TABLE);
      stmt.setTableName(this.exprParser.name());
      this.accept(Token.IN);
      if (this.lexer.identifierEquals("ACCESS")) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.SHARE) {
            stmt.setAccessShare(true);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("EXCLUSIVE")) {
            stmt.setAccessExclusive(true);
            this.lexer.nextToken();
         }
      } else if (this.lexer.token() == Token.ROW) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.SHARE) {
            stmt.setRowShare(true);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("EXCLUSIVE")) {
            stmt.setRowExclusive(true);
            this.lexer.nextToken();
         }
      } else if (this.lexer.token() == Token.SHARE) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.ROW) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("EXCLUSIVE")) {
               stmt.setShareRowExclusive(true);
               this.lexer.nextToken();
            }
         } else {
            stmt.setShare(true);
         }
      } else if (this.lexer.identifierEquals("EXCLUSIVE")) {
         stmt.setExclusive(true);
         this.lexer.nextToken();
      }

      this.acceptIdentifier("MODE");
      if (this.lexer.token() == Token.NOWAIT) {
         stmt.setNowait(true);
         this.lexer.nextToken();
      }

      return stmt;
   }

   public SQLStatement parseCommonStatement() {
      GaussCommonStatement stmt = new GaussCommonStatement();
      if (this.lexer.identifierEquals("SHUTDOWN")) {
         stmt.setShutdown(true);
         this.lexer.nextToken();
         if (this.lexer.token() != Token.EOF) {
            stmt.setShutdownParam(new SQLIdentifierExpr(this.lexer.stringVal()));
            this.lexer.nextToken();
         }

         return stmt;
      } else if (this.lexer.identifierEquals("SAVEPOINT")) {
         stmt.setSavepoint(true);
         this.lexer.nextToken();
         if (this.lexer.token() != Token.EOF) {
            stmt.setSavepointName(this.exprParser.name());
            this.lexer.nextToken();
         }

         return stmt;
      } else {
         if (this.lexer.identifierEquals("ROLLBACK")) {
            stmt.setRollback(true);
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("PREPARED")) {
               stmt.setPrepared(true);
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("WORK")) {
               stmt.setWork(true);
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("TRANSACTION")) {
               stmt.setTransaction(true);
               this.lexer.nextToken();
            }

            if (this.lexer.token() != Token.EOF) {
               stmt.setTransactionId(this.exprParser.name());
               this.lexer.nextToken();
            }
         }

         if (this.lexer.identifierEquals("CHECKPOINT")) {
            stmt.setCheckpoint(true);
            this.lexer.nextToken();
         }

         if (this.lexer.token == Token.RESET) {
            stmt.setReset(true);
            this.lexer.nextToken();
            if (this.lexer.token == Token.ROLE) {
               stmt.setRole(true);
               this.lexer.nextToken();
            }
         }

         if (this.lexer.token == Token.COMMIT) {
            stmt.setCommit(true);
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("PREPARED")) {
               stmt.setPrepared(true);
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("WORK")) {
               stmt.setWork(true);
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("TRANSACTION")) {
               stmt.setTransaction(true);
               this.lexer.nextToken();
            }

            if (this.lexer.token == Token.IDENTIFIER) {
               stmt.setTransactionId(this.exprParser.name());
               if (this.lexer.token == Token.WITH) {
                  stmt.setWith(true);
                  this.lexer.nextToken();
                  stmt.setCsn(this.exprParser.name());
               }
            }
         }

         if (this.lexer.token == Token.END) {
            stmt.setEnd(true);
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("WORK")) {
               stmt.setWork(true);
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("TRANSACTION")) {
               stmt.setTransaction(true);
               this.lexer.nextToken();
            }
         }

         return stmt;
      }
   }

   public SQLStatement parseCopy() {
      throw new ParserException("TODO : " + this.lexer.info());
   }

   public SQLStatement parseFor() {
      this.accept(Token.FOR);
      SQLForStatement stmt = new SQLForStatement();
      stmt.setDbType(this.dbType);
      stmt.setIndex(this.exprParser.name());
      this.accept(Token.IN);
      stmt.setRange(this.exprParser.expr());
      this.accept(Token.LOOP);
      this.parseStatementList(stmt.getStatements(), -1, stmt);
      this.accept(Token.END);
      this.accept(Token.LOOP);
      this.accept(Token.SEMI);
      stmt.setAfterSemi(true);
      return stmt;
   }

   public SQLStatement parseFlashback() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseDump() {
      SQLDumpStatement stmt = new SQLDumpStatement();
      this.acceptIdentifier("DUMP");
      this.acceptIdentifier("DATA");
      if (this.lexer.identifierEquals(FnvHash.Constants.OVERWRITE)) {
         this.lexer.nextToken();
         stmt.setOverwrite(true);
      }

      if (this.lexer.token == Token.INTO) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.LITERAL_CHARS) {
            stmt.setInto((SQLExpr)(new SQLCharExpr(this.lexer.stringVal)));
            this.lexer.nextToken();
         } else {
            stmt.setInto(this.exprParser.expr());
         }
      }

      SQLSelect select = this.createSQLSelectParser().select();
      stmt.setSelect(select);
      return stmt;
   }

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

      Lexer.SavePoint mark = this.lexer.mark();
      this.lexer.nextToken();
      List<SQLCommentHint> hints = null;
      if (this.lexer.token == Token.HINT) {
         hints = this.exprParser.parseHints();
      }

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

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

      Lexer.SavePoint mark2 = null;
      if (this.lexer.identifierEquals("CONTEXT") && this.dbType == DbType.dm) {
         mark2 = this.lexer.mark();
         this.lexer.nextToken();
      }

      SQLStatement stmt;
      label148:
      switch (this.lexer.token) {
         case TABLE:
            SQLDropTableStatement dropTable = this.parseDropTable(false);
            if (temporary) {
               dropTable.setTemporary(true);
            }

            if (hints != null) {
               dropTable.setHints(hints);
            }

            stmt = dropTable;
            break;
         case SELECT:
         case UPDATE:
         case CREATE:
         case INSERT:
         case DELETE:
         case EXPLAIN:
         case SET:
         case ALTER:
         case TRUNCATE:
         case USE:
         case GRANT:
         case REVOKE:
         case SHOW:
         case MERGE:
         case REPEAT:
         case DECLARE:
         case WHILE:
         case IF:
         case CASE:
         case OPEN:
         case FETCH:
         case DROP:
         case COMMENT:
         case KILL:
         case CLOSE:
         case RETURN:
         case UPSERT:
         case LEAVE:
         case REPAIR:
         case CACHE:
         default:
            if (this.lexer.token != Token.TABLE && !this.lexer.identifierEquals("TEMPORARY") && !this.lexer.identifierEquals(FnvHash.Constants.PARTITIONED)) {
               if (this.lexer.identifierEquals(FnvHash.Constants.TABLES)) {
                  stmt = this.parseDropTable(false);
               } else if (this.lexer.identifierEquals(FnvHash.Constants.EVENT)) {
                  stmt = this.parseDropEvent();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.RESOURCE)) {
                  stmt = this.parseDropResource();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.LOGFILE)) {
                  stmt = this.parseDropLogFileGroup();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.SERVER)) {
                  stmt = this.parseDropServer();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.TABLEGROUP)) {
                  stmt = this.parseDropTableGroup();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.ROLE)) {
                  this.lexer.reset(mark);
                  stmt = this.parseDropRole();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.OUTLINE)) {
                  this.lexer.reset(mark);
                  stmt = this.parseDropOutline();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
                  this.lexer.nextToken();
                  if (this.lexer.token == Token.TABLE) {
                     this.lexer.reset(mark);
                     stmt = this.parseDropTable(true);
                  } else {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.CATALOG)) {
                        throw new ParserException("TODO " + this.lexer.info());
                     }

                     this.lexer.reset(mark);
                     stmt = this.parseDropCatalog();
                  }
               } else if (this.lexer.token() == Token.FULLTEXT) {
                  this.lexer.nextToken();
                  FullTextType type = this.parseFullTextType();
                  SQLName name = this.exprParser.name();
                  MysqlDropFullTextStatement x = new MysqlDropFullTextStatement();
                  x.setName(name);
                  x.setType(type);
                  stmt = x;
               } else if (this.lexer.identifierEquals("INSTANCE_GROUP")) {
                  this.lexer.nextToken();
                  MySqlManageInstanceGroupStatement x = new MySqlManageInstanceGroupStatement();
                  x.setOperation(new SQLIdentifierExpr("DROP"));

                  while(true) {
                     x.getGroupNames().add(this.exprParser.expr());
                     if (this.lexer.token() != Token.COMMA) {
                        stmt = x;
                        break label148;
                     }

                     this.lexer.nextToken();
                  }
               } else if (this.lexer.identifierEquals("MATERIALIZED")) {
                  stmt = this.parseDropMaterializedView();
               } else if (this.lexer.identifierEquals("DOMAIN")) {
                  this.lexer.reset(mark);
                  stmt = this.parseDropDomian();
               } else if (this.lexer.identifierEquals("DIRECTORY")) {
                  this.lexer.reset(mark);
                  stmt = this.parseDropDirectory();
               } else if (this.lexer.identifierEquals("PACKAGE")) {
                  this.lexer.reset(mark);
                  stmt = this.dropPackage();
               } else {
                  if (!this.lexer.identifierEquals(FnvHash.Constants.SYNONYM) && !this.lexer.identifierEquals(FnvHash.Constants.PUBLIC)) {
                     if (mark2 != null) {
                        this.lexer.reset(mark);
                        stmt = this.parseDropContext();
                        return stmt;
                     }

                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  this.lexer.reset(mark);
                  stmt = this.parseDropSynonym();
               }
            } else {
               SQLDropTableStatement dropTable2 = this.parseDropTable(false);
               if (hints != null) {
                  dropTable2.setHints(hints);
               }

               stmt = dropTable2;
            }
            break;
         case USER:
            stmt = this.parseDropUser();
            break;
         case INDEX:
            if (mark2 != null) {
               this.lexer.reset(mark);
            }

            stmt = this.parseDropIndex();
            break;
         case VIEW:
            stmt = this.parseDropView(false);
            break;
         case TRIGGER:
            stmt = this.parseDropTrigger(false);
            break;
         case DATABASE:
            stmt = this.parseDropDatabaseOrSchema(false);
            if (physical) {
               ((SQLDropDatabaseStatement)stmt).setPhysical(physical);
            }
            break;
         case SCHEMA:
            if (this.dbType == DbType.postgresql) {
               stmt = this.parseDropSchema();
            } else {
               stmt = this.parseDropDatabaseOrSchema(false);
               if (physical) {
                  ((SQLDropDatabaseStatement)stmt).setPhysical(physical);
               }
            }
            break;
         case FUNCTION:
            SQLDropFunctionStatement dropFunc = this.parseDropFunction(false);
            if (temporary) {
               dropFunc.setTemporary(true);
            }

            stmt = dropFunc;
            break;
         case TABLESPACE:
            stmt = this.parseDropTablespace(false);
            break;
         case PROCEDURE:
            stmt = this.parseDropProcedure(false);
            break;
         case SEQUENCE:
            stmt = this.parseDropSequence(false);
            break;
         case SESSION:
            stmt = this.parseDropSession();
            break;
         case GROUP:
            stmt = this.parseDropGroup();
      }

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

      return stmt;
   }

   private GaussCommonStatement parseDropGroup() {
      GaussCommonStatement stmt = new GaussCommonStatement();
      this.accept(Token.GROUP);
      stmt.setDropGroup(true);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      SQLListExpr listExpr = new SQLListExpr();
      this.exprParser.exprList(listExpr.getItems(), listExpr);
      stmt.setGroupNameList(listExpr);
      return stmt;
   }

   private SQLStatement dropPackage() {
      this.accept(Token.DROP);
      SQLDropPackageStatement stmt = new SQLDropPackageStatement(this.getDbType());
      this.acceptIdentifier("PACKAGE");
      if (this.lexer.identifierEquals("BODY")) {
         this.lexer.nextToken();
         stmt.setBody(true);
      }

      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected FullTextType parseFullTextType() {
      FullTextType textType;
      if (this.lexer.identifierEquals(FnvHash.Constants.CHARFILTER)) {
         textType = FullTextType.CHARFILTER;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.TOKENIZER)) {
         textType = FullTextType.TOKENIZER;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.TOKENFILTER)) {
         textType = FullTextType.TOKENFILTER;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.ANALYZER)) {
         textType = FullTextType.ANALYZER;
      } else {
         if (!this.lexer.identifierEquals(FnvHash.Constants.DICTIONARY)) {
            throw new ParserException("type of full text must be [CHARFILTER/TOKENIZER/TOKENFILTER/ANALYZER/DICTIONARY] .");
         }

         textType = FullTextType.DICTIONARY;
      }

      this.lexer.nextToken();
      return textType;
   }

   protected SQLStatement parseWhoami() {
      this.lexer.nextToken();
      this.acceptIdentifier("AM");
      this.acceptIdentifier("I");
      return new SQLWhoamiStatement();
   }

   protected SQLStatement parseDropOutline() {
      this.accept(Token.DROP);
      SQLDropOutlineStatement stmt = new SQLDropOutlineStatement();
      stmt.setDbType(this.dbType);
      this.acceptIdentifier("OUTLINE");
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      return stmt;
   }

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

      SQLRefreshMaterializedViewStatement stmt = new SQLRefreshMaterializedViewStatement();
      stmt.setDbType(this.dbType);
      this.acceptIdentifier("MATERIALIZED");
      this.accept(Token.VIEW);
      stmt.setName(this.exprParser.name());
      return stmt;
   }

   public SQLStatement parseShowMaterializedView() {
      if (this.lexer.token() == Token.SHOW) {
         this.lexer.nextToken();
      }

      SQLShowMaterializedViewStatement stmt = new SQLShowMaterializedViewStatement();
      stmt.setDbType(this.dbType);
      this.acceptIdentifier("MATERIALIZED");
      this.acceptIdentifier("VIEWS");
      if (this.lexer.token() == Token.LIKE) {
         this.lexer.nextToken();
         stmt.setLike(this.exprParser.charExpr());
      }

      return stmt;
   }

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

      SQLDropMaterializedViewStatement stmt = new SQLDropMaterializedViewStatement();
      stmt.setDbType(this.dbType);
      this.acceptIdentifier("MATERIALIZED");
      this.accept(Token.VIEW);
      if (this.lexer.token() == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      SQLListExpr listExpr = new SQLListExpr();
      this.exprParser.exprList(listExpr.getItems(), listExpr);
      stmt.setViewNameList(listExpr);
      if (this.lexer.token() == Token.CASCADE) {
         stmt.setCascade(true);
         this.lexer.nextToken();
      } else if (this.lexer.token() == Token.RESTRICT) {
         stmt.setRestrict(true);
         this.lexer.nextToken();
      }

      return stmt;
   }

   protected SQLStatement parseDropCatalog() {
      this.accept(Token.DROP);
      SQLDropCatalogStatement stmt = new SQLDropCatalogStatement(this.dbType);
      if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
         stmt.setExternal(true);
         this.lexer.nextToken();
      }

      this.acceptIdentifier("CATALOG");
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      return stmt;
   }

   protected SQLStatement parseDropRole() {
      this.accept(Token.DROP);
      this.acceptIdentifier("ROLE");
      SQLDropRoleStatement stmt = new SQLDropRoleStatement();
      stmt.setDbType(this.dbType);
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      return stmt;
   }

   protected SQLStatement parseDropTableGroup() {
      if (this.lexer.token == Token.DROP) {
         this.lexer.nextToken();
      }

      this.acceptIdentifier("TABLEGROUP");
      SQLDropTableGroupStatement stmt = new SQLDropTableGroupStatement();
      stmt.setDbType(this.dbType);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLStatement parseDropServer() {
      if (this.lexer.token == Token.DROP) {
         this.lexer.nextToken();
      }

      this.acceptIdentifier("SERVER");
      SQLDropServerStatement stmt = new SQLDropServerStatement();
      stmt.setDbType(this.dbType);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLStatement parseDropLogFileGroup() {
      if (this.lexer.token == Token.DROP) {
         this.lexer.nextToken();
      }

      this.acceptIdentifier("LOGFILE");
      this.accept(Token.GROUP);
      SQLDropLogFileGroupStatement stmt = new SQLDropLogFileGroupStatement();
      stmt.setDbType(this.dbType);
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      if (this.lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.EQ) {
            this.lexer.nextToken();
         }

         SQLExpr engine = this.exprParser.primary();
         stmt.setEngine(engine);
      }

      return stmt;
   }

   protected SQLStatement parseDropEvent() {
      if (this.lexer.token == Token.DROP) {
         this.lexer.nextToken();
      }

      this.acceptIdentifier("EVENT");
      SQLDropEventStatement stmt = new SQLDropEventStatement();
      stmt.setDbType(this.dbType);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLStatement parseDropResource() {
      if (this.lexer.token == Token.DROP) {
         this.lexer.nextToken();
      }

      this.acceptIdentifier("RESOURCE");
      if (this.lexer.token == Token.GROUP) {
         this.lexer.nextToken();
         SQLDropResourceGroupStatement stmt = new SQLDropResourceGroupStatement();
         stmt.setDbType(this.dbType);
         if (this.lexer.token == Token.IF) {
            this.lexer.nextToken();
            this.accept(Token.EXISTS);
            stmt.setIfExists(true);
         }

         SQLName name = this.exprParser.name();
         stmt.setName(name);
         return stmt;
      } else {
         SQLDropResourceStatement stmt = new SQLDropResourceStatement();
         stmt.setDbType(this.dbType);
         if (this.lexer.token == Token.IF) {
            this.lexer.nextToken();
            this.accept(Token.EXISTS);
            stmt.setIfExists(true);
         }

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

   protected SQLStatement parseAlterFunction() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   public SQLStatement parseKill() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseCase() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseIf() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseWhile() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseDeclare() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseRepeat() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseLeave() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseRepair() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parseCacheIndex() {
      throw new ParserException("not supported. " + this.lexer.info());
   }

   public SQLStatement parsePurge() {
      this.acceptIdentifier("PURGE");
      if (this.lexer.token == Token.TABLE) {
         this.lexer.nextToken();
         SQLName tableName = this.exprParser.name();
         SQLPurgeTableStatement stmt = new SQLPurgeTableStatement();
         stmt.setTable(tableName);
         return stmt;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.RECYCLEBIN)) {
         this.lexer.nextToken();
         SQLPurgeRecyclebinStatement stmt = new SQLPurgeRecyclebinStatement();
         return stmt;
      } else {
         SQLPurgeLogsStatement stmt = new SQLPurgeLogsStatement();
         if (this.lexer.token == Token.BINARY) {
            this.lexer.nextToken();
            stmt.setBinary(true);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.MASTER)) {
            this.lexer.nextToken();
            stmt.setMaster(true);
         }

         if (this.lexer.token == Token.ALL) {
            this.lexer.nextToken();
            stmt.setAll(true);
            return stmt;
         } else {
            this.acceptIdentifier("LOGS");
            if (this.lexer.token == Token.TO) {
               this.lexer.nextToken();
               SQLExpr to = this.exprParser.expr();
               stmt.setTo(to);
            }

            if (this.lexer.identifierEquals(FnvHash.Constants.BEFORE)) {
               this.lexer.nextToken();
               SQLExpr before = this.exprParser.expr();
               stmt.setBefore(before);
            }

            return stmt;
         }
      }
   }

   public SQLStatement parseReturn() {
      if (this.lexer.token == Token.RETURN || this.lexer.identifierEquals("RETURN")) {
         this.lexer.nextToken();
      }

      SQLReturnStatement stmt = new SQLReturnStatement();
      if (this.lexer.token != Token.SEMI) {
         SQLExpr expr = this.exprParser.expr();
         stmt.setExpr(expr);
      }

      if (this.lexer.token == Token.SEMI) {
         this.accept(Token.SEMI);
         stmt.setAfterSemi(true);
      }

      return stmt;
   }

   public SQLStatement parseUpsert() {
      SQLInsertStatement insertStatement = new SQLInsertStatement();
      if (this.lexer.token == Token.UPSERT || this.lexer.identifierEquals("UPSERT")) {
         this.lexer.nextToken();
         insertStatement.setUpsert(true);
      }

      this.parseInsert0(insertStatement);
      return insertStatement;
   }

   public SQLStatement parseRollback() {
      this.lexer.nextToken();
      SQLRollbackStatement stmt = new SQLRollbackStatement(this.getDbType());
      if (this.lexer.identifierEquals("WORK")) {
         this.lexer.nextToken();
         stmt.setWork(true);
      }

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

         stmt.setTo(this.exprParser.name());
      }

      return stmt;
   }

   public SQLStatement parseCommit() {
      this.acceptIdentifier("COMMIT");
      return new SQLCommitStatement();
   }

   public SQLStatement parseShow() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   public SQLUseStatement parseUse() {
      this.accept(Token.USE);
      SQLUseStatement stmt = new SQLUseStatement(this.getDbType());
      stmt.setDatabase(this.exprParser.name());
      return stmt;
   }

   protected SQLExpr parseUser() {
      SQLExpr user = this.exprParser.expr();
      return user;
   }

   public SQLGrantStatement parseGrant() {
      this.accept(Token.GRANT);
      SQLGrantStatement stmt = new SQLGrantStatement(this.getDbType());
      this.parsePrivileages(stmt.getPrivileges(), stmt);
      if (this.lexer.token == Token.ON) {
         this.lexer.nextToken();
         switch (this.lexer.token) {
            case TABLE:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.TABLE);
               break;
            case USER:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.USER);
               break;
            case DATABASE:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.DATABASE);
               break;
            case FUNCTION:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.FUNCTION);
               break;
            case PROCEDURE:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.PROCEDURE);
               break;
            case IDENTIFIER:
               if (this.lexer.identifierEquals("SYSTEM")) {
                  this.lexer.nextToken();
                  stmt.setResourceType(SQLObjectType.SYSTEM);
               } else if (this.lexer.identifierEquals("DIRECTORY")) {
                  this.lexer.nextToken();
                  stmt.setResourceType(SQLObjectType.DIRECTORY);
               }
         }

         if (stmt.getResourceType() != null && this.lexer.token == Token.COLONCOLON) {
            this.lexer.nextToken();
         }

         SQLExpr expr;
         if (this.lexer.token == Token.DOT) {
            expr = new SQLAllColumnExpr();
            this.lexer.nextToken();
         } else {
            expr = this.exprParser.expr();
         }

         if (stmt.getResourceType() != SQLObjectType.TABLE && stmt.getResourceType() != null) {
            stmt.setResource(expr);
         } else {
            stmt.setResource(new SQLExprTableSource(expr));
         }
      }

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

         while(true) {
            SQLExpr user = this.parseUser();
            stmt.getUsers().add(user);
            if (this.lexer.token() != Token.COMMA) {
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.token == Token.WITH) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.GRANT) {
            this.lexer.nextToken();
            this.acceptIdentifier("OPTION");
            stmt.setWithGrantOption(true);
         }

         label76:
         while(true) {
            while(!this.lexer.identifierEquals("MAX_QUERIES_PER_HOUR")) {
               if (this.lexer.identifierEquals("MAX_UPDATES_PER_HOUR")) {
                  this.lexer.nextToken();
                  stmt.setMaxUpdatesPerHour(this.exprParser.primary());
               } else if (this.lexer.identifierEquals("MAX_CONNECTIONS_PER_HOUR")) {
                  this.lexer.nextToken();
                  stmt.setMaxConnectionsPerHour(this.exprParser.primary());
               } else {
                  if (!this.lexer.identifierEquals("MAX_USER_CONNECTIONS")) {
                     break label76;
                  }

                  this.lexer.nextToken();
                  stmt.setMaxUserConnections(this.exprParser.primary());
               }
            }

            this.lexer.nextToken();
            stmt.setMaxQueriesPerHour(this.exprParser.primary());
         }
      }

      if (this.lexer.identifierEquals("ADMIN")) {
         this.lexer.nextToken();
         this.acceptIdentifier("OPTION");
         stmt.setAdminOption(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.IDENTIFIED)) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         if (this.lexer.identifierEquals("PASSWORD")) {
            this.lexer.nextToken();
            String password = this.lexer.stringVal();
            this.accept(Token.LITERAL_CHARS);
            stmt.setIdentifiedByPassword(password);
         } else {
            stmt.setIdentifiedBy(this.exprParser.expr());
         }
      }

      if (this.lexer.token == Token.WITH) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.GRANT) {
            this.lexer.nextToken();
            this.acceptIdentifier("OPTION");
            stmt.setWithGrantOption(true);
         }
      }

      return stmt;
   }

   protected void parsePrivileages(List<SQLPrivilegeItem> privileges, SQLObject parent) {
      while(true) {
         String privilege = null;
         if (this.lexer.token == Token.ALL) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("PRIVILEGES")) {
               privilege = "ALL PRIVILEGES";
               this.lexer.nextToken();
            } else {
               privilege = "ALL";
            }
         } else if (this.lexer.token == Token.SELECT) {
            privilege = "SELECT";
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.UPDATE) {
            privilege = "UPDATE";
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.DELETE) {
            privilege = "DELETE";
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.INSERT) {
            privilege = "INSERT";
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.INDEX) {
            this.lexer.nextToken();
            privilege = "INDEX";
         } else if (this.lexer.token == Token.TRIGGER) {
            this.lexer.nextToken();
            privilege = "TRIGGER";
         } else if (this.lexer.token == Token.REFERENCES) {
            privilege = "REFERENCES";
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.DESC) {
            privilege = "DESCRIBE";
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.CREATE) {
            this.lexer.nextToken();
            if (this.lexer.token == Token.TABLE) {
               privilege = "CREATE TABLE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.SESSION) {
               privilege = "CREATE SESSION";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.TABLESPACE) {
               privilege = "CREATE TABLESPACE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.USER) {
               privilege = "CREATE USER";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.VIEW) {
               privilege = "CREATE VIEW";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.PROCEDURE) {
               privilege = "CREATE PROCEDURE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.SEQUENCE) {
               privilege = "CREATE SEQUENCE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.ANY) {
               this.lexer.nextToken();
               if (this.lexer.token == Token.TABLE) {
                  this.lexer.nextToken();
                  privilege = "CREATE ANY TABLE";
               } else {
                  if (!this.lexer.identifierEquals("MATERIALIZED")) {
                     throw new ParserException("TODO : " + this.lexer.token + " " + this.lexer.stringVal());
                  }

                  this.lexer.nextToken();
                  this.accept(Token.VIEW);
                  privilege = "CREATE ANY MATERIALIZED VIEW";
               }
            } else if (this.lexer.identifierEquals("SYNONYM")) {
               privilege = "CREATE SYNONYM";
               this.lexer.nextToken();
            } else if (this.lexer.identifierEquals("ROUTINE")) {
               privilege = "CREATE ROUTINE";
               this.lexer.nextToken();
            } else if (this.lexer.identifierEquals("TEMPORARY")) {
               this.lexer.nextToken();
               this.acceptIdentifier("TABLES");
               privilege = "CREATE TEMPORARY TABLES";
            } else if (this.lexer.token == Token.ON) {
               privilege = "CREATE";
            } else {
               if (this.lexer.token != Token.COMMA) {
                  throw new ParserException("TODO : " + this.lexer.token + " " + this.lexer.stringVal());
               }

               privilege = "CREATE";
            }
         } else if (this.lexer.token == Token.ALTER) {
            this.lexer.nextToken();
            if (this.lexer.token == Token.TABLE) {
               privilege = "ALTER TABLE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.SESSION) {
               privilege = "ALTER SESSION";
               this.lexer.nextToken();
            } else if (this.lexer.identifierEquals(FnvHash.Constants.ROUTINE)) {
               privilege = "ALTER ROUTINE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.ANY) {
               this.lexer.nextToken();
               if (this.lexer.token == Token.TABLE) {
                  this.lexer.nextToken();
                  privilege = "ALTER ANY TABLE";
               } else {
                  if (!this.lexer.identifierEquals("MATERIALIZED")) {
                     throw new ParserException("TODO : " + this.lexer.token + " " + this.lexer.stringVal());
                  }

                  this.lexer.nextToken();
                  this.accept(Token.VIEW);
                  privilege = "ALTER ANY MATERIALIZED VIEW";
               }
            } else {
               if (this.lexer.token != Token.ON && this.lexer.token != Token.COMMA) {
                  throw new ParserException("TODO : " + this.lexer.token + " " + this.lexer.stringVal());
               }

               privilege = "ALTER";
            }
         } else if (this.lexer.token == Token.DROP) {
            this.lexer.nextToken();
            if (this.lexer.token == Token.DROP) {
               privilege = "DROP TABLE";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.SESSION) {
               privilege = "DROP SESSION";
               this.lexer.nextToken();
            } else if (this.lexer.token == Token.ANY) {
               this.lexer.nextToken();
               if (this.lexer.token == Token.TABLE) {
                  this.lexer.nextToken();
                  privilege = "DROP ANY TABLE";
               } else {
                  if (!this.lexer.identifierEquals("MATERIALIZED")) {
                     throw new ParserException("TODO : " + this.lexer.token + " " + this.lexer.stringVal());
                  }

                  this.lexer.nextToken();
                  this.accept(Token.VIEW);
                  privilege = "DROP ANY MATERIALIZED VIEW";
               }
            } else {
               privilege = "DROP";
            }
         } else if (this.lexer.identifierEquals("USAGE")) {
            privilege = "USAGE";
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("EXECUTE")) {
            privilege = "EXECUTE";
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("PROXY")) {
            privilege = "PROXY";
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("QUERY")) {
            this.lexer.nextToken();
            this.acceptIdentifier("REWRITE");
            privilege = "QUERY REWRITE";
         } else if (this.lexer.identifierEquals("GLOBAL")) {
            this.lexer.nextToken();
            this.acceptIdentifier("QUERY");
            this.acceptIdentifier("REWRITE");
            privilege = "GLOBAL QUERY REWRITE";
         } else if (this.lexer.identifierEquals("INHERIT")) {
            this.lexer.nextToken();
            this.acceptIdentifier("PRIVILEGES");
            privilege = "INHERIT PRIVILEGES";
         } else if (this.lexer.identifierEquals("EVENT")) {
            this.lexer.nextToken();
            privilege = "EVENT";
         } else if (this.lexer.identifierEquals("FILE")) {
            this.lexer.nextToken();
            privilege = "FILE";
         } else if (this.lexer.identifierEquals("DESCRIBE")) {
            this.lexer.nextToken();
            privilege = "DESCRIBE";
         } else if (this.lexer.token == Token.GRANT) {
            this.lexer.nextToken();
            this.acceptIdentifier("OPTION");
            if (this.lexer.token == Token.FOR) {
               privilege = "GRANT OPTION FOR";
               this.lexer.nextToken();
            } else {
               privilege = "GRANT OPTION";
            }
         } else if (this.lexer.token == Token.LOCK) {
            this.lexer.nextToken();
            this.acceptIdentifier("TABLES");
            privilege = "LOCK TABLES";
         } else if (this.lexer.identifierEquals("PROCESS")) {
            this.lexer.nextToken();
            privilege = "PROCESS";
         } else if (this.lexer.identifierEquals("RELOAD")) {
            this.lexer.nextToken();
            privilege = "RELOAD";
         } else if (this.lexer.identifierEquals("CONNECT")) {
            privilege = "CONNECT";
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("RESOURCE")) {
            this.lexer.nextToken();
            privilege = "RESOURCE";
         } else if (this.lexer.token == Token.CONNECT) {
            this.lexer.nextToken();
            privilege = "CONNECT";
         } else if (this.lexer.identifierEquals("REPLICATION")) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("SLAVE")) {
               this.lexer.nextToken();
               privilege = "REPLICATION SLAVE";
            } else {
               this.acceptIdentifier("CLIENT");
               privilege = "REPLICATION CLIENT";
            }
         } else if (this.lexer.token == Token.SHOW) {
            this.lexer.nextToken();
            if (this.lexer.token == Token.VIEW) {
               this.lexer.nextToken();
               privilege = "SHOW VIEW";
            } else if (this.lexer.identifierEquals("DATABASES")) {
               this.acceptIdentifier("DATABASES");
               privilege = "SHOW DATABASES";
            } else {
               privilege = "SHOW";
            }
         } else if (this.lexer.identifierEquals("SHUTDOWN")) {
            this.lexer.nextToken();
            privilege = "SHUTDOWN";
         } else if (this.lexer.identifierEquals("SUPER")) {
            this.lexer.nextToken();
            privilege = "SUPER";
         } else if (this.lexer.identifierEquals("CONTROL")) {
            this.lexer.nextToken();
            privilege = "CONTROL";
         } else if (this.lexer.identifierEquals("IMPERSONATE")) {
            this.lexer.nextToken();
            privilege = "IMPERSONATE";
         } else if (this.lexer.identifierEquals("LOAD")) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("DATA")) {
               this.lexer.nextToken();
               privilege = "LOAD DATA";
            }
         } else if (this.lexer.identifierEquals("DUMP")) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("DATA")) {
               this.lexer.nextToken();
               privilege = "DUMP DATA";
            }
         } else if (this.lexer.identifierEquals("DBA")) {
            this.lexer.nextToken();
            privilege = "DBA";
         } else if (this.lexer.identifierEquals("READ")) {
            this.lexer.nextToken();
            privilege = "READ";
         } else if (this.lexer.token == Token.LITERAL_CHARS) {
            privilege = this.lexer.stringVal();
            this.lexer.nextToken();
         } else if (this.lexer.token == Token.IDENTIFIER && JdbcUtils.isDmDbType(this.dbType)) {
            privilege = this.lexer.stringVal();
            this.lexer.nextToken();
         }

         if (privilege != null) {
            SQLExpr expr = new SQLIdentifierExpr(privilege);
            SQLPrivilegeItem privilegeItem = new SQLPrivilegeItem();
            privilegeItem.setAction(expr);
            if (this.lexer.token == Token.LPAREN) {
               this.lexer.nextToken();

               while(true) {
                  privilegeItem.getColumns().add(this.exprParser.name());
                  if (this.lexer.token() != Token.COMMA) {
                     this.accept(Token.RPAREN);
                     break;
                  }

                  this.lexer.nextToken();
               }
            }

            expr.setParent(parent);
            privileges.add(privilegeItem);
         }

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

         this.lexer.nextToken();
      }
   }

   public SQLRevokeStatement parseRevoke() {
      this.accept(Token.REVOKE);
      SQLRevokeStatement stmt = new SQLRevokeStatement(this.dbType);
      if (this.lexer.token == Token.GRANT) {
         this.lexer.nextToken();
         this.acceptIdentifier("OPTION");
         stmt.setGrantOption(true);
         if (this.lexer.token == Token.FOR) {
            this.lexer.nextToken();
         }
      }

      this.parsePrivileages(stmt.getPrivileges(), stmt);
      if (this.lexer.token == Token.ON) {
         this.lexer.nextToken();
         switch (this.lexer.token) {
            case TABLE:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.TABLE);
               break;
            case USER:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.USER);
               break;
            case FUNCTION:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.FUNCTION);
               break;
            case PROCEDURE:
               this.lexer.nextToken();
               stmt.setResourceType(SQLObjectType.PROCEDURE);
               break;
            case IDENTIFIER:
               if (this.lexer.identifierEquals("SYSTEM")) {
                  this.lexer.nextToken();
                  stmt.setResourceType(SQLObjectType.SYSTEM);
               } else if (this.lexer.identifierEquals("PROJECT")) {
                  this.lexer.nextToken();
                  stmt.setResourceType(SQLObjectType.PROJECT);
               } else if (this.lexer.identifierEquals("DIRECTORY")) {
                  this.lexer.nextToken();
                  stmt.setResourceType(SQLObjectType.DIRECTORY);
               }
         }

         SQLExpr expr = this.exprParser.expr();
         if (stmt.getResourceType() != SQLObjectType.TABLE && stmt.getResourceType() != null) {
            stmt.setResource(expr);
         } else {
            stmt.setResource(new SQLExprTableSource(expr));
         }
      }

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

         while(true) {
            if (this.lexer.token() == Token.USER && this.dbType == DbType.odps) {
               this.lexer.nextToken();
            }

            SQLExpr user = this.parseUser();
            stmt.getUsers().add(user);
            if (this.lexer.token() != Token.COMMA) {
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.identifierEquals("CASCADE") && JdbcUtils.isDmDbType(this.dbType)) {
         stmt.setCascade(true);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals("RESTRICT") && JdbcUtils.isDmDbType(this.dbType)) {
         stmt.setRestrict(true);
         this.lexer.nextToken();
      }

      return stmt;
   }

   public SQLStatement parseSavePoint() {
      this.acceptIdentifier("SAVEPOINT");
      SQLSavePointStatement stmt = new SQLSavePointStatement(this.getDbType());
      stmt.setName(this.exprParser.name());
      return stmt;
   }

   public SQLStatement parseReleaseSavePoint() {
      this.acceptIdentifier("RELEASE");
      this.acceptIdentifier("SAVEPOINT");
      SQLReleaseSavePointStatement stmt = new SQLReleaseSavePointStatement(this.getDbType());
      stmt.setName(this.exprParser.name());
      return stmt;
   }

   public SQLStatement parseAlter() {
      Lexer.SavePoint mark = this.lexer.mark();
      this.accept(Token.ALTER);
      if (this.lexer.token != Token.TABLE) {
         if (this.lexer.token == Token.VIEW) {
            this.lexer.nextToken();
            SQLName viewName = this.exprParser.name();
            if (this.lexer.identifierEquals("RENAME")) {
               this.lexer.nextToken();
               this.accept(Token.TO);
               SQLAlterViewRenameStatement stmt = new SQLAlterViewRenameStatement();
               stmt.setName(viewName);
               SQLName newName = this.exprParser.name();
               stmt.setTo(newName);
               return stmt;
            } else {
               throw new ParserException("TODO " + this.lexer.info());
            }
         } else if (this.lexer.token == Token.INDEX) {
            this.lexer.reset(mark);
            return this.parseAlterIndex();
         } else if (this.lexer.token == Token.DATABASE) {
            this.lexer.reset(mark);
            return this.parseAlterDatabase();
         } else if (this.lexer.token == Token.SCHEMA) {
            this.lexer.reset(mark);
            return this.parseAlterSchema();
         } else if (this.lexer.identifierEquals(FnvHash.Constants.RESOURCE)) {
            this.lexer.reset(mark);
            return this.parseAlterResourceGroup();
         } else {
            throw new ParserException("TODO " + this.lexer.info());
         }
      } else {
         this.lexer.nextToken();
         SQLAlterTableStatement stmt = new SQLAlterTableStatement(this.getDbType());
         if (this.lexer.token == Token.IF) {
            this.lexer.nextToken();
            this.accept(Token.EXISTS);
            stmt.setIfExists(true);
         }

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

         while(true) {
            while(this.lexer.token == Token.DROP) {
               this.parseAlterDrop(stmt);
            }

            if (!this.lexer.identifierEquals(FnvHash.Constants.ADD)) {
               if (this.lexer.token != Token.DISABLE) {
                  if (this.lexer.token != Token.ENABLE) {
                     if (this.lexer.token != Token.ALTER) {
                        if (this.lexer.token != Token.DELETE) {
                           if (!this.lexer.identifierEquals(FnvHash.Constants.CHANGE)) {
                              if (!this.lexer.identifierEquals(FnvHash.Constants.EXCHANGE)) {
                                 if (this.lexer.token != Token.WITH) {
                                    if (!this.lexer.identifierEquals("RENAME")) {
                                       if (this.lexer.token != Token.SET) {
                                          if (this.lexer.token != Token.PARTITION) {
                                             if (!this.lexer.identifierEquals("TOUCH")) {
                                                if (!this.lexer.identifierEquals(FnvHash.Constants.ARCHIVE)) {
                                                   if (!this.lexer.identifierEquals(FnvHash.Constants.UNARCHIVE)) {
                                                      if (!this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITION_AVAILABLE_PARTITION_NUM)) {
                                                         if (DbType.odps != this.dbType || !this.lexer.identifierEquals("MERGE")) {
                                                            if (DbType.odps != this.dbType || !this.lexer.identifierEquals(FnvHash.Constants.CLUSTERED)) {
                                                               if (DbType.odps != this.dbType || !this.lexer.identifierEquals(FnvHash.Constants.SORTED)) {
                                                                  if (stmt.getClusteredBy().size() <= 0 && stmt.getSortedBy().size() <= 0 || this.lexer.token != Token.INTO) {
                                                                     if (this.lexer.token != Token.REPLACE) {
                                                                        if (DbType.hive != this.dbType || !this.lexer.identifierEquals(FnvHash.Constants.RECOVER)) {
                                                                           return stmt;
                                                                        }

                                                                        this.lexer.nextToken();
                                                                        this.acceptIdentifier("PARTITIONS");
                                                                        stmt.addItem(new SQLAlterTableRecoverPartitions());
                                                                     } else {
                                                                        SQLAlterTableReplaceColumn item = this.parseAlterTableReplaceColumn();
                                                                        stmt.addItem(item);
                                                                     }
                                                                  } else {
                                                                     this.lexer.nextToken();
                                                                     if (this.lexer.token() != Token.LITERAL_INT) {
                                                                        throw new ParserException("into buckets must be integer. " + this.lexer.info());
                                                                     }

                                                                     int num = this.lexer.integerValue().intValue();
                                                                     this.lexer.nextToken();
                                                                     if (this.lexer.identifierEquals(FnvHash.Constants.BUCKETS)) {
                                                                        stmt.setBuckets(num);
                                                                        this.lexer.nextToken();
                                                                     } else {
                                                                        this.acceptIdentifier("SHARDS");
                                                                        stmt.setShards(num);
                                                                     }
                                                                  }
                                                               } else {
                                                                  this.lexer.nextToken();
                                                                  this.accept(Token.BY);
                                                                  this.accept(Token.LPAREN);

                                                                  while(true) {
                                                                     SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
                                                                     stmt.addSortedByItem(item);
                                                                     if (this.lexer.token() != Token.COMMA) {
                                                                        this.accept(Token.RPAREN);
                                                                        break;
                                                                     }

                                                                     this.lexer.nextToken();
                                                                  }
                                                               }
                                                            } else {
                                                               this.lexer.nextToken();
                                                               this.accept(Token.BY);
                                                               this.accept(Token.LPAREN);

                                                               while(true) {
                                                                  SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
                                                                  stmt.addClusteredByItem(item);
                                                                  if (this.lexer.token() != Token.COMMA) {
                                                                     this.accept(Token.RPAREN);
                                                                     break;
                                                                  }

                                                                  this.lexer.nextToken();
                                                               }
                                                            }
                                                         } else {
                                                            this.lexer.nextToken();
                                                            boolean ifExists = false;
                                                            if (this.lexer.token == Token.IF) {
                                                               this.lexer.nextToken();
                                                               this.accept(Token.EXISTS);
                                                               ifExists = true;
                                                            }

                                                            if (this.lexer.token != Token.PARTITION) {
                                                               this.acceptIdentifier("SMALLFILES");
                                                               stmt.setMergeSmallFiles(true);
                                                            } else {
                                                               SQLAlterTableMergePartition item = new SQLAlterTableMergePartition();

                                                               while(true) {
                                                                  item.addPartition(this.getExprParser().parsePartitionSpec());
                                                                  if (this.lexer.token != Token.COMMA) {
                                                                     this.accept(Token.OVERWRITE);
                                                                     item.setOverwritePartition(this.getExprParser().parsePartitionSpec());
                                                                     if (ifExists) {
                                                                        item.setIfExists(true);
                                                                     }

                                                                     stmt.addItem(item);
                                                                     break;
                                                                  }

                                                                  this.lexer.nextToken();
                                                               }
                                                            }
                                                         }
                                                      } else {
                                                         this.lexer.nextToken();
                                                         this.accept(Token.EQ);
                                                         SQLIntegerExpr num = this.exprParser.integerExpr();
                                                         SQLAlterTableSubpartitionAvailablePartitionNum item = new SQLAlterTableSubpartitionAvailablePartitionNum();
                                                         item.setNumber(num);
                                                         stmt.addItem(item);
                                                      }
                                                   } else {
                                                      this.lexer.nextToken();
                                                      this.accept(Token.PARTITION);
                                                      SQLAlterTableUnarchivePartition item = new SQLAlterTableUnarchivePartition();
                                                      this.accept(Token.LPAREN);
                                                      this.parseAssignItems(item.getPartitions(), item, false);
                                                      this.accept(Token.RPAREN);
                                                      stmt.addItem(item);
                                                   }
                                                } else {
                                                   this.lexer.nextToken();
                                                   this.accept(Token.PARTITION);
                                                   SQLAlterTableArchivePartition item = new SQLAlterTableArchivePartition();
                                                   this.accept(Token.LPAREN);
                                                   this.parseAssignItems(item.getPartitions(), item, false);
                                                   this.accept(Token.RPAREN);
                                                   stmt.addItem(item);
                                                }
                                             } else {
                                                this.lexer.nextToken();
                                                SQLAlterTableTouch item = new SQLAlterTableTouch();
                                                if (this.lexer.token == Token.PARTITION) {
                                                   this.lexer.nextToken();
                                                   this.accept(Token.LPAREN);
                                                   this.parseAssignItems(item.getPartition(), item);
                                                   this.accept(Token.RPAREN);
                                                }

                                                stmt.addItem(item);
                                             }
                                          } else {
                                             this.lexer.nextToken();
                                             SQLAlterTableRenamePartition renamePartition = new SQLAlterTableRenamePartition();
                                             this.accept(Token.LPAREN);
                                             this.parseAssignItems(renamePartition.getPartition(), renamePartition);
                                             this.accept(Token.RPAREN);
                                             if (this.lexer.token == Token.ENABLE) {
                                                this.lexer.nextToken();
                                                if (this.lexer.identifierEquals("LIFECYCLE")) {
                                                   this.lexer.nextToken();
                                                }

                                                SQLAlterTableEnableLifecycle enableLifeCycle = new SQLAlterTableEnableLifecycle();

                                                for(SQLAssignItem condition : renamePartition.getPartition()) {
                                                   enableLifeCycle.getPartition().add(condition);
                                                   condition.setParent(enableLifeCycle);
                                                }

                                                stmt.addItem(enableLifeCycle);
                                             } else if (this.lexer.token == Token.DISABLE) {
                                                this.lexer.nextToken();
                                                if (this.lexer.identifierEquals("LIFECYCLE")) {
                                                   this.lexer.nextToken();
                                                }

                                                SQLAlterTableDisableLifecycle disableLifeCycle = new SQLAlterTableDisableLifecycle();

                                                for(SQLAssignItem condition : renamePartition.getPartition()) {
                                                   disableLifeCycle.getPartition().add(condition);
                                                   condition.setParent(disableLifeCycle);
                                                }

                                                stmt.addItem(disableLifeCycle);
                                             } else {
                                                if (DbType.odps == this.dbType) {
                                                   if (this.lexer.identifierEquals("MERGE")) {
                                                      SQLAlterTablePartition alterTablePartition = new SQLAlterTablePartition();

                                                      for(SQLAssignItem condition : renamePartition.getPartition()) {
                                                         alterTablePartition.getPartition().add(condition);
                                                         condition.setParent(alterTablePartition);
                                                      }

                                                      stmt.addItem(alterTablePartition);
                                                      continue;
                                                   }

                                                   if (this.lexer.token == Token.SET) {
                                                      SQLAlterTablePartitionSetProperties alterTablePartition = new SQLAlterTablePartitionSetProperties();

                                                      for(SQLAssignItem condition : renamePartition.getPartition()) {
                                                         alterTablePartition.getPartition().add(condition);
                                                         condition.setParent(alterTablePartition);
                                                      }

                                                      this.lexer.nextToken();
                                                      this.acceptIdentifier("PARTITIONPROPERTIES");
                                                      this.accept(Token.LPAREN);
                                                      this.parseAssignItems(alterTablePartition.getPartitionProperties(), alterTablePartition);
                                                      this.accept(Token.RPAREN);
                                                      stmt.addItem(alterTablePartition);
                                                      continue;
                                                   }
                                                }

                                                this.acceptIdentifier("RENAME");
                                                this.accept(Token.TO);
                                                this.accept(Token.PARTITION);
                                                this.accept(Token.LPAREN);
                                                this.parseAssignItems(renamePartition.getTo(), renamePartition);
                                                this.accept(Token.RPAREN);
                                                stmt.addItem(renamePartition);
                                             }
                                          }
                                       } else {
                                          this.lexer.nextToken();
                                          if (this.lexer.token == Token.COMMENT) {
                                             this.lexer.nextToken();
                                             SQLAlterTableSetComment setComment = new SQLAlterTableSetComment();
                                             setComment.setComment(this.exprParser.primary());
                                             stmt.addItem(setComment);
                                          } else if (this.lexer.identifierEquals(FnvHash.Constants.LIFECYCLE)) {
                                             this.lexer.nextToken();
                                             SQLAlterTableSetLifecycle setLifecycle = new SQLAlterTableSetLifecycle();
                                             setLifecycle.setLifecycle(this.exprParser.primary());
                                             stmt.addItem(setLifecycle);
                                          } else if (this.lexer.identifierEquals(FnvHash.Constants.LOCATION)) {
                                             this.lexer.nextToken();
                                             SQLAlterTableSetLocation setLocation = new SQLAlterTableSetLocation();
                                             setLocation.setLocation(this.exprParser.primary());
                                             stmt.addItem(setLocation);
                                          } else {
                                             if (!this.lexer.identifierEquals(FnvHash.Constants.TBLPROPERTIES)) {
                                                throw new ParserException("TODO " + this.lexer.info());
                                             }

                                             this.lexer.nextToken();
                                             SQLAlterTableSetOption setOption = new SQLAlterTableSetOption();
                                             this.accept(Token.LPAREN);

                                             while(true) {
                                                SQLAssignItem item = this.exprParser.parseAssignItem();
                                                setOption.addOption(item);
                                                if (this.lexer.token != Token.COMMA) {
                                                   this.accept(Token.RPAREN);
                                                   stmt.addItem(setOption);
                                                   break;
                                                }

                                                this.lexer.nextToken();
                                             }
                                          }
                                       }
                                    } else {
                                       stmt.addItem(this.parseAlterTableRename());
                                    }
                                 } else {
                                    this.lexer.nextToken();
                                    this.acceptIdentifier("NOCHECK");
                                    this.acceptIdentifier("ADD");
                                    SQLConstraint check = this.exprParser.parseConstaint();
                                    SQLAlterTableAddConstraint addCheck = new SQLAlterTableAddConstraint();
                                    addCheck.setWithNoCheck(true);
                                    addCheck.setConstraint(check);
                                    stmt.addItem(addCheck);
                                 }
                              } else {
                                 this.lexer.nextToken();
                                 this.accept(Token.PARTITION);
                                 SQLAlterTableExchangePartition item = new SQLAlterTableExchangePartition();
                                 this.accept(Token.LPAREN);

                                 while(true) {
                                    SQLExpr partition = this.exprParser.name();
                                    if (this.lexer.token == Token.EQ) {
                                       this.lexer.nextToken();
                                       SQLExpr value = this.exprParser.primary();
                                       partition = new SQLAssignItem(partition, value);
                                    }

                                    item.addPartition(partition);
                                    if (this.lexer.token != Token.COMMA) {
                                       this.accept(Token.RPAREN);
                                       this.accept(Token.WITH);
                                       this.accept(Token.TABLE);
                                       SQLName table = this.exprParser.name();
                                       item.setTable(table);
                                       if (this.lexer.token == Token.WITH) {
                                          this.lexer.nextToken();
                                          this.acceptIdentifier("VALIDATION");
                                          item.setValidation(true);
                                       } else if (this.lexer.identifierEquals(FnvHash.Constants.WITHOUT)) {
                                          this.lexer.nextToken();
                                          this.acceptIdentifier("VALIDATION");
                                          item.setValidation(false);
                                       }

                                       stmt.addItem(item);
                                       break;
                                    }

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

                              SQLName columnName = this.exprParser.name();
                              if (this.lexer.identifierEquals("RENAME")) {
                                 this.lexer.nextToken();
                                 this.accept(Token.TO);
                                 SQLName toName = this.exprParser.name();
                                 SQLAlterTableRenameColumn renameColumn = new SQLAlterTableRenameColumn();
                                 renameColumn.setColumn(columnName);
                                 renameColumn.setTo(toName);
                                 stmt.addItem(renameColumn);
                              } else if (this.lexer.token == Token.COMMENT) {
                                 this.lexer.nextToken();
                                 SQLExpr comment;
                                 if (this.lexer.token == Token.LITERAL_ALIAS) {
                                    String alias = this.lexer.stringVal();
                                    if (alias.length() > 2 && alias.charAt(0) == '"' && alias.charAt(alias.length() - 1) == '"') {
                                       alias = alias.substring(1, alias.length() - 1);
                                    }

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

                                 SQLColumnDefinition column = new SQLColumnDefinition();
                                 column.setDbType(this.dbType);
                                 column.setName(columnName);
                                 column.setComment(comment);
                                 SQLAlterTableAlterColumn changeColumn = new SQLAlterTableAlterColumn();
                                 changeColumn.setColumn(column);
                                 stmt.addItem(changeColumn);
                              } else {
                                 SQLColumnDefinition column = this.exprParser.parseColumn();
                                 SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
                                 alterColumn.setColumn(column);
                                 alterColumn.setOriginColumn(columnName);
                                 if (this.lexer.identifierEquals(FnvHash.Constants.AFTER)) {
                                    this.lexer.nextToken();
                                    SQLName after = this.exprParser.name();
                                    alterColumn.setAfter(after);
                                 }

                                 stmt.addItem(alterColumn);
                              }
                           }
                        } else {
                           this.lexer.nextToken();
                           if (this.lexer.token != Token.WHERE) {
                              throw new ParserException("TODO " + this.lexer.info());
                           }

                           this.lexer.nextToken();
                           SQLAlterTableDeleteByCondition alterColumn = new SQLAlterTableDeleteByCondition();
                           alterColumn.setWhere(this.exprParser.expr());
                           stmt.addItem(alterColumn);
                        }
                     } else {
                        this.lexer.nextToken();
                        if (this.lexer.token == Token.COLUMN) {
                           SQLAlterTableAlterColumn alterColumn = this.parseAlterColumn();
                           stmt.addItem(alterColumn);
                           if (this.dbType == DbType.postgresql && this.lexer.token == Token.COMMA) {
                              this.lexer.nextToken();
                           }
                        } else {
                           if (this.lexer.token != Token.LITERAL_ALIAS) {
                              throw new ParserException("TODO " + this.lexer.info());
                           }

                           SQLAlterTableAlterColumn alterColumn = this.parseAlterColumn();
                           stmt.addItem(alterColumn);
                        }
                     }
                  } else {
                     this.lexer.nextToken();
                     if (this.lexer.token == Token.CONSTRAINT) {
                        this.lexer.nextToken();
                        SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
                        item.setConstraintName(this.exprParser.name());
                        stmt.addItem(item);
                     } else if (this.lexer.identifierEquals("LIFECYCLE")) {
                        this.lexer.nextToken();
                        SQLAlterTableEnableLifecycle item = new SQLAlterTableEnableLifecycle();
                        stmt.addItem(item);
                     } else {
                        this.acceptIdentifier("KEYS");
                        SQLAlterTableEnableKeys item = new SQLAlterTableEnableKeys();
                        stmt.addItem(item);
                     }
                  }
               } else {
                  this.lexer.nextToken();
                  if (this.lexer.token == Token.CONSTRAINT) {
                     this.lexer.nextToken();
                     SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
                     item.setConstraintName(this.exprParser.name());
                     stmt.addItem(item);
                  } else if (this.lexer.identifierEquals("LIFECYCLE")) {
                     this.lexer.nextToken();
                     SQLAlterTableDisableLifecycle item = new SQLAlterTableDisableLifecycle();
                     stmt.addItem(item);
                  } else {
                     this.acceptIdentifier("KEYS");
                     SQLAlterTableDisableKeys item = new SQLAlterTableDisableKeys();
                     stmt.addItem(item);
                  }
               }
            } else {
               this.lexer.nextToken();
               boolean ifNotExists = false;
               if (this.lexer.token == Token.IF) {
                  this.lexer.nextToken();
                  this.accept(Token.NOT);
                  this.accept(Token.EXISTS);
                  ifNotExists = true;
               }

               if (this.lexer.token == Token.PRIMARY) {
                  SQLPrimaryKey primaryKey = this.exprParser.parsePrimaryKey();
                  SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(primaryKey);
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.UNIQUE) {
                  SQLUnique unique = this.exprParser.parseUnique();
                  SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(unique);
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.IDENTIFIER) {
                  SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.LPAREN) {
                  if (this.dbType == DbType.h2) {
                     this.lexer.nextToken();
                     SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                     stmt.addItem(item);
                     this.accept(Token.RPAREN);
                  }
               } else if (this.lexer.token == Token.COLUMN) {
                  this.lexer.nextToken();
                  SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.CHECK) {
                  SQLCheck check = this.exprParser.parseCheck();
                  SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(check);
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.CONSTRAINT) {
                  SQLConstraint constraint = this.exprParser.parseConstaint();
                  SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(constraint);
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.FOREIGN) {
                  SQLConstraint constraint = this.exprParser.parseForeignKey();
                  SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(constraint);
                  stmt.addItem(item);
               } else if (this.lexer.token == Token.PARTITION) {
                  while(true) {
                     this.lexer.nextToken();
                     SQLAlterTableAddPartition addPartition = new SQLAlterTableAddPartition();
                     addPartition.setIfNotExists(ifNotExists);
                     this.accept(Token.LPAREN);
                     this.parseAssignItems(addPartition.getPartitions(), addPartition, false);
                     this.accept(Token.RPAREN);
                     if (this.lexer.identifierEquals(FnvHash.Constants.LOCATION)) {
                        this.lexer.nextToken();
                        SQLExpr location = this.exprParser.primary();
                        addPartition.setLocation(location);
                     }

                     stmt.addItem(addPartition);
                     if (this.lexer.token != Token.PARTITION) {
                        if (this.lexer.token != Token.COMMA) {
                           break;
                        }

                        this.lexer.nextToken();
                        if (!this.lexer.identifierEquals("ADD") && this.lexer.token != Token.PARTITION) {
                           break;
                        }
                     }
                  }
               } else {
                  if (this.lexer.token != Token.DEFAULT) {
                     throw new ParserException("TODO " + this.lexer.info());
                  }

                  SQLConstraint constraint = this.exprParser.parseConstaint();
                  SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(constraint);
                  stmt.addItem(item);
               }
            }
         }
      }
   }

   protected SQLStatement parseAlterDatabase() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   protected SQLStatement parseAlterSchema() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   protected SQLAlterTableItem parseAlterTableRename() {
      this.acceptIdentifier("RENAME");
      if (this.lexer.token == Token.COLUMN) {
         this.lexer.nextToken();
         SQLAlterTableRenameColumn renameColumn = new SQLAlterTableRenameColumn();
         renameColumn.setColumn(this.exprParser.name());
         this.accept(Token.TO);
         renameColumn.setTo(this.exprParser.name());
         return renameColumn;
      } else if (this.lexer.token == Token.TO) {
         this.lexer.nextToken();
         SQLAlterTableRename item = new SQLAlterTableRename();
         item.setTo((SQLExpr)this.exprParser.name());
         return item;
      } else {
         throw new ParserException("TODO " + this.lexer.info());
      }
   }

   protected SQLAlterTableAlterColumn parseAlterColumn() {
      this.lexer.nextToken();
      SQLColumnDefinition column = this.exprParser.parseColumn();
      SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
      alterColumn.setColumn(column);
      return alterColumn;
   }

   public void parseAlterDrop(SQLAlterTableStatement stmt) {
      this.lexer.nextToken();
      boolean ifExists = false;
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         ifExists = true;
      }

      if (this.lexer.token == Token.CONSTRAINT) {
         this.lexer.nextToken();
         SQLAlterTableDropConstraint item = new SQLAlterTableDropConstraint();
         item.setConstraintName(this.exprParser.name());
         if (this.lexer.token == Token.RESTRICT) {
            this.lexer.nextToken();
            item.setRestrict(true);
         } else if (this.lexer.token == Token.CASCADE) {
            this.lexer.nextToken();
            item.setCascade(true);
         }

         stmt.addItem(item);
      } else if (this.lexer.token == Token.COLUMN) {
         this.lexer.nextToken();
         SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
         if (this.dbType == DbType.postgresql) {
            item.getColumns().add(this.exprParser.name());
         } else {
            this.exprParser.names(item.getColumns());
         }

         if (this.lexer.token == Token.CASCADE) {
            item.setCascade(true);
            this.lexer.nextToken();
         }

         stmt.addItem(item);
         if (this.dbType == DbType.postgresql && this.lexer.token == Token.COMMA) {
            this.lexer.nextToken();
         }
      } else if (this.lexer.token == Token.LITERAL_ALIAS) {
         SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
         this.exprParser.names(item.getColumns());
         if (this.lexer.token == Token.CASCADE) {
            item.setCascade(true);
            this.lexer.nextToken();
         }

         stmt.addItem(item);
      } else if (this.lexer.token == Token.PARTITION) {
         SQLAlterTableDropPartition dropPartition = this.parseAlterTableDropPartition(ifExists);
         stmt.addItem(dropPartition);

         while(this.lexer.token == Token.COMMA) {
            this.lexer.nextToken();
            Lexer.SavePoint mark = this.lexer.mark();
            if (this.lexer.token == Token.PARTITION) {
               dropPartition = this.parseAlterTableDropPartition(ifExists);
               stmt.addItem(dropPartition);
            } else {
               this.lexer.reset(mark);
            }
         }
      } else if (this.lexer.token == Token.INDEX) {
         this.lexer.nextToken();
         SQLName indexName = this.exprParser.name();
         SQLAlterTableDropIndex item = new SQLAlterTableDropIndex();
         item.setIndexName(indexName);
         stmt.addItem(item);
      } else {
         if (this.lexer.token != Token.PRIMARY) {
            throw new ParserException("TODO " + this.lexer.info());
         }

         this.lexer.nextToken();
         this.accept(Token.KEY);
         SQLAlterTableDropPrimaryKey item = new SQLAlterTableDropPrimaryKey();
         stmt.addItem(item);
      }

   }

   protected SQLAlterTableDropPartition parseAlterTableDropPartition(boolean ifExists) {
      this.lexer.nextToken();
      SQLAlterTableDropPartition dropPartition = new SQLAlterTableDropPartition();
      dropPartition.setIfExists(ifExists);
      if (this.lexer.token == Token.LPAREN) {
         this.accept(Token.LPAREN);
         this.exprParser.exprList(dropPartition.getPartitions(), dropPartition);
         this.accept(Token.RPAREN);
         if (this.lexer.identifierEquals("PURGE")) {
            this.lexer.nextToken();
            dropPartition.setPurge(true);
         }
      } else {
         while(true) {
            SQLExpr partition = this.exprParser.expr();
            dropPartition.addPartition(partition);
            if (this.lexer.token != Token.COMMA) {
               dropPartition.getAttributes().put("SIMPLE", true);
               break;
            }

            this.lexer.nextToken();
         }
      }

      return dropPartition;
   }

   protected SQLAlterTableDropSubpartition parseAlterTableDropSubpartition() {
      this.lexer.nextToken();
      SQLAlterTableDropSubpartition item = new SQLAlterTableDropSubpartition();
      if (this.lexer.token() == Token.LITERAL_INT) {
         while(true) {
            item.getPartitionIds().add(this.exprParser.integerExpr());
            String pidStr = this.lexer.stringVal();
            this.accept(Token.VARIANT);
            String s = pidStr.replaceAll(":", "");
            if (StringUtils.isEmpty(s)) {
               item.getSubpartitionIds().add(this.exprParser.integerExpr());
            } else {
               item.getSubpartitionIds().add(new SQLIntegerExpr(Integer.valueOf(s)));
            }

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

            this.lexer.nextToken();
         }
      }

      return item;
   }

   public SQLStatement parseRename() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   public SQLStatement parseList() {
      if (this.lexer.identifierEquals(FnvHash.Constants.LIST)) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals(FnvHash.Constants.RESOURCE)) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals(FnvHash.Constants.GROUP) || this.lexer.token == Token.GROUP) {
               this.lexer.nextToken();
               return new SQLListResourceGroupStatement();
            }
         }
      }

      return null;
   }

   protected SQLDropTableStatement parseDropTable(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

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

      if (this.lexer.identifierEquals(FnvHash.Constants.PARTITIONED)) {
         this.lexer.nextToken();
         stmt.setDropPartition(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
         this.lexer.nextToken();
         stmt.setExternal(true);
      }

      if (this.lexer.token == Token.TABLE) {
         this.lexer.nextToken();
      } else {
         if (!this.lexer.identifierEquals(FnvHash.Constants.TABLES) || this.dbType != DbType.mysql) {
            throw new ParserException("expected token: TABLE.");
         }

         this.lexer.nextToken();
      }

      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      while(true) {
         SQLName name = this.exprParser.name();
         stmt.addPartition(new SQLExprTableSource(name));
         if (this.lexer.token != Token.COMMA) {
            while(true) {
               while(!this.lexer.identifierEquals("RESTRICT")) {
                  if (this.lexer.identifierEquals(FnvHash.Constants.CASCADE)) {
                     this.lexer.nextToken();
                     stmt.setCascade(true);
                     if (this.lexer.identifierEquals("CONSTRAINTS")) {
                        this.lexer.nextToken();
                     }
                  } else {
                     if (this.lexer.token != Token.PURGE && !this.lexer.identifierEquals("PURGE")) {
                        if (stmt.isDropPartition() && this.lexer.token() == Token.WHERE) {
                           this.lexer.nextToken();
                           name = (SQLName) this.exprParser.expr();
                           stmt.setWhere(name);
                        }

                        return stmt;
                     }

                     this.lexer.nextToken();
                     stmt.setPurge(true);
                  }
               }

               this.lexer.nextToken();
               stmt.setRestrict(true);
            }
         }

         this.lexer.nextToken();
      }
   }

   protected SQLDropDomainStatement parseDropDomian() {
      this.accept(Token.DROP);
      SQLDropDomainStatement stmt = new SQLDropDomainStatement(this.getDbType());
      this.acceptIdentifier("DOMAIN");
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      stmt.setName(this.exprParser.name());
      if (this.lexer.identifierEquals("RESTRICT")) {
         this.lexer.nextToken();
         stmt.setRestrict(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.CASCADE)) {
         this.lexer.nextToken();
         stmt.setCascade(true);
      }

      return stmt;
   }

   protected SQLDropContextStatement parseDropContext() {
      this.accept(Token.DROP);
      SQLDropContextStatement stmt = new SQLDropContextStatement(this.getDbType());
      this.acceptIdentifier("CONTEXT");
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      stmt.setNamespace(this.getExprParser().name());
      return stmt;
   }

   protected SQLDropDirectoryStatement parseDropDirectory() {
      this.accept(Token.DROP);
      SQLDropDirectoryStatement stmt = new SQLDropDirectoryStatement(this.getDbType());
      this.acceptIdentifier("DIRECTORY");
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLDropSequenceStatement parseDropSequence(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      this.lexer.nextToken();
      SQLDropSequenceStatement stmt = new SQLDropSequenceStatement(this.getDbType());
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLDropTriggerStatement parseDropTrigger(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      this.lexer.nextToken();
      SQLDropTriggerStatement stmt = new SQLDropTriggerStatement(this.getDbType());
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLDropViewStatement parseDropView(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      SQLDropViewStatement stmt = new SQLDropViewStatement(this.getDbType());
      this.accept(Token.VIEW);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      while(true) {
         SQLName name = this.exprParser.name();
         stmt.addPartition(new SQLExprTableSource(name));
         if (this.lexer.token != Token.COMMA) {
            if (this.lexer.identifierEquals("RESTRICT")) {
               this.lexer.nextToken();
               stmt.setRestrict(true);
            } else if (this.lexer.identifierEquals("CASCADE")) {
               this.lexer.nextToken();
               if (this.lexer.identifierEquals("CONSTRAINTS")) {
                  this.lexer.nextToken();
               }

               stmt.setCascade(true);
            }

            return stmt;
         }

         this.lexer.nextToken();
      }
   }

   protected SQLDropStatement parseDropSchema() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   protected SQLDropStatement parseDropDatabaseOrSchema(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      SQLDropDatabaseStatement stmt = new SQLDropDatabaseStatement(this.getDbType());
      if (this.lexer.token == Token.SCHEMA) {
         this.lexer.nextToken();
      } else {
         this.accept(Token.DATABASE);
      }

      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      SQLName name = this.exprParser.name();
      stmt.setDatabase((SQLExpr)name);
      if (this.lexer.identifierEquals(FnvHash.Constants.RESTRICT)) {
         this.lexer.nextToken();
         stmt.setRestrict(true);
      } else if (this.lexer.token != Token.CASCADE && !this.lexer.identifierEquals(FnvHash.Constants.CASCADE)) {
         stmt.setCascade(false);
      } else {
         this.lexer.nextToken();
         stmt.setCascade(true);
      }

      return stmt;
   }

   protected SQLDropFunctionStatement parseDropFunction(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      SQLDropFunctionStatement stmt = new SQLDropFunctionStatement(this.getDbType());
      this.accept(Token.FUNCTION);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   protected SQLDropTableSpaceStatement parseDropTablespace(boolean acceptDrop) {
      SQLDropTableSpaceStatement stmt = new SQLDropTableSpaceStatement(this.getDbType());
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         stmt.addBeforeComment(this.lexer.readAndResetComments());
      }

      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      this.accept(Token.TABLESPACE);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      SQLName name = this.exprParser.name();
      stmt.setName(name);
      if (this.lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.EQ) {
            this.lexer.nextToken();
         }

         SQLExpr engine = this.exprParser.primary();
         stmt.setEngine(engine);
      }

      if (this.lexer.identifierEquals("INCLUDING")) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("CONTENTS")) {
            stmt.setIncludingContents(true);
            this.lexer.nextToken();
         }
      }

      return stmt;
   }

   protected SQLDropProcedureStatement parseDropProcedure(boolean acceptDrop) {
      if (acceptDrop) {
         this.accept(Token.DROP);
      }

      SQLDropProcedureStatement stmt = new SQLDropProcedureStatement(this.getDbType());
      this.accept(Token.PROCEDURE);
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

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

   public SQLStatement parseTruncate() {
      this.accept(Token.TRUNCATE);
      if (this.lexer.token == Token.TABLE) {
         this.lexer.nextToken();
      }

      SQLTruncateStatement stmt = new SQLTruncateStatement(this.getDbType());
      if (this.lexer.token == Token.ONLY) {
         this.lexer.nextToken();
         stmt.setOnly(true);
      }

      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      while(true) {
         SQLName name = this.exprParser.name();
         stmt.addTableSource(name);
         if (this.lexer.token != Token.COMMA) {
            if (this.lexer.token == Token.PARTITION) {
               this.lexer.nextToken();
               if (this.lexer.token == Token.LPAREN) {
                  this.accept(Token.LPAREN);

                  while(true) {
                     SQLAssignItem item = this.exprParser.parseAssignItem();
                     item.setParent(stmt);
                     stmt.getPartitions().add(item);
                     if (this.lexer.token != Token.COMMA) {
                        this.accept(Token.RPAREN);
                        break;
                     }

                     this.lexer.nextToken();
                  }
               } else if (this.lexer.token == Token.ALL) {
                  this.lexer.nextToken();
                  stmt.setPartitionAll(true);
               } else {
                  while(true) {
                     stmt.getPartitionsForADB().add(this.exprParser.integerExpr());
                     if (this.lexer.token != Token.COMMA) {
                        break;
                     }

                     this.lexer.nextToken();
                  }
               }
            }

            while(true) {
               while(this.lexer.token == Token.PURGE && this.dbType != DbType.gauss) {
                  this.lexer.nextToken();
                  if (!this.lexer.identifierEquals("SNAPSHOT")) {
                     throw new ParserException("TODO : " + this.lexer.token + " " + this.lexer.stringVal());
                  }

                  this.lexer.nextToken();
                  this.acceptIdentifier("LOG");
                  stmt.setPurgeSnapshotLog(true);
               }

               if (this.lexer.token != Token.RESTART) {
                  if (this.lexer.token != Token.SHARE) {
                     if (this.dbType != DbType.greenplum && this.dbType != DbType.gauss || this.lexer.token != Token.CONTINUE) {
                        if (this.lexer.token != Token.CASCADE) {
                           if (this.lexer.token != Token.RESTRICT) {
                              if (this.lexer.token != Token.DROP) {
                                 if (!this.lexer.identifierEquals("REUSE")) {
                                    if (!this.lexer.identifierEquals("IGNORE")) {
                                       if (!this.lexer.identifierEquals("RESTRICT")) {
                                          if (this.lexer.token != Token.CONTINUE) {
                                             if (!this.lexer.identifierEquals("IMMEDIATE")) {
                                                if (this.dbType != DbType.gauss || this.lexer.token != Token.PURGE) {
                                                   return stmt;
                                                }

                                                this.lexer.nextToken();
                                                stmt.setPurge(Boolean.TRUE);
                                             } else {
                                                this.lexer.nextToken();
                                                stmt.setImmediate(true);
                                             }
                                          } else {
                                             this.lexer.nextToken();
                                             this.accept(Token.IDENTITY);
                                          }
                                       } else {
                                          this.lexer.nextToken();
                                          this.accept(Token.WHEN);
                                          this.accept(Token.DELETE);
                                          this.acceptIdentifier("TRIGGERS");
                                          stmt.setRestrictWhenDeleteTriggers(true);
                                       }
                                    } else {
                                       this.lexer.nextToken();
                                       this.accept(Token.DELETE);
                                       this.acceptIdentifier("TRIGGERS");
                                       stmt.setIgnoreDeleteTriggers(true);
                                    }
                                 } else {
                                    this.lexer.nextToken();
                                    this.acceptIdentifier("STORAGE");
                                    stmt.setReuseStorage(true);
                                 }
                              } else {
                                 this.lexer.nextToken();
                                 this.acceptIdentifier("STORAGE");
                                 stmt.setDropStorage(true);
                              }
                           } else {
                              this.lexer.nextToken();
                              stmt.setCascade(Boolean.FALSE);
                           }
                        } else {
                           this.lexer.nextToken();
                           stmt.setCascade(Boolean.TRUE);
                        }
                     } else {
                        this.lexer.nextToken();
                        this.accept(Token.IDENTITY);
                        stmt.setRestartIdentity(Boolean.FALSE);
                     }
                  } else {
                     this.lexer.nextToken();
                     this.accept(Token.IDENTITY);
                     stmt.setRestartIdentity(Boolean.FALSE);
                  }
               } else {
                  this.lexer.nextToken();
                  this.accept(Token.IDENTITY);
                  stmt.setRestartIdentity(Boolean.TRUE);
               }
            }
         }

         this.lexer.nextToken();
      }
   }

   public SQLStatement parseInsert() {
      SQLInsertStatement stmt = new SQLInsertStatement();
      stmt.setDbType(this.getDbType());
      if (this.lexer.token == Token.INSERT) {
         this.accept(Token.INSERT);
      }

      this.parseInsert0(stmt);
      return stmt;
   }

   protected void parseInsert0(SQLInsertInto insertStatement) {
      this.parseInsert0(insertStatement, true);
   }

   protected void parseInsert0_hinits(SQLInsertInto insertStatement) {
   }

   protected void parseInsert0(SQLInsertInto insertStatement, boolean acceptSubQuery) {
      if (this.lexer.token == Token.INTO) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.LPAREN) {
            SQLSelect select = this.createSQLSelectParser().select();
            insertStatement.setSelect(select);
            if ((this.lexer.token() == Token.IDENTIFIER || this.lexer.token() == Token.AS) && insertStatement instanceof DmInsertStatement) {
               if (this.lexer.token() == Token.AS) {
                  ((DmInsertStatement)insertStatement).setSelectAsToken(true);
               }

               ((DmInsertStatement)insertStatement).setSelectAlias(this.tableAlias());
            }
         } else {
            this.parseTable(insertStatement);
            if (this.lexer.token == Token.LITERAL_ALIAS) {
               insertStatement.setAlias(this.tableAlias());
            }
         }

         this.parseInsert0_hinits(insertStatement);
         if (this.lexer.token == Token.IDENTIFIER || this.lexer.token == Token.AS) {
            if (this.lexer.token() == Token.AS) {
               this.lexer.nextToken();
            }

            insertStatement.setAlias(this.lexer.stringVal());
            this.lexer.nextToken();
         }
      }

      if (this.lexer.token == Token.LPAREN) {
         this.lexer.nextToken();
         this.parseInsertColumns(insertStatement);
         this.accept(Token.RPAREN);
      }

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

         while(true) {
            if (this.lexer.token == Token.LPAREN) {
               this.lexer.nextToken();
               SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
               this.exprParser.exprList(values.getValues(), values);
               insertStatement.addValueCause(values);
               this.accept(Token.RPAREN);
            } else {
               SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
               SQLExpr value = this.exprParser.expr();
               values.addValue(value);
               insertStatement.addValueCause(values);
            }

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

            this.lexer.nextToken();
         }
      } else if (!acceptSubQuery || this.lexer.token != Token.SELECT && this.lexer.token != Token.LPAREN) {
         if (this.lexer.identifierEquals(FnvHash.Constants.VALUE)) {
            throw new ParserException("'values' expected, but 'value'. " + this.lexer.info());
         }
      } else {
         SQLSelect select = this.createSQLSelectParser().select();
         insertStatement.setQuery(select);
      }

   }

   protected void parseTable(SQLInsertInto insertStatement) {
      SQLName tableName = this.exprParser.name();
      insertStatement.setTableName(tableName);
   }

   public void parsePartition(DmSelectTableReference tableReference) {
      if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();
         DmPartitionExtensionClause partition = new DmPartitionExtensionClause();
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();
            partition.setPartition(this.exprParser.name());
            this.accept(Token.RPAREN);
         } else if (this.lexer.token() == Token.BY) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            partition.setPartition(this.exprParser.name());
            this.accept(Token.RPAREN);
         } else {
            this.accept(Token.FOR);
            this.accept(Token.LPAREN);
            this.exprParser.names(partition.getFor());
            this.accept(Token.RPAREN);
         }

         tableReference.setPartition(partition);
      }

      if (this.lexer.identifierEquals("SUBPARTITION")) {
         this.lexer.nextToken();
         DmPartitionExtensionClause partition = new DmPartitionExtensionClause();
         partition.setSubPartition(true);
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();
            partition.setPartition(this.exprParser.name());
            this.accept(Token.RPAREN);
         } else {
            this.accept(Token.FOR);
            this.accept(Token.LPAREN);
            this.exprParser.names(partition.getFor());
            this.accept(Token.RPAREN);
         }

         tableReference.setPartition(partition);
      }

   }

   protected void parseInsertColumns(SQLInsertInto insert) {
      this.exprParser.exprList(insert.getColumns(), insert);
   }

   public boolean parseStatementListDialect(List<SQLStatement> statementList) {
      return false;
   }

   public SQLDropUserStatement parseDropUser() {
      this.accept(Token.USER);
      SQLDropUserStatement stmt = new SQLDropUserStatement(this.getDbType());
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      while(true) {
         if (this.lexer.token == Token.IF) {
            this.lexer.nextToken();
            this.accept(Token.EXISTS);
         }

         SQLExpr expr = this.exprParser.expr();
         stmt.addUser(expr);
         if (this.lexer.token != Token.COMMA) {
            if (this.lexer.token == Token.CASCADE) {
               stmt.setCascade(true);
               this.lexer.nextToken();
            }

            if (this.lexer.token == Token.RESTRICT) {
               stmt.setRestrict(true);
               this.lexer.nextToken();
            }

            return stmt;
         }

         this.lexer.nextToken();
      }
   }

   public SQLStatement parseDropIndex() {
      this.accept(Token.INDEX);
      SQLDropIndexStatement stmt = new SQLDropIndexStatement(this.getDbType());
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      String indexNames;
      for(indexNames = this.exprParser.name().toString(); this.lexer.token == Token.COMMA; indexNames = indexNames + ", " + this.exprParser.name().toString()) {
         this.lexer.nextToken();
      }

      stmt.setIndexName(new SQLIdentifierExpr(indexNames));
      if (this.lexer.token == Token.CASCADE && (this.dbType.equals("gauss") || this.dbType.equals("db2"))) {
         stmt.setCascade(true);
         this.lexer.nextToken();
      }

      if (this.lexer.token == Token.RESTRICT && (this.dbType.equals("gauss") || this.dbType.equals("db2"))) {
         stmt.setRestrict(true);
         this.lexer.nextToken();
      }

      if (this.lexer.token == Token.ON) {
         this.lexer.nextToken();
         stmt.setTableName(this.exprParser.name());
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.ALGORITHM)) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.EQ) {
            this.lexer.nextToken();
         }

         SQLExpr algorithm = this.exprParser.primary();
         stmt.setAlgorithm(algorithm);
      }

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

         SQLExpr option = this.exprParser.primary();
         stmt.setLockOption(option);
      }

      return stmt;
   }

   public SQLCallStatement parseCall() {
      boolean brace = false;
      if (this.lexer.token == Token.LBRACE) {
         this.lexer.nextToken();
         brace = true;
      }

      SQLCallStatement stmt = new SQLCallStatement(this.getDbType());
      if (this.lexer.token == Token.QUES) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         stmt.setOutParameter(new SQLVariantRefExpr("?"));
      }

      this.acceptIdentifier("CALL");
      stmt.setProcedureName(this.exprParser.name());
      if (this.lexer.token == Token.LPAREN) {
         this.lexer.nextToken();
         this.exprParser.exprList(stmt.getParameters(), stmt);
         this.accept(Token.RPAREN);
      }

      if (brace) {
         this.accept(Token.RBRACE);
         stmt.setBrace(true);
      }

      return stmt;
   }

   public SQLStatement parseSet() {
      this.accept(Token.SET);
      if (this.lexer.token() == Token.SCHEMA) {
         this.accept(Token.SCHEMA);
         SQLSetSchemaStatement setSchemaStatement = new SQLSetSchemaStatement();
         setSchemaStatement.setSchemaName((SQLIdentifierExpr)this.exprParser.name());
         return setSchemaStatement;
      } else if (this.lexer.identifierEquals("TIME")) {
         SQLSetTimeZoneStatement setTimeZone = this.parseSetTimeZone();
         return setTimeZone;
      } else {
         SQLSetStatement stmt = new SQLSetStatement(this.getDbType());
         this.parseAssignItems(stmt.getItems(), stmt);
         return stmt;
      }
   }

   protected SQLSetTimeZoneStatement parseSetTimeZone() {
      this.acceptIdentifier("TIME");
      this.acceptIdentifier("ZONE");
      SQLSetTimeZoneStatement setTimeZone = new SQLSetTimeZoneStatement(this.dbType);
      if (this.lexer.token() == Token.LOCAL) {
         this.lexer.nextToken();
         setTimeZone.setLocal(true);
      } else if (this.lexer.token() == Token.INTERVAL) {
         this.lexer.nextToken();
         setTimeZone.setInterval(true);
         setTimeZone.setTimeZone(this.exprParser.expr());
         if (this.lexer.token() != Token.SEMI && this.lexer.token() != Token.EOF) {
            setTimeZone.setType(this.exprParser.parseDataType());
         }
      } else {
         setTimeZone.setTimeZone(this.exprParser.expr());
      }

      return setTimeZone;
   }

   public void parseAssignItems(List<? super SQLAssignItem> items, SQLObject parent) {
      this.parseAssignItems(items, parent, true);
   }

   public void parseAssignItems(List<? super SQLAssignItem> items, SQLObject parent, boolean variant) {
      while(true) {
         SQLAssignItem item = this.exprParser.parseAssignItem(variant);
         item.setParent(parent);
         items.add(item);
         if (this.lexer.token != Token.COMMA) {
            return;
         }

         this.lexer.nextToken();
      }
   }

   public SQLPartitionRef parsePartitionRef() {
      this.accept(Token.PARTITION);
      SQLPartitionRef partitionRef = new SQLPartitionRef();
      this.accept(Token.LPAREN);

      while(true) {
         SQLIdentifierExpr name = (SQLIdentifierExpr)this.exprParser.name();
         if (this.lexer.token == Token.EQ) {
            this.lexer.nextToken();
            SQLExpr value = this.exprParser.expr();
            partitionRef.addItem(name, value);
         } else {
            partitionRef.addItem(new SQLPartitionRef.Item(name));
         }

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

         this.lexer.nextToken();
      }
   }

   public SQLStatement parseCreatePackage() {
      throw new ParserException("TODO " + this.lexer.info());
   }

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

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

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

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

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

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

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

            return stmt;
         case USER:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            return this.parseCreateUser();
         case INDEX:
         case NOT:
         case UNIQUE:
            SQLCreateIndexStatement createIndex = this.parseCreateIndex(false);
            if (nonclustered) {
               createIndex.setType("NONCLUSTERED");
            }

            return createIndex;
         case DATABASE:
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("LINK")) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateDbLink();
            }

            this.lexer.reset(markBp, markChar, Token.CREATE);
            SQLStatement stmt4 = this.parseCreateDatabase();
            if (comments != null) {
               stmt4.addBeforeComment(comments);
               List<String> var12 = null;
            }

            return stmt4;
         case SCHEMA:
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("LINK")) {
               this.lexer.reset(markBp, markChar, Token.CREATE);
               return this.parseCreateDbLink();
            }

            this.lexer.reset(markBp, markChar, Token.CREATE);
            SQLStatement stmt5 = this.parseCreateSchema();
            if (comments != null) {
               stmt5.addBeforeComment(comments);
               List<String> var11 = null;
            }

            return stmt5;
         case FUNCTION:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            SQLStatement createFunct = this.parseCreateFunction();
            return createFunct;
         case TABLESPACE:
            this.lexer.reset(markBp, markChar, Token.CREATE);
            return this.parseCreateTableSpace();
         case SEQUENCE:
            return this.parseCreateSequence(false);
         default:
            if (token == Token.OR) {
               this.lexer.nextToken();
               this.accept(Token.REPLACE);
               if (this.lexer.identifierEquals(FnvHash.Constants.FORCE)) {
                  this.lexer.nextToken();
               }

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

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

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

                  return createIndex3;
               }
            }
      }
   }

   protected SQLCreateContextStatement parseCreateContext() {
      SQLCreateContextStatement contextStatement = new SQLCreateContextStatement();
      this.accept(Token.CREATE);
      if (this.lexer.token == Token.OR) {
         this.lexer.nextToken();
         this.accept(Token.REPLACE);
         contextStatement.setOrReplace(true);
      }

      this.acceptIdentifier("CONTEXT");
      contextStatement.setNamespace(this.getExprParser().name());
      this.accept(Token.USING);
      contextStatement.setPackagename(this.getExprParser().name());
      return contextStatement;
   }

   protected SQLCreateDomainStatement parseCreateDomain() {
      this.accept(Token.CREATE);
      this.acceptIdentifier("DOMAIN");
      SQLCreateDomainStatement createDomain = new SQLCreateDomainStatement();
      createDomain.setName(this.exprParser.name());
      this.acceptIf(Token.AS);
      createDomain.setDataType(this.exprParser.parseDataType());
      if (this.lexer.token == Token.DEFAULT) {
         this.accept(Token.DEFAULT);
         createDomain.setDefaultExpr(this.exprParser.expr());
      } else if (this.lexer.token == Token.CONSTRAINT || this.lexer.token == Token.CHECK) {
         if (this.lexer.token == Token.CONSTRAINT) {
            this.accept(Token.CONSTRAINT);
            createDomain.setConstraintName(this.exprParser.name());
         }

         if (this.lexer.token == Token.CHECK) {
            this.accept(Token.CHECK);
            this.accept(Token.LPAREN);
            createDomain.setCheckExpr(this.exprParser.expr());
            this.accept(Token.RPAREN);
         }
      }

      return createDomain;
   }

   protected SQLCreateDirectoryStatement parseCreateDirectory() {
      SQLCreateDirectoryStatement createDirectory = new SQLCreateDirectoryStatement();
      this.accept(Token.CREATE);
      if (this.lexer.token == Token.OR) {
         this.lexer.nextToken();
         this.accept(Token.REPLACE);
         createDirectory.setOrReplace(true);
      }

      this.acceptIdentifier("DIRECTORY");
      createDirectory.setName(this.exprParser.name());
      this.acceptIf(Token.AS);
      createDirectory.setDirPath(this.exprParser.name());
      return createDirectory;
   }

   protected SQLStatement parseCreateTableSpace() {
      this.accept(Token.CREATE);
      this.accept(Token.TABLESPACE);
      SQLCreateTablespaceStatement stmt = new SQLCreateTablespaceStatement();
      stmt.setName(this.exprParser.name());
      this.acceptIdentifier("DATAFILE");

      while(true) {
         SQLCreateTablespaceStatement.FileItem fileItem = this.parseFileItem();
         stmt.getFileItems().add(fileItem);
         if (this.lexer.token() != Token.COMMA) {
            return stmt;
         }

         this.accept(Token.COMMA);
      }
   }

   protected SQLCreateTablespaceStatement.FileItem parseFileItem() {
      SQLCreateTablespaceStatement.FileItem fileItem = new SQLCreateTablespaceStatement.FileItem();
      fileItem.setFilepath(this.exprParser.name());
      if (this.lexer.identifierEquals("MIRROR")) {
         this.acceptIdentifier("MIRROR");
         fileItem.setMirrorFilepath(this.exprParser.name());
      }

      this.acceptIdentifier("SIZE");
      fileItem.setFilesize(this.exprParser.expr());
      if (this.lexer.identifierEquals("autoextend")) {
         this.acceptIdentifier("autoextend");
         if (this.lexer.token() == Token.ON) {
            fileItem.setOn(true);
            this.accept(Token.ON);
            if (this.lexer.token() == Token.NEXT) {
               this.accept(Token.NEXT);
               fileItem.setNext(this.exprParser.expr());
            }

            if (this.lexer.token() == Token.MAXSIZE) {
               this.accept(Token.MAXSIZE);
               fileItem.setMaxsize(this.exprParser.expr());
            }
         }

         if (this.lexer.identifierEquals("OFF")) {
            fileItem.setOff(true);
            this.acceptIdentifier("OFF");
         }
      }

      return fileItem;
   }

   public SQLStatement parseCreateRole() {
      this.accept(Token.CREATE);
      this.acceptIdentifier("ROLE");
      SQLName name = this.exprParser.name();
      SQLCreateRoleStatement stmt = new SQLCreateRoleStatement(this.dbType);
      stmt.setName(name);
      return stmt;
   }

   public SQLStatement parseCreateType() {
      throw new ParserException("TODO " + this.lexer.token);
   }

   public SQLStatement parseCreateTableGroup() {
      this.accept(Token.CREATE);
      this.acceptIdentifier("TABLEGROUP");
      SQLCreateTableGroupStatement stmt = new SQLCreateTableGroupStatement();
      if (this.lexer.identifierEquals(FnvHash.Constants.IF)) {
         this.lexer.nextToken();
         this.accept(Token.NOT);
         this.accept(Token.EXISTS);
         stmt.setIfNotExists(true);
      }

      SQLName name = this.exprParser.name();
      stmt.setName(name);
      if (this.lexer.token == Token.PARTITION || this.lexer.identifierEquals("PARTITION")) {
         this.lexer.nextToken();
         this.acceptIdentifier("NUM");
         SQLExpr num = this.exprParser.expr();
         stmt.setPartitionNum(num);
      }

      return stmt;
   }

   public SQLStatement parseCreateUser() {
      this.accept(Token.CREATE);
      this.accept(Token.USER);
      SQLCreateUserStatement stmt = new SQLCreateUserStatement();
      stmt.setUser(this.exprParser.name());
      this.acceptIdentifier("IDENTIFIED");
      this.accept(Token.BY);
      stmt.setPassword(this.exprParser.primary());
      return stmt;
   }

   public SQLCreateFunctionStatement parseCreateFunction() {
      throw new ParserException("TODO " + this.lexer.token);
   }

   public SQLStatement parseCreateMaterializedView() {
      this.accept(Token.CREATE);
      this.acceptIdentifier("MATERIALIZED");
      this.accept(Token.VIEW);
      SQLCreateMaterializedViewStatement stmt = new SQLCreateMaterializedViewStatement();
      stmt.setName(this.exprParser.name());
      if (this.dbType == DbType.mysql) {
         stmt.setDbType(DbType.mysql);
         if (this.lexer.token == Token.LPAREN) {
            this.lexer.nextToken();

            while(true) {
               Token token = this.lexer.token;
               if (this.lexer.identifierEquals(FnvHash.Constants.CLUSTERED)) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.KEY) {
                     MySqlKey clsKey = new MySqlKey();
                     this.exprParser.parseIndex(clsKey.getIndexDefinition());
                     clsKey.setIndexType("CLUSTERED");
                     clsKey.setParent(stmt);
                     stmt.getTableElementList().add(clsKey);
                     if (this.lexer.token() == Token.COMMA) {
                        this.lexer.nextToken();
                        continue;
                     }
                  } else if (this.lexer.token() == Token.INDEX) {
                     MySqlTableIndex idx = new MySqlTableIndex();
                     this.exprParser.parseIndex(idx.getIndexDefinition());
                     idx.setIndexType("CLUSTERED");
                     idx.setParent(stmt);
                     stmt.getTableElementList().add(idx);
                     if (this.lexer.token() == Token.RPAREN) {
                        break;
                     }

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

               if (token == Token.IDENTIFIER) {
                  SQLColumnDefinition column = this.exprParser.parseColumn(stmt);
                  stmt.getTableElementList().add(column);
               } else if (token != Token.PRIMARY && token != Token.UNIQUE && token != Token.CHECK && token != Token.CONSTRAINT && token != Token.FOREIGN) {
                  if (this.lexer.token() == Token.INDEX) {
                     MySqlTableIndex idx = new MySqlTableIndex();
                     this.exprParser.parseIndex(idx.getIndexDefinition());
                     idx.setParent(stmt);
                     stmt.getTableElementList().add(idx);
                  } else if (this.lexer.token() == Token.KEY) {
                     Lexer.SavePoint savePoint = this.lexer.mark();
                     this.lexer.nextToken();
                     boolean isColumn = false;
                     if (this.lexer.identifierEquals(FnvHash.Constants.VARCHAR)) {
                        isColumn = true;
                     }

                     this.lexer.reset(savePoint);
                     if (isColumn) {
                        stmt.getTableElementList().add(this.exprParser.parseColumn());
                     } else {
                        SQLName name = null;
                        if (this.lexer.token() == Token.IDENTIFIER) {
                           name = this.exprParser.name();
                        }

                        MySqlKey key = new MySqlKey();
                        this.exprParser.parseIndex(key.getIndexDefinition());
                        if (name != null) {
                           key.setName(name);
                        }

                        key.setParent(stmt);
                        stmt.getTableElementList().add(key);
                     }
                     continue;
                  }
               } else {
                  SQLConstraint constraint = this.exprParser.parseConstaint();
                  constraint.setParent(stmt);
                  stmt.getTableElementList().add((SQLTableElement)constraint);
               }

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

               this.lexer.nextToken();
            }

            this.accept(Token.RPAREN);
         }

         label231:
         while(true) {
            while(!this.lexer.identifierEquals(FnvHash.Constants.DISTRIBUTED)) {
               if (this.lexer.identifierEquals("INDEX_ALL")) {
                  this.lexer.nextToken();
                  this.accept(Token.EQ);
                  if (this.lexer.token() == Token.LITERAL_CHARS) {
                     if ("Y".equalsIgnoreCase(this.lexer.stringVal())) {
                        this.lexer.nextToken();
                        stmt.addOption("INDEX_ALL", new SQLCharExpr("Y"));
                     } else {
                        if (!"N".equalsIgnoreCase(this.lexer.stringVal())) {
                           throw new ParserException("INDEX_ALL accept parameter ['Y' or 'N'] only.");
                        }

                        this.lexer.nextToken();
                        stmt.addOption("INDEX_ALL", new SQLCharExpr("N"));
                     }
                  }
               } else if (this.lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.EQ) {
                     this.lexer.nextToken();
                  }

                  SQLExpr expr = this.exprParser.expr();
                  stmt.addOption("ENGINE", expr);
               } else if (this.lexer.token == Token.PARTITION) {
                  SQLPartitionBy partitionBy = this.exprParser.parsePartitionBy();
                  stmt.setPartitionBy(partitionBy);
               } else {
                  if (this.lexer.token() != Token.COMMENT) {
                     break label231;
                  }

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

                  stmt.setComment(this.exprParser.expr());
               }
            }

            this.lexer.nextToken();
            this.accept(Token.BY);
            if (this.lexer.identifierEquals(FnvHash.Constants.HASH)) {
               this.lexer.nextToken();
               this.accept(Token.LPAREN);

               while(true) {
                  SQLName name = this.exprParser.name();
                  stmt.getDistributedBy().add(name);
                  if (this.lexer.token() != Token.COMMA) {
                     this.accept(Token.RPAREN);
                     stmt.setDistributedByType(new SQLIdentifierExpr("HASH"));
                     break;
                  }

                  this.lexer.nextToken();
               }
            } else if (this.lexer.identifierEquals(FnvHash.Constants.BROADCAST)) {
               this.lexer.nextToken();
               stmt.setDistributedByType(new SQLIdentifierExpr("BROADCAST"));
            }
         }
      } else if (this.lexer.token == Token.PARTITION) {
         SQLPartitionBy partitionBy = this.exprParser.parsePartitionBy();
         stmt.setPartitionBy(partitionBy);
      }

      label180:
      while(true) {
         if (this.exprParser instanceof OracleExprParser) {
            ((OracleExprParser)this.exprParser).parseSegmentAttributes(stmt);
         }

         if (this.lexer.identifierEquals("REFRESH")) {
            this.lexer.nextToken();
            boolean refresh = false;

            while(true) {
               while(!this.lexer.identifierEquals("FAST")) {
                  if (this.lexer.identifierEquals("COMPLETE")) {
                     this.lexer.nextToken();
                     stmt.setRefreshComplete(true);
                     refresh = true;
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.FORCE)) {
                     this.lexer.nextToken();
                     stmt.setRefreshForce(true);
                     refresh = true;
                  } else if (this.lexer.token == Token.ON) {
                     this.lexer.nextToken();
                     if (this.lexer.token != Token.COMMIT && !this.lexer.identifierEquals(FnvHash.Constants.COMMIT)) {
                        if (this.lexer.identifierEquals(FnvHash.Constants.OVERWRITE)) {
                           this.lexer.nextToken();
                           stmt.setRefreshOnOverWrite(true);
                           refresh = true;
                        } else {
                           this.acceptIdentifier("DEMAND");
                           stmt.setRefreshOnDemand(true);
                           refresh = true;
                        }
                     } else {
                        this.lexer.nextToken();
                        stmt.setRefreshOnCommit(true);
                        refresh = true;
                     }
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.START)) {
                     this.lexer.nextToken();
                     this.accept(Token.WITH);
                     SQLExpr startWith = this.exprParser.expr();
                     stmt.setStartWith(startWith);
                     stmt.setRefreshStartWith(true);
                     refresh = true;
                  } else {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.NEXT)) {
                        if (!refresh) {
                           throw new ParserException("refresh clause is empty. " + this.lexer.info());
                        }
                        continue label180;
                     }

                     this.lexer.nextToken();
                     SQLExpr next = this.exprParser.expr();
                     stmt.setNext(next);
                     stmt.setRefreshNext(true);
                     refresh = true;
                  }
               }

               this.lexer.nextToken();
               stmt.setRefreshFast(true);
               refresh = true;
            }
         } else if (this.lexer.identifierEquals("BUILD")) {
            this.lexer.nextToken();
            if (!this.lexer.identifierEquals("IMMEDIATE") && this.lexer.token != Token.IMMEDIATE) {
               this.accept(Token.DEFERRED);
               stmt.setBuildDeferred(true);
            } else {
               this.lexer.nextToken();
               stmt.setBuildImmediate(true);
            }
         } else if (this.lexer.identifierEquals("PARALLEL")) {
            this.lexer.nextToken();
            stmt.setParallel(true);
            if (this.lexer.token == Token.LITERAL_INT) {
               stmt.setParallelValue(this.lexer.integerValue().intValue());
               this.lexer.nextToken();
            }
         } else if (!this.lexer.identifierEquals(FnvHash.Constants.NOCACHE) && this.lexer.token != Token.NOCACHE) {
            if (this.lexer.identifierEquals(FnvHash.Constants.NOPARALLEL)) {
               this.lexer.nextToken();
               stmt.setParallel(false);
            } else {
               if (this.lexer.token != Token.WITH) {
                  Boolean enableQueryRewrite = null;
                  if (this.lexer.token == Token.ENABLE) {
                     this.lexer.nextToken();
                     enableQueryRewrite = true;
                  }

                  if (this.lexer.token == Token.DISABLE) {
                     this.lexer.nextToken();
                     enableQueryRewrite = false;
                  }

                  if (enableQueryRewrite != null) {
                     this.acceptIdentifier("QUERY");
                     this.acceptIdentifier("REWRITE");
                     stmt.setEnableQueryRewrite(enableQueryRewrite);
                  }

                  this.accept(Token.AS);
                  SQLSelect select = this.createSQLSelectParser().select();
                  stmt.setQuery(select);
                  return stmt;
               }

               this.lexer.nextToken();
               this.acceptIdentifier("ROWID");
               stmt.setWithRowId(true);
            }
         } else {
            this.lexer.nextToken();
            stmt.setCache(false);
         }
      }
   }

   public SQLStatement parseCreateDbLink() {
      throw new ParserException("TODO " + this.lexer.token);
   }

   public SQLStatement parseCreateSynonym() {
      throw new ParserException("TODO " + this.lexer.token);
   }

   public SQLStatement parseCreateExternalCatalog() {
      MySqlCreateExternalCatalogStatement stmt = new MySqlCreateExternalCatalogStatement();
      if (this.lexer.token == Token.CREATE) {
         this.lexer.nextToken();
      }

      this.acceptIdentifier("EXTERNAL");
      this.acceptIdentifier("CATALOG");
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.NOT);
         this.accept(Token.EXISTS);
         stmt.setIfNotExists(true);
      }

      SQLName name = this.exprParser.name();
      stmt.setName(name);
      this.acceptIdentifier("PROPERTIES");
      this.accept(Token.LPAREN);

      do {
         SQLName key = this.exprParser.name();
         this.accept(Token.EQ);
         SQLName value = this.exprParser.name();
         stmt.getProperties().put(key, value);
      } while(this.lexer.token != Token.RPAREN);

      this.accept(Token.RPAREN);
      if (this.lexer.token() == Token.COMMENT) {
         this.lexer.nextToken();
         SQLName comment = this.exprParser.name();
         stmt.setComment(comment);
      }

      return stmt;
   }

   public SQLStatement parseCreateTrigger() {
      SQLCreateTriggerStatement stmt = new SQLCreateTriggerStatement(this.getDbType());
      if (this.lexer.token == Token.CREATE) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.OR) {
            this.lexer.nextToken();
            this.accept(Token.REPLACE);
            stmt.setOrReplace(true);
         }
      }

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

      this.accept(Token.TRIGGER);
      stmt.setName(this.exprParser.name());
      if (this.lexer.identifierEquals(FnvHash.Constants.BEFORE)) {
         stmt.setTriggerType(SQLCreateTriggerStatement.TriggerType.BEFORE);
         this.lexer.nextToken();
      } else if (this.lexer.identifierEquals(FnvHash.Constants.AFTER)) {
         stmt.setTriggerType(SQLCreateTriggerStatement.TriggerType.AFTER);
         this.lexer.nextToken();
      } else if (this.lexer.identifierEquals(FnvHash.Constants.INSTEAD)) {
         this.lexer.nextToken();
         this.accept(Token.OF);
         stmt.setTriggerType(SQLCreateTriggerStatement.TriggerType.INSTEAD_OF);
      }

      while(true) {
         if (this.lexer.token == Token.INSERT) {
            this.lexer.nextToken();
            stmt.setInsert(true);
         } else if (this.lexer.token == Token.UPDATE) {
            this.lexer.nextToken();
            stmt.setUpdate(true);
            if (this.lexer.token == Token.OF) {
               this.lexer.nextToken();
               this.exprParser.names(stmt.getUpdateOfColumns(), stmt);
            }
         } else if (this.lexer.token == Token.DELETE) {
            this.lexer.nextToken();
            stmt.setDelete(true);
         }

         if (this.lexer.token != Token.COMMA && this.lexer.token != Token.OR) {
            this.accept(Token.ON);
            stmt.setOn(this.exprParser.name());
            if (this.lexer.token == Token.FOR) {
               this.lexer.nextToken();
               this.acceptIdentifier("EACH");
               this.accept(Token.ROW);
               stmt.setForEachRow(true);
               if (this.lexer.token() == Token.FOLLOWS || this.lexer.token() == Token.PRECEDES) {
                  SQLName order = new SQLIdentifierExpr(this.lexer.stringVal());
                  stmt.setTriggerOrder(order);
                  this.lexer.nextToken();
                  if (this.lexer.token == Token.IDENTIFIER) {
                     SQLName name = this.exprParser.name();
                     stmt.setOtherTriggerName(name);
                  }
               }
            }

            if (this.lexer.token == Token.WHEN) {
               this.lexer.nextToken();
               SQLExpr condition = this.exprParser.expr();
               stmt.setWhen(condition);
            }

            List<SQLStatement> body = this.parseStatementList();
            if (body != null && !body.isEmpty()) {
               stmt.setBody((SQLStatement)body.get(0));
               return stmt;
            }

            throw new ParserException("syntax error");
         }

         this.lexer.nextToken();
      }
   }

   public SQLStatement parseBlock() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   public SQLStatement parseCreateSchema() {
      throw new ParserException("TODO " + this.lexer.info());
   }

   public SQLStatement parseCreateDatabase() {
      SQLCreateDatabaseStatement stmt = new SQLCreateDatabaseStatement(this.dbType);
      if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
         stmt.addBeforeComment(this.lexer.readAndResetComments());
      }

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

      if (this.lexer.token == Token.SCHEMA && this.dbType == DbType.hive) {
         this.lexer.nextToken();
      } else {
         this.accept(Token.DATABASE);
      }

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

      stmt.setName(this.exprParser.name());
      if (this.lexer.token == Token.COMMENT) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.EQ) {
            this.lexer.nextToken();
         }

         SQLExpr comment = this.exprParser.expr();
         stmt.setComment(comment);
      }

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

      if (this.lexer.token == Token.WITH) {
         this.lexer.nextToken();
         if (!this.lexer.identifierEquals(FnvHash.Constants.DBPROPERTIES)) {
            throw new ParserException("TODO " + this.lexer.info());
         }

         this.lexer.nextToken();
         this.accept(Token.LPAREN);

         while(true) {
            SQLAssignItem assignItem = this.exprParser.parseAssignItem();
            assignItem.setParent(stmt);
            stmt.getDbProperties().add(assignItem);
            if (this.lexer.token != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

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

         SQLExpr comment = this.exprParser.expr();
         stmt.setComment(comment);
      }

      return stmt;
   }

   public SQLCreateProcedureStatement parseCreateProcedure() {
      throw new ParserException("TODO " + this.lexer.token);
   }

   public SQLStatement parseCreateSequence(boolean acceptCreate) {
      if (acceptCreate) {
         this.accept(Token.CREATE);
      }

      SQLCreateSequenceStatement stmt = new SQLCreateSequenceStatement();
      if (this.lexer.token == Token.GROUP) {
         this.lexer.nextToken();
         stmt.setGroup(true);
      } else if (this.lexer.identifierEquals(FnvHash.Constants.SIMPLE)) {
         this.lexer.nextToken();
         stmt.setSimple(true);
         if (this.lexer.token == Token.WITH) {
            this.lexer.nextToken();
            this.accept(Token.CACHE);
            stmt.setWithCache(true);
         }
      } else if (this.lexer.identifierEquals(FnvHash.Constants.TIME)) {
         this.lexer.nextToken();
         stmt.setTime(true);
      }

      this.accept(Token.SEQUENCE);
      stmt.setDbType(this.dbType);
      stmt.setName(this.exprParser.name());

      while(true) {
         while(this.lexer.token() == Token.START || this.lexer.identifierEquals(FnvHash.Constants.START)) {
            this.lexer.nextToken();
            this.accept(Token.WITH);
            stmt.setStartWith(this.exprParser.expr());
         }

         if (this.lexer.identifierEquals(FnvHash.Constants.INCREMENT)) {
            this.lexer.nextToken();
            this.accept(Token.BY);
            stmt.setIncrementBy(this.exprParser.expr());
         } else if (this.lexer.token() != Token.CACHE && !this.lexer.identifierEquals(FnvHash.Constants.CACHE)) {
            if (this.lexer.token == Token.WITH) {
               this.lexer.nextToken();
               this.accept(Token.CACHE);
               stmt.setCache(true);
            } else if (this.lexer.token() != Token.NOCACHE && !this.lexer.identifierEquals(FnvHash.Constants.NOCACHE)) {
               if (this.lexer.token() == Token.ORDER) {
                  this.lexer.nextToken();
                  stmt.setOrder(Boolean.TRUE);
               } else if (this.lexer.identifierEquals("NOORDER")) {
                  this.lexer.nextToken();
                  stmt.setOrder(Boolean.FALSE);
               } else if (this.lexer.identifierEquals("CYCLE")) {
                  this.lexer.nextToken();
                  stmt.setCycle(Boolean.TRUE);
               } else if (this.lexer.identifierEquals(FnvHash.Constants.NOCYCLE)) {
                  this.lexer.nextToken();
                  stmt.setCycle(Boolean.FALSE);
               } else if (this.lexer.identifierEquals("MINVALUE")) {
                  this.lexer.nextToken();
                  stmt.setMinValue(this.exprParser.expr());
               } else if (this.lexer.identifierEquals("MAXVALUE")) {
                  this.lexer.nextToken();
                  stmt.setMaxValue(this.exprParser.expr());
               } else if (this.lexer.identifierEquals("NOMAXVALUE")) {
                  this.lexer.nextToken();
                  stmt.setNoMaxValue(true);
               } else if (this.lexer.identifierEquals("NOMINVALUE")) {
                  this.lexer.nextToken();
                  stmt.setNoMinValue(true);
               } else if (this.lexer.identifierEquals("GLOBAL")) {
                  this.lexer.nextToken();
                  stmt.setGlobal(true);
               } else {
                  if (!this.lexer.identifierEquals("LOCAL")) {
                     if (this.lexer.identifierEquals(FnvHash.Constants.UNIT)) {
                        this.lexer.nextToken();
                        if (this.lexer.identifierEquals(FnvHash.Constants.COUNT)) {
                           this.lexer.nextToken();
                           SQLExpr unitCount = this.exprParser.primary();
                           stmt.setUnitCount(unitCount);
                        }

                        if (this.lexer.token == Token.INDEX) {
                           this.lexer.nextToken();
                           SQLExpr unitIndex = this.exprParser.primary();
                           stmt.setUnitIndex(unitIndex);
                        }

                        if (this.lexer.hash_lower() == FnvHash.Constants.STEP) {
                           this.lexer.nextToken();
                           SQLExpr step = this.exprParser.primary();
                           stmt.setStep(step);
                        }
                     }

                     return stmt;
                  }

                  this.lexer.nextToken();
                  stmt.setLocal(true);
               }
            } else {
               this.lexer.nextToken();
               stmt.setCache(Boolean.FALSE);
            }
         } else {
            this.lexer.nextToken();
            stmt.setCache(Boolean.TRUE);
            if (this.lexer.token() == Token.LITERAL_INT) {
               stmt.setCacheValue(this.exprParser.primary());
            }
         }
      }
   }

   public SQLCreateIndexStatement parseCreateIndex(boolean acceptCreate) {
      if (acceptCreate) {
         this.accept(Token.CREATE);
      }

      SQLCreateIndexStatement stmt = new SQLCreateIndexStatement(this.getDbType());
      if (this.lexer.token == Token.UNIQUE) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("CLUSTERED")) {
            this.lexer.nextToken();
            stmt.setType("UNIQUE CLUSTERED");
         } else if (this.lexer.identifierEquals("NONCLUSTERED")) {
            stmt.setType("UNIQUE NONCLUSTERED");
            this.lexer.nextToken();
         } else {
            stmt.setType("UNIQUE");
         }
      } else if (this.lexer.token() == Token.FULLTEXT) {
         stmt.setType("FULLTEXT");
         this.lexer.nextToken();
      } else if (this.lexer.identifierEquals("NONCLUSTERED")) {
         stmt.setType("NONCLUSTERED");
         this.lexer.nextToken();
      }

      this.accept(Token.INDEX);
      stmt.setName(this.exprParser.name());
      this.accept(Token.ON);
      stmt.setTable(this.exprParser.name());
      this.accept(Token.LPAREN);

      while(true) {
         SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
         item.setParent(stmt);
         stmt.addItem(item);
         if (this.lexer.token != Token.COMMA) {
            this.accept(Token.RPAREN);
            return stmt;
         }

         this.lexer.nextToken();
      }
   }

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

   public SQLStatement parseSelect() {
      SQLSelectParser selectParser = this.createSQLSelectParser();
      SQLSelect select = selectParser.select();
      return new SQLSelectStatement(select, this.getDbType());
   }

   public SQLSelectParser createSQLSelectParser() {
      return new SQLSelectParser(this.exprParser, this.selectListCache);
   }

   public SQLSelectParser createSQLSelectParser(SQLExprParser exprParser) {
      return new SQLSelectParser(exprParser);
   }

   public SQLUpdateStatement parseUpdateStatement() {
      SQLUpdateStatement udpateStatement = this.createUpdateStatement();
      if (this.lexer.token == Token.UPDATE) {
         this.lexer.nextToken();
         SQLTableSource tableSource = this.exprParser.createSelectParser().parseTableSource();
         udpateStatement.setTableSource(tableSource);
      }

      this.parseUpdateSet(udpateStatement);
      if (this.lexer.token == Token.WHERE) {
         this.lexer.nextToken();
         udpateStatement.setWhere(this.exprParser.expr());
      }

      return udpateStatement;
   }

   protected void parseUpdateSet(SQLUpdateStatement update) {
      this.accept(Token.SET);

      while(true) {
         SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
         update.addItem(item);
         if (this.lexer.token != Token.COMMA) {
            return;
         }

         this.lexer.nextToken();
      }
   }

   protected SQLUpdateStatement createUpdateStatement() {
      return new SQLUpdateStatement(this.getDbType());
   }

   public SQLDeleteStatement parseDeleteStatement() {
      SQLDeleteStatement deleteStatement = new SQLDeleteStatement(this.getDbType());
      if (this.lexer.token == Token.DELETE) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.FROM) {
            this.lexer.nextToken();
         }

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

         SQLName tableName = this.exprParser.name();
         deleteStatement.setTableName(tableName);
         if (this.lexer.token == Token.FROM) {
            this.lexer.nextToken();
            SQLTableSource tableSource = this.createSQLSelectParser().parseTableSource();
            deleteStatement.setFrom(tableSource);
         }
      }

      if (this.lexer.token == Token.WHERE) {
         this.lexer.nextToken();
         SQLExpr where = this.exprParser.expr();
         deleteStatement.setWhere(where);
      }

      return deleteStatement;
   }

   public SQLCreateTableStatement parseCreateTable() {
      SQLCreateTableParser parser = new SQLCreateTableParser(this.exprParser);
      return parser.parseCreateTable();
   }

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

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

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

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

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

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

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

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

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

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

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

                  column.setComment(comment);
               }

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

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

            this.lexer.nextToken();
         }
      }

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

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

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

      return createView;
   }

   public SQLCommentStatement parseComment() {
      this.accept(Token.COMMENT);
      SQLCommentStatement stmt = new SQLCommentStatement();
      this.accept(Token.ON);
      if (this.lexer.token == Token.TABLE) {
         stmt.setType(SQLCommentStatement.Type.TABLE);
         this.lexer.nextToken();
      } else if (this.lexer.token == Token.COLUMN) {
         stmt.setType(SQLCommentStatement.Type.COLUMN);
         this.lexer.nextToken();
      } else if (this.lexer.token == Token.VIEW) {
         stmt.setType(SQLCommentStatement.Type.VIEW);
         this.lexer.nextToken();
      }

      stmt.setOn(this.exprParser.name());
      this.accept(Token.IS);
      stmt.setComment(this.exprParser.expr());
      return stmt;
   }

   protected SQLAlterTableAddColumn parseAlterTableAddColumn() {
      boolean odps = DbType.odps == this.dbType || DbType.hive == this.dbType;
      if (odps) {
         this.acceptIdentifier("COLUMNS");
         this.accept(Token.LPAREN);
      }

      SQLAlterTableAddColumn item = new SQLAlterTableAddColumn();

      do {
         SQLColumnDefinition columnDef = this.exprParser.parseColumn();
         item.addColumn(columnDef);
         if (this.lexer.token == Token.WITH) {
            Lexer.SavePoint mark = this.lexer.mark();
            this.lexer.nextToken();
            if (this.lexer.token == Token.DEFAULT) {
               this.lexer.nextToken();
               SQLExpr defaultExpr = this.exprParser.expr();
               columnDef.setDefaultExpr(defaultExpr);
            } else {
               this.lexer.reset(mark);
            }
         }

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

         this.lexer.nextToken();
      } while(!this.lexer.identifierEquals("ADD"));

      if (odps) {
         this.accept(Token.RPAREN);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.RESTRICT)) {
         this.lexer.nextToken();
         item.setRestrict(true);
      } else if (this.lexer.token() != Token.CASCADE && !this.lexer.identifierEquals(FnvHash.Constants.CASCADE)) {
         item.setCascade(false);
      } else {
         this.lexer.nextToken();
         item.setCascade(true);
      }

      return item;
   }

   protected SQLAlterTableReplaceColumn parseAlterTableReplaceColumn() {
      this.accept(Token.REPLACE);
      this.acceptIdentifier("COLUMNS");
      SQLAlterTableReplaceColumn item = new SQLAlterTableReplaceColumn();
      this.accept(Token.LPAREN);

      do {
         SQLColumnDefinition columnDef = this.exprParser.parseColumn();
         item.addColumn(columnDef);
         if (this.lexer.token != Token.COMMA) {
            break;
         }

         this.lexer.nextToken();
      } while(!this.lexer.identifierEquals(FnvHash.Constants.ADD));

      this.accept(Token.RPAREN);
      return item;
   }

   public SQLStatement parseStatement() {
      if (this.lexer.token == Token.SELECT) {
         return this.parseSelect();
      } else if (this.lexer.token == Token.INSERT) {
         return this.parseInsert();
      } else if (this.lexer.token == Token.UPDATE) {
         return this.parseUpdateStatement();
      } else if (this.lexer.token == Token.DELETE) {
         return this.parseDeleteStatement();
      } else {
         List<SQLStatement> list = new ArrayList(1);
         this.parseStatementList(list, 1, (SQLObject)null);
         return (SQLStatement)list.get(0);
      }
   }

   public SQLStatement parseStatement(boolean tryBest) {
      List<SQLStatement> list = new ArrayList();
      this.parseStatementList(list, 1, (SQLObject)null);
      if (tryBest && this.lexer.token != Token.EOF) {
         throw new ParserException("sql syntax error, no terminated. " + this.lexer.token);
      } else {
         return (SQLStatement)list.get(0);
      }
   }

   public SQLExplainStatement parseExplain() {
      this.accept(Token.EXPLAIN);
      if (this.lexer.identifierEquals("PLAN")) {
         this.lexer.nextToken();
      }

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

      SQLExplainStatement explain = new SQLExplainStatement(this.dbType);
      if (this.lexer.token == Token.ANALYZE || this.lexer.identifierEquals(FnvHash.Constants.ANALYZE)) {
         this.lexer.nextToken();
         explain.setType("ANALYZE");
      }

      if (this.lexer.token == Token.HINT) {
         explain.setHints(this.exprParser.parseHints());
      }

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

      if (this.lexer.identifierEquals(FnvHash.Constants.DEPENDENCY)) {
         this.lexer.nextToken();
         explain.setDependency(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.AUTHORIZATION)) {
         this.lexer.nextToken();
         explain.setAuthorization(true);
      }

      if (DbType.mysql == this.dbType && (this.lexer.identifierEquals("FORMAT") || this.lexer.identifierEquals("PARTITIONS"))) {
         explain.setType(this.lexer.stringVal);
         this.lexer.nextToken();
      }

      if ((DbType.mysql == this.dbType || DbType.ads == this.dbType || DbType.presto == this.dbType) && this.lexer.token == Token.LPAREN) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("FORMAT")) {
            this.lexer.nextToken();
            (new StringBuilder()).append("FORMAT ").append(this.lexer.stringVal).toString();
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("TYPE")) {
            this.lexer.nextToken();
            (new StringBuilder()).append("TYPE ").append(this.lexer.stringVal).toString();
            this.lexer.nextToken();
         }

         this.accept(Token.RPAREN);
      }

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

   protected SQLAlterTableAddClusteringKey parseAlterTableAddClusteringKey() {
      this.lexer.nextToken();
      SQLAlterTableAddClusteringKey item = new SQLAlterTableAddClusteringKey();
      this.accept(Token.KEY);
      item.setName(this.exprParser.name());
      this.accept(Token.LPAREN);

      while(true) {
         item.getColumns().add(this.exprParser.name());
         if (this.lexer.token != Token.COMMA) {
            this.accept(Token.RPAREN);
            return item;
         }

         this.lexer.nextToken();
      }
   }

   public SQLOpenStatement parseOpen() {
      SQLOpenStatement stmt = new SQLOpenStatement();
      this.accept(Token.OPEN);
      SQLName cursorName;
      if (this.lexer.token == Token.QUES) {
         this.lexer.nextToken();
         cursorName = new SQLIdentifierExpr("?");
      } else {
         cursorName = this.exprParser.name();
      }

      stmt.setCursorName(cursorName);
      if (this.lexer.token == Token.LPAREN) {
         this.lexer.nextToken();
         this.exprParser.names(stmt.getColumns(), stmt);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.token == Token.FOR) {
         this.lexer.nextToken();
         if (this.lexer.token == Token.SELECT) {
            SQLSelectParser selectParser = this.createSQLSelectParser();
            SQLSelect select = selectParser.select();
            SQLQueryExpr queryExpr = new SQLQueryExpr(select);
            stmt.setFor(queryExpr);
         } else if (this.lexer.token == Token.LITERAL_CHARS) {
            String chars = this.lexer.stringVal;
            SQLExprParser exprParser = SQLParserUtils.createExprParser(chars, this.dbType);
            SQLSelectParser selectParser = this.createSQLSelectParser(exprParser);
            SQLSelect select = selectParser.select();
            SQLQueryExpr queryExpr = new SQLQueryExpr(select);
            stmt.setFor(queryExpr);
            this.lexer.nextToken();
         } else {
            if (this.lexer.token != Token.QUES) {
               throw new ParserException("TODO " + this.lexer.info());
            }

            this.lexer.nextToken();
            stmt.setFor(new SQLVariantRefExpr("?"));
         }
      }

      if (this.lexer.token == Token.USING) {
         this.lexer.nextToken();
         this.exprParser.exprList(stmt.getUsing(), stmt);
      }

      this.accept(Token.SEMI);
      stmt.setAfterSemi(true);
      return stmt;
   }

   public SQLFetchStatement parseFetch() {
      this.accept(Token.FETCH);
      if (this.lexer.token() == Token.NEXT) {
         this.lexer.nextToken();
      }

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

      SQLFetchStatement stmt = new SQLFetchStatement();
      stmt.setCursorName(this.exprParser.name());
      if (this.lexer.identifierEquals("BULK")) {
         this.lexer.nextToken();
         this.acceptIdentifier("COLLECT");
         stmt.setBulkCollect(true);
      }

      this.accept(Token.INTO);

      while(true) {
         stmt.getInto().add(this.exprParser.name());
         if (this.lexer.token != Token.COMMA) {
            if (this.lexer.token == Token.LIMIT) {
               SQLLimit limit = this.exprParser.parseLimit();
               stmt.setLimit(limit);
            }

            return stmt;
         }

         this.lexer.nextToken();
      }
   }

   public SQLStatement parseClose() {
      SQLCloseStatement stmt = new SQLCloseStatement();
      this.accept(Token.CLOSE);
      stmt.setCursorName(this.exprParser.name());
      this.accept(Token.SEMI);
      stmt.setAfterSemi(true);
      return stmt;
   }

   public boolean isParseCompleteValues() {
      return this.parseCompleteValues;
   }

   public void setParseCompleteValues(boolean parseCompleteValues) {
      this.parseCompleteValues = parseCompleteValues;
   }

   public int getParseValuesSize() {
      return this.parseValuesSize;
   }

   public void setParseValuesSize(int parseValuesSize) {
      this.parseValuesSize = parseValuesSize;
   }

   public SQLStatement parseMerge() {
      this.accept(Token.MERGE);
      SQLMergeStatement stmt = new SQLMergeStatement();
      stmt.setDbType(this.dbType);
      this.parseHints(stmt.getHints());
      this.accept(Token.INTO);
      if (this.lexer.token == Token.LPAREN) {
         this.lexer.nextToken();
         SQLSelect select = this.createSQLSelectParser().select();
         SQLSubqueryTableSource tableSource = new SQLSubqueryTableSource(select);
         stmt.setInto((SQLTableSource)tableSource);
         this.accept(Token.RPAREN);
      } else {
         stmt.setInto(this.exprParser.name());
      }

      stmt.getInto().setAlias(this.tableAlias());
      this.accept(Token.USING);
      SQLTableSource using = this.createSQLSelectParser().parseTableSource();
      stmt.setUsing(using);
      this.accept(Token.ON);
      stmt.setOn(this.exprParser.expr());

      do {
         boolean insertFlag = false;
         if (this.lexer.token == Token.WHEN) {
            this.lexer.nextToken();
            if (this.lexer.token != Token.MATCHED) {
               if (this.lexer.token == Token.NOT) {
                  this.lexer.nextToken();
                  insertFlag = true;
               }
            } else {
               SQLMergeStatement.MergeUpdateClause updateClause = new SQLMergeStatement.MergeUpdateClause();
               this.lexer.nextToken();
               if (this.lexer.token == Token.AND) {
                  this.lexer.nextToken();
                  SQLExpr where = this.exprParser.expr();
                  updateClause.setWhere(where);
               }

               this.accept(Token.THEN);
               this.accept(Token.UPDATE);
               this.accept(Token.SET);

               while(true) {
                  SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
                  updateClause.addItem(item);
                  item.setParent(updateClause);
                  if (this.lexer.token != Token.COMMA) {
                     if (this.lexer.token == Token.WHERE) {
                        this.lexer.nextToken();
                        updateClause.setWhere(this.exprParser.expr());
                     }

                     SQLExpr deleteWhere = null;
                     if (this.lexer.token == Token.WHEN) {
                        Lexer.SavePoint savePoint = this.lexer.mark();
                        this.lexer.nextToken();
                        if (this.lexer.token == Token.MATCHED) {
                           this.lexer.nextToken();
                           if (this.lexer.token == Token.AND) {
                              this.lexer.nextToken();
                              deleteWhere = this.exprParser.expr();
                           }

                           if (this.lexer.token == Token.THEN) {
                              this.lexer.nextToken();
                              if (this.lexer.token == Token.DELETE) {
                                 this.lexer.nextToken();
                                 updateClause.setDeleteWhere(deleteWhere);
                              } else {
                                 deleteWhere = null;
                              }
                           } else {
                              deleteWhere = null;
                           }

                           if (deleteWhere == null) {
                              this.lexer.reset(savePoint);
                           }
                        }
                     }

                     if (this.lexer.token == Token.DELETE) {
                        this.lexer.nextToken();
                        this.accept(Token.WHERE);
                        updateClause.setDeleteWhere(this.exprParser.expr());
                     }

                     stmt.setUpdateClause(updateClause);
                     break;
                  }

                  this.lexer.nextToken();
               }
            }
         }

         if (!insertFlag) {
            if (this.lexer.token == Token.WHEN) {
               this.lexer.nextToken();
            }

            if (this.lexer.token == Token.NOT) {
               this.lexer.nextToken();
               insertFlag = true;
            }
         }

         if (insertFlag) {
            SQLMergeStatement.MergeInsertClause insertClause = new SQLMergeStatement.MergeInsertClause();
            this.accept(Token.MATCHED);
            this.accept(Token.THEN);
            this.accept(Token.INSERT);
            if (this.lexer.token == Token.LPAREN) {
               this.accept(Token.LPAREN);
               this.exprParser.exprList(insertClause.getColumns(), insertClause);
               this.accept(Token.RPAREN);
            }

            this.accept(Token.VALUES);
            this.accept(Token.LPAREN);
            this.exprParser.exprList(insertClause.getValues(), insertClause);
            this.accept(Token.RPAREN);
            if (this.lexer.token == Token.WHERE) {
               this.lexer.nextToken();
               insertClause.setWhere(this.exprParser.expr());
            }

            stmt.setInsertClause(insertClause);
         }
      } while(this.lexer.token == Token.WHEN);

      SQLErrorLoggingClause errorClause = this.parseErrorLoggingClause();
      stmt.setErrorLoggingClause(errorClause);
      return stmt;
   }

   protected SQLErrorLoggingClause parseErrorLoggingClause() {
      if (this.lexer.identifierEquals("LOG")) {
         SQLErrorLoggingClause errorClause = new SQLErrorLoggingClause();
         this.lexer.nextToken();
         this.accept(Token.ERRORS);
         if (this.lexer.token == Token.INTO) {
            this.lexer.nextToken();
            errorClause.setInto(this.exprParser.name());
         }

         if (this.lexer.token == Token.LPAREN) {
            this.lexer.nextToken();
            errorClause.setSimpleExpression(this.exprParser.expr());
            this.accept(Token.RPAREN);
         }

         if (this.lexer.token == Token.REJECT) {
            this.lexer.nextToken();
            this.accept(Token.LIMIT);
            errorClause.setLimit(this.exprParser.expr());
         }

         return errorClause;
      } else {
         return null;
      }
   }

   public void parseHints(List<SQLHint> hints) {
      this.getExprParser().parseHints(hints);
   }

   public SQLStatement parseDescribe() {
      if (this.lexer.token != Token.DESC && !this.lexer.identifierEquals("DESCRIBE")) {
         throw new ParserException("expect DESC, actual " + this.lexer.token);
      } else {
         this.lexer.nextToken();
         SQLDescribeStatement stmt = new SQLDescribeStatement();
         stmt.setDbType(this.dbType);
         if (this.lexer.token == Token.DATABASE) {
            this.lexer.nextToken();
            stmt.setObjectType(SQLObjectType.DATABASE);
         } else if (this.lexer.token == Token.SCHEMA) {
            this.lexer.nextToken();
            stmt.setObjectType(SQLObjectType.SCHEMA);
         } else if (this.lexer.identifierEquals("ROLE")) {
            this.lexer.nextToken();
            stmt.setObjectType(SQLObjectType.ROLE);
         } else if (this.lexer.identifierEquals("PACKAGE")) {
            this.lexer.nextToken();
            stmt.setObjectType(SQLObjectType.PACKAGE);
         } else if (this.lexer.identifierEquals("INSTANCE")) {
            this.lexer.nextToken();
            stmt.setObjectType(SQLObjectType.INSTANCE);
         } else if (this.lexer.token == Token.TABLE) {
            this.lexer.nextToken();
            stmt.setObjectType(SQLObjectType.TABLE);
         }

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

         if (this.lexer.identifierEquals(FnvHash.Constants.FORMATTED)) {
            this.lexer.nextToken();
            stmt.setFormatted(true);
         }

         stmt.setObject(this.exprParser.name());
         if (this.lexer.token == Token.IDENTIFIER) {
            SQLName column = this.exprParser.name();
            stmt.setColumn(column);
         }

         if (this.lexer.token == Token.PARTITION) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);

            while(true) {
               stmt.getPartition().add(this.exprParser.expr());
               if (this.lexer.token == Token.COMMA) {
                  this.lexer.nextToken();
               } else if (this.lexer.token == Token.RPAREN) {
                  this.lexer.nextToken();
                  break;
               }
            }
         }

         if (this.lexer.token == Token.IDENTIFIER && stmt.getColumn() == null) {
            SQLName column = this.exprParser.name();
            stmt.setColumn(column);
         }

         return stmt;
      }
   }

   public SQLWithSubqueryClause parseWithQuery() {
      SQLWithSubqueryClause withQueryClause = new SQLWithSubqueryClause();
      if (this.lexer.hasComment() && this.lexer.isKeepComments()) {
         withQueryClause.addBeforeComment(this.lexer.readAndResetComments());
      }

      this.accept(Token.WITH);
      if (this.lexer.token == Token.RECURSIVE || this.lexer.identifierEquals("RECURSIVE")) {
         this.lexer.nextToken();
         withQueryClause.setRecursive(true);
      }

      while(true) {
         SQLWithSubqueryClause.Entry entry = new SQLWithSubqueryClause.Entry();
         entry.setParent(withQueryClause);
         String alias = this.lexer.stringVal();
         this.lexer.nextToken();
         entry.setAlias(alias);
         if (this.lexer.token == Token.LPAREN) {
            this.lexer.nextToken();
            this.exprParser.names(entry.getColumns());
            this.accept(Token.RPAREN);
         }

         this.accept(Token.AS);
         this.accept(Token.LPAREN);
         switch (this.lexer.token) {
            case WITH:
            case SELECT:
            case VALUES:
            case LPAREN:
            case FROM:
               entry.setSubQuery(this.createSQLSelectParser().select());
               break;
            case UPDATE:
               entry.setReturningStatement(this.parseUpdateStatement());
               break;
            case INSERT:
               entry.setReturningStatement(this.parseInsert());
               break;
            case DELETE:
               entry.setReturningStatement(this.parseDeleteStatement());
         }

         this.accept(Token.RPAREN);
         withQueryClause.addEntry(entry);
         if (this.lexer.token != Token.COMMA) {
            return withQueryClause;
         }

         this.lexer.nextToken();
      }
   }

   public SQLStatement parseWith() {
      SQLWithSubqueryClause with = this.parseWithQuery();
      if (this.lexer.token != Token.SELECT && this.lexer.token != Token.LPAREN) {
         if (this.lexer.token == Token.INSERT) {
            SQLInsertStatement insert = (SQLInsertStatement)this.parseInsert();
            insert.setWith(with);
            return insert;
         } else if (this.lexer.token == Token.FROM) {
            HiveMultiInsertStatement insert = (HiveMultiInsertStatement)this.parseInsert();
            insert.setWith(with);
            return insert;
         } else {
            throw new ParserException("TODO. " + this.lexer.info());
         }
      } else {
         SQLSelectParser selectParser = this.createSQLSelectParser();
         SQLSelect select = selectParser.select();
         select.setWithSubQuery(with);
         return new SQLSelectStatement(select, this.dbType);
      }
   }

   protected void parseValueClause(List<SQLInsertStatement.ValuesClause> valueClauseList, int columnSize, SQLObject parent) {
      this.parseValueClause(valueClauseList, (List)null, 0, parent);
   }

   protected void parseValueClauseNative(List<SQLInsertStatement.ValuesClause> valueClauseList, List<SQLColumnDefinition> columnDefinitionList, int columnSize, SQLObject parent) {
      TimeZone timeZone = this.lexer.getTimeZone();
      int i = 0;

      while(true) {
         int startPos = this.lexer.pos - 1;
         if (this.lexer.token != Token.LPAREN) {
            throw new ParserException("syntax error, expect ')', " + this.lexer.info());
         }

         if (this.lexer.ch == '\'') {
            this.lexer.bufPos = 0;
            if (this.dbType == DbType.mysql) {
               this.lexer.scanString2();
            } else {
               this.lexer.scanString();
            }
         } else if (this.lexer.ch == '0') {
            this.lexer.bufPos = 0;
            if (this.lexer.charAt(this.lexer.pos + 1) == 'x') {
               this.lexer.scanChar();
               this.lexer.scanChar();
               this.lexer.scanHexaDecimal();
            } else {
               this.lexer.scanNumber();
            }
         } else if (this.lexer.ch > '0' && this.lexer.ch <= '9') {
            this.lexer.bufPos = 0;
            this.lexer.scanNumber();
         } else if (this.lexer.ch == '-' && this.lexer.charAt(this.lexer.pos + 1) != '-') {
            this.lexer.scanNumber();
         } else {
            this.lexer.nextTokenValue();
         }

         SQLInsertStatement.ValuesClause values;
         if (this.lexer.token() == Token.RPAREN) {
            values = new SQLInsertStatement.ValuesClause(new ArrayList(0));
         } else {
            List valueExprList;
            if (columnSize > 0) {
               valueExprList = new ArrayList(columnSize);
            } else {
               valueExprList = new ArrayList();
            }

            values = new SQLInsertStatement.ValuesClause(valueExprList, parent);
            int funcExecCount = 0;
            int j = 0;

            while(true) {
               SQLExpr expr = null;
               Object value = null;
               SQLColumnDefinition columnDefinition = null;
               if (columnDefinitionList != null && j < columnDefinitionList.size()) {
                  columnDefinition = (SQLColumnDefinition)columnDefinitionList.get(j);
               }

               SQLDataType dataType = null;
               if (columnDefinition != null) {
                  dataType = columnDefinition.getDataType();
               }

               switch (this.lexer.token) {
                  case IDENTIFIER:
                     long hash = this.lexer.hash_lower();
                     if (hash == FnvHash.Constants.DATE) {
                        this.lexer.nextTokenValue();
                        String strVal = this.lexer.stringVal();
                        value = Date.valueOf(strVal);
                        this.lexer.nextTokenComma();
                     } else if (hash == FnvHash.Constants.TIMESTAMP && timeZone != null) {
                        this.lexer.nextTokenValue();
                        String strVal = this.lexer.stringVal();
                        value = new Timestamp(MySqlUtils.parseDate(strVal, timeZone).getTime());
                        this.lexer.nextTokenComma();
                     } else if (hash != FnvHash.Constants.CURDATE && hash != FnvHash.Constants.CUR_DATE && hash != FnvHash.Constants.CURRENT_DATE) {
                        if ((hash == FnvHash.Constants.SYSDATE || hash == FnvHash.Constants.NOW || hash == FnvHash.Constants.CURRENT_TIMESTAMP) && timeZone != null) {
                           this.lexer.nextTokenValue();
                           if (this.lexer.token == Token.LPAREN) {
                              this.lexer.nextToken();
                              this.accept(Token.RPAREN);
                           }

                           if (this.now == null) {
                              this.now = new Timestamp(System.currentTimeMillis());
                           }

                           value = this.now;
                           ++funcExecCount;
                        } else if (hash == FnvHash.Constants.UUID) {
                           this.lexer.nextTokenLParen();
                           this.accept(Token.LPAREN);
                           this.accept(Token.RPAREN);
                           value = UUID.randomUUID().toString();
                           ++funcExecCount;
                        } else {
                           value = null;
                           Lexer.SavePoint mark = this.lexer.mark();
                           expr = this.exprParser.expr();
                           if (expr instanceof SQLName) {
                              this.lexer.reset(mark);
                              this.lexer.info();
                              throw new ParserException("insert value error, token " + this.lexer.stringVal() + ", line " + this.lexer.posLine + ", column " + this.lexer.posColumn, this.lexer.posLine, this.lexer.posColumn);
                           }

                           expr.setParent(values);
                        }
                     } else {
                        this.lexer.nextTokenValue();
                        if (this.lexer.token == Token.LPAREN) {
                           this.lexer.nextToken();
                           this.accept(Token.RPAREN);
                        }

                        if (this.now == null) {
                           this.now = new Timestamp(System.currentTimeMillis());
                        }

                        if (this.currentDate == null) {
                           this.currentDate = new Date(this.now.getTime());
                        }

                        value = this.currentDate;
                        ++funcExecCount;
                     }
                     break;
                  case NOT:
                  case UNIQUE:
                  case VALUES:
                  case LPAREN:
                  case FROM:
                  default:
                     value = null;
                     expr = this.exprParser.expr();
                     expr.setParent(values);
                     break;
                  case LITERAL_INT:
                     Number integerValue = this.lexer.integerValue();
                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        SQLExpr var26 = new SQLIntegerExpr(integerValue, values);
                        expr = this.exprParser.exprRest(var26);
                        expr.setParent(values);
                        break;
                     }

                     value = integerValue;
                     break;
                  case LITERAL_CHARS:
                     String strVal = this.lexer.stringVal();
                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        SQLExpr var25 = new SQLCharExpr(strVal, values);
                        expr = this.exprParser.exprRest(var25);
                        expr.setParent(values);
                        break;
                     }

                     value = strVal;
                     break;
                  case LITERAL_NCHARS:
                     String strVal2 = this.lexer.stringVal();
                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        SQLExpr var24 = new SQLNCharExpr(strVal2, values);
                        expr = this.exprParser.exprRest(var24);
                        expr.setParent(values);
                        break;
                     }

                     value = strVal2;
                     break;
                  case LITERAL_FLOAT:
                     BigDecimal number = this.lexer.decimalValue();
                     if (dataType != null && dataType.nameHashCode64() == FnvHash.Constants.DECIMAL) {
                        int precision = 0;
                        int scale = 0;
                        List<SQLExpr> arguments = dataType.getArguments();
                        if (arguments.size() > 0) {
                           SQLExpr arg0 = (SQLExpr)arguments.get(0);
                           if (arg0 instanceof SQLIntegerExpr) {
                              precision = ((SQLIntegerExpr)arg0).getNumber().intValue();
                           }
                        }

                        if (arguments.size() > 1) {
                           SQLExpr arg0 = (SQLExpr)arguments.get(1);
                           if (arg0 instanceof SQLIntegerExpr) {
                              scale = ((SQLIntegerExpr)arg0).getNumber().intValue();
                           }
                        }

                        if (number instanceof BigDecimal) {
                           number = MySqlUtils.decimal(number, precision, scale);
                        }
                     }

                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        SQLExpr var23 = new SQLDecimalExpr(number);
                        expr = this.exprParser.exprRest(var23);
                        expr.setParent(values);
                        break;
                     }

                     value = number;
                     break;
                  case NULL:
                     this.lexer.nextTokenCommaValue();
                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        SQLExpr orginalString = new SQLNullExpr(parent);
                        expr = this.exprParser.exprRest(orginalString);
                        expr.setParent(values);
                     } else {
                        value = null;
                     }
               }

               if (expr != null) {
                  expr.setParent(values);
                  value = expr;
               }

               if (this.lexer.token == Token.COMMA) {
                  valueExprList.add(value);
                  if (this.lexer.ch == '\'') {
                     this.lexer.bufPos = 0;
                     if (this.dbType == DbType.mysql) {
                        this.lexer.scanString2();
                     } else {
                        this.lexer.scanString();
                     }
                  } else if (this.lexer.ch == '0') {
                     this.lexer.bufPos = 0;
                     if (this.lexer.charAt(this.lexer.pos + 1) == 'x') {
                        this.lexer.scanChar();
                        this.lexer.scanChar();
                        this.lexer.scanHexaDecimal();
                     } else {
                        this.lexer.scanNumber();
                     }
                  } else if (this.lexer.ch > '0' && this.lexer.ch <= '9') {
                     this.lexer.bufPos = 0;
                     this.lexer.scanNumber();
                  } else if (this.lexer.ch == '-' && this.lexer.charAt(this.lexer.pos + 1) != '-') {
                     this.lexer.bufPos = 0;
                     this.lexer.scanNumber();
                  } else {
                     this.lexer.nextTokenValue();
                  }
               } else {
                  if (this.lexer.token == Token.RPAREN) {
                     valueExprList.add(value);
                     break;
                  }

                  expr = this.exprParser.primaryRest(expr);
                  if (this.lexer.token != Token.COMMA && this.lexer.token() != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                  }

                  expr.setParent(values);
                  valueExprList.add(expr);
                  if (this.lexer.token != Token.COMMA) {
                     break;
                  }

                  this.lexer.nextTokenValue();
               }

               ++j;
            }

            if (funcExecCount == 0 && this.lexer.isEnabled(SQLParserFeature.KeepInsertValueClauseOriginalString)) {
               j = this.lexer.pos();
               String orginalString = this.lexer.subString(startPos, j - startPos);
               values.setOriginalString(orginalString);
            }
         }

         valueClauseList.add(values);
         if (this.lexer.token != Token.RPAREN) {
            throw new ParserException("syntax error. " + this.lexer.info());
         }

         if (!this.parseCompleteValues && valueClauseList.size() >= this.parseValuesSize) {
            this.lexer.skipToEOF();
            break;
         }

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

         this.lexer.nextTokenLParen();
         if (values != null) {
            columnSize = values.getValues().size();
         }

         ++i;
      }

   }

   public void parseValueClause(SQLInsertValueHandler valueHandler) throws SQLException {
      while(this.lexer.token == Token.LPAREN) {
         if (this.lexer.ch == '\'') {
            this.lexer.bufPos = 0;
            if (this.dbType == DbType.mysql) {
               this.lexer.scanString2();
            } else {
               this.lexer.scanString();
            }
         } else if (this.lexer.ch == '0') {
            this.lexer.bufPos = 0;
            if (this.lexer.charAt(this.lexer.pos + 1) == 'x') {
               this.lexer.scanChar();
               this.lexer.scanChar();
               this.lexer.scanHexaDecimal();
            } else {
               this.lexer.scanNumber();
            }
         } else if (this.lexer.ch > '0' && this.lexer.ch <= '9') {
            this.lexer.bufPos = 0;
            this.lexer.scanNumber();
         } else if (this.lexer.ch == '-' && this.lexer.charAt(this.lexer.pos + 1) != '-') {
            this.lexer.scanNumber();
         } else {
            this.lexer.nextTokenValue();
         }

         if (this.lexer.token() != Token.RPAREN) {
            Object row = valueHandler.newRow();
            int j = 0;

            while(true) {
               switch (this.lexer.token) {
                  case IDENTIFIER:
                     long hash = this.lexer.hash_lower();
                     if (hash == FnvHash.Constants.DATE) {
                        this.lexer.nextTokenValue();
                        String strVal = this.lexer.stringVal();
                        valueHandler.processDate(row, j, strVal);
                        this.lexer.nextTokenComma();
                     } else if (hash == FnvHash.Constants.TIMESTAMP) {
                        this.lexer.nextTokenValue();
                        String strVal = this.lexer.stringVal();
                        valueHandler.processTimestamp(row, j, strVal);
                        this.lexer.nextTokenComma();
                     } else if (hash == FnvHash.Constants.TIME) {
                        this.lexer.nextTokenValue();
                        String strVal = this.lexer.stringVal();
                        valueHandler.processTime(row, j, strVal);
                        this.lexer.nextTokenComma();
                     } else if (hash == FnvHash.Constants.DECIMAL) {
                        this.lexer.nextTokenValue();
                        String strVal = this.lexer.stringVal();
                        BigDecimal decimal = new BigDecimal(strVal);
                        valueHandler.processDecimal(row, j, decimal);
                        this.lexer.nextTokenComma();
                     } else if (hash != FnvHash.Constants.CURDATE && hash != FnvHash.Constants.CUR_DATE && hash != FnvHash.Constants.CURRENT_DATE && hash != FnvHash.Constants.SYSDATE) {
                        if (hash != FnvHash.Constants.NOW && hash != FnvHash.Constants.CURRENT_TIMESTAMP) {
                           if (hash == FnvHash.Constants.UUID) {
                              String funcName = this.lexer.stringVal();
                              this.lexer.nextTokenLParen();
                              if (this.lexer.token != Token.LPAREN) {
                                 throw new ParserException("insert value error, " + this.lexer.info());
                              }

                              this.lexer.nextToken();
                              this.accept(Token.RPAREN);
                              if (this.now == null) {
                                 this.now = new Timestamp(System.currentTimeMillis());
                              }

                              valueHandler.processFunction(row, j, funcName, hash);
                           } else if (hash != FnvHash.Constants.STR_TO_DATE && hash != FnvHash.Constants.DATE_PARSE) {
                              if (FnvHash.Constants.CLOTHES_FEATURE_EXTRACT_V1 != hash && FnvHash.Constants.CLOTHES_ATTRIBUTE_EXTRACT_V1 != hash && FnvHash.Constants.GENERIC_FEATURE_EXTRACT_V1 != hash && FnvHash.Constants.TEXT_FEATURE_EXTRACT_V1 != hash && FnvHash.Constants.FACE_FEATURE_EXTRACT_V1 != hash) {
                                 throw new ParserException("insert value error, " + this.lexer.info());
                              }

                              String funcName = this.lexer.stringVal();
                              this.lexer.nextTokenLParen();
                              if (Token.LPAREN != this.lexer.token) {
                                 throw new ParserException("insert value error, " + this.lexer.info());
                              }

                              this.lexer.nextTokenValue();
                              String urlVal = this.lexer.stringVal();
                              this.lexer.nextToken();
                              this.accept(Token.RPAREN);
                              valueHandler.processFunction(row, j, funcName, hash, urlVal);
                           } else {
                              String funcName = this.lexer.stringVal();
                              this.lexer.nextTokenLParen();
                              if (this.lexer.token != Token.LPAREN) {
                                 throw new ParserException("insert value error, " + this.lexer.info());
                              }

                              this.lexer.nextTokenValue();
                              String strVal = this.lexer.stringVal();
                              this.lexer.nextTokenComma();
                              this.accept(Token.COMMA);
                              String format = this.lexer.stringVal();
                              this.lexer.nextTokenValue();
                              this.accept(Token.RPAREN);
                              valueHandler.processFunction(row, j, funcName, hash, strVal, format);
                           }
                        } else {
                           this.lexer.nextTokenLParen();
                           if (this.lexer.token == Token.LPAREN) {
                              this.lexer.nextToken();
                              this.accept(Token.RPAREN);
                           }

                           if (this.now == null) {
                              this.now = new Timestamp(System.currentTimeMillis());
                           }

                           valueHandler.processTimestamp(row, j, (java.util.Date)this.now);
                        }
                     } else {
                        this.lexer.nextTokenLParen();
                        if (this.lexer.token == Token.LPAREN) {
                           this.lexer.nextToken();
                           this.accept(Token.RPAREN);
                        }

                        if (this.currentDate == null) {
                           this.currentDate = new Date(this.now.getTime());
                        }

                        valueHandler.processDate(row, j, (java.util.Date)this.currentDate);
                     }
                     break;
                  case NOT:
                  case UNIQUE:
                  case VALUES:
                  case LPAREN:
                  case FROM:
                  default:
                     throw new ParserException("insert value error, " + this.lexer.info());
                  case LITERAL_INT:
                     Number number = this.lexer.integerValue();
                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        throw new ParserException("insert value error, " + this.lexer.info());
                     }

                     valueHandler.processInteger(row, j, number);
                     break;
                  case LITERAL_CHARS:
                  case LITERAL_NCHARS:
                     String strVal = this.lexer.stringVal();
                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        throw new ParserException("insert value error, " + this.lexer.info());
                     }

                     valueHandler.processString(row, j, strVal);
                     break;
                  case LITERAL_FLOAT:
                     BigDecimal number2 = this.lexer.decimalValue();
                     if (this.lexer.ch == ',') {
                        this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                        this.lexer.token = Token.COMMA;
                     } else {
                        this.lexer.nextTokenCommaValue();
                     }

                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        throw new ParserException("insert value error, " + this.lexer.info());
                     }

                     valueHandler.processDecimal(row, j, number2);
                     break;
                  case NULL:
                     this.lexer.nextTokenCommaValue();
                     if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                        throw new ParserException("insert value error, " + this.lexer.info());
                     }

                     valueHandler.processNull(row, j);
                     break;
                  case TRUE:
                     valueHandler.processBoolean(row, j, true);
                     this.lexer.nextTokenComma();
                     break;
                  case FALSE:
                     valueHandler.processBoolean(row, j, false);
                     this.lexer.nextTokenComma();
               }

               if (this.lexer.token != Token.COMMA) {
                  if (this.lexer.token != Token.RPAREN) {
                     throw new ParserException("insert value error, " + this.lexer.info());
                  }

                  valueHandler.processRow(row);
                  break;
               }

               if (this.lexer.ch == '\'') {
                  this.lexer.bufPos = 0;
                  if (this.dbType == DbType.mysql) {
                     this.lexer.scanString2();
                  } else {
                     this.lexer.scanString();
                  }
               } else if (this.lexer.ch == '0') {
                  this.lexer.bufPos = 0;
                  if (this.lexer.charAt(this.lexer.pos + 1) == 'x') {
                     this.lexer.scanChar();
                     this.lexer.scanChar();
                     this.lexer.scanHexaDecimal();
                  } else {
                     this.lexer.scanNumber();
                  }
               } else if (this.lexer.ch > '0' && this.lexer.ch <= '9') {
                  this.lexer.bufPos = 0;
                  this.lexer.scanNumber();
               } else if (this.lexer.ch == '-' && this.lexer.charAt(this.lexer.pos + 1) != '-') {
                  this.lexer.bufPos = 0;
                  this.lexer.scanNumber();
               } else {
                  this.lexer.nextTokenValue();
               }

               ++j;
            }
         }

         if (this.lexer.token != Token.RPAREN) {
            throw new ParserException("syntax error. " + this.lexer.info());
         }

         this.lexer.nextTokenComma();
         if (this.lexer.token != Token.COMMA) {
            valueHandler.processComplete();
            return;
         }

         this.lexer.nextTokenLParen();
      }

      throw new ParserException("syntax error, expect ')', " + this.lexer.info());
   }

   protected void parseValueClause(List<SQLInsertStatement.ValuesClause> valueClauseList, List<SQLColumnDefinition> columnDefinitionList, int columnSize, SQLObject parent) {
      boolean optimizedForParameterized = this.lexer.isEnabled(SQLParserFeature.OptimizedForForParameterizedSkipValue);
      int i = 0;

      while(true) {
         int startPos = this.lexer.pos - 1;
         if (this.lexer.token == Token.ROW) {
            this.lexer.nextToken();
         }

         if (this.lexer.token != Token.LPAREN) {
            throw new ParserException("syntax error, expect ')', " + this.lexer.info());
         }

         if (this.lexer.ch == '\'') {
            this.lexer.bufPos = 0;
            if (this.dbType == DbType.mysql) {
               this.lexer.scanString2();
            } else {
               this.lexer.scanString();
            }
         } else if (this.lexer.ch == '0') {
            this.lexer.bufPos = 0;
            if (this.lexer.charAt(this.lexer.pos + 1) == 'x') {
               this.lexer.scanChar();
               this.lexer.scanChar();
               this.lexer.scanHexaDecimal();
            } else {
               this.lexer.scanNumber();
            }
         } else if (this.lexer.ch > '0' && this.lexer.ch <= '9') {
            this.lexer.bufPos = 0;
            this.lexer.scanNumber();
         } else if (this.lexer.ch == '-' && this.lexer.charAt(this.lexer.pos + 1) != '-') {
            this.lexer.bufPos = 0;
            this.lexer.scanNumber();
         } else {
            this.lexer.nextTokenValue();
         }

         SQLInsertStatement.ValuesClause values;
         if (this.lexer.token() == Token.RPAREN) {
            values = new SQLInsertStatement.ValuesClause(new ArrayList(0));
         } else {
            List valueExprList;
            if (columnSize > 0) {
               valueExprList = new ArrayList(columnSize);
            } else {
               valueExprList = new ArrayList();
            }

            values = new SQLInsertStatement.ValuesClause(valueExprList, parent);
            int j = 0;

            while(true) {
               SQLColumnDefinition columnDefinition = null;
               if (columnDefinitionList != null && j < columnDefinitionList.size()) {
                  columnDefinition = (SQLColumnDefinition)columnDefinitionList.get(j);
               }

               SQLDataType dataType = null;
               if (columnDefinition != null) {
                  dataType = columnDefinition.getDataType();
               }

               SQLExpr expr;
               if (this.lexer.token == Token.LITERAL_INT) {
                  if (optimizedForParameterized) {
                     expr = new SQLVariantRefExpr("?", values);
                     values.incrementReplaceCount();
                  } else {
                     expr = new SQLIntegerExpr(this.lexer.integerValue(), values);
                  }

                  if (this.lexer.ch == ',') {
                     this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                     this.lexer.token = Token.COMMA;
                  } else {
                     this.lexer.nextTokenCommaValue();
                  }

                  if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                     expr.setParent(values);
                  }
               } else if (this.lexer.token == Token.LITERAL_CHARS) {
                  if (optimizedForParameterized) {
                     expr = new SQLVariantRefExpr("?", values);
                     values.incrementReplaceCount();
                  } else {
                     expr = new SQLCharExpr(this.lexer.stringVal(), values);
                  }

                  if (this.lexer.ch == ',') {
                     this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                     this.lexer.token = Token.COMMA;
                  } else {
                     this.lexer.nextTokenCommaValue();
                  }

                  if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                     expr.setParent(values);
                  }
               } else if (this.lexer.token == Token.LITERAL_NCHARS) {
                  if (optimizedForParameterized) {
                     expr = new SQLVariantRefExpr("?", values);
                     values.incrementReplaceCount();
                  } else {
                     expr = new SQLNCharExpr(this.lexer.stringVal(), values);
                  }

                  if (this.lexer.ch == ',') {
                     this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                     this.lexer.token = Token.COMMA;
                  } else {
                     this.lexer.nextTokenCommaValue();
                  }

                  if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                     expr.setParent(values);
                  }
               } else if (this.lexer.token == Token.LITERAL_FLOAT) {
                  if (optimizedForParameterized) {
                     expr = new SQLVariantRefExpr("?", values);
                     values.incrementReplaceCount();
                  } else {
                     SQLNumberExpr numberExpr = this.lexer.numberExpr(parent);
                     if (dataType != null && dataType.nameHashCode64() == FnvHash.Constants.DECIMAL) {
                        Number number = numberExpr.getNumber();
                        int precision = 0;
                        int scale = 0;
                        List<SQLExpr> arguments = dataType.getArguments();
                        if (arguments.size() > 0) {
                           SQLExpr arg0 = (SQLExpr)arguments.get(0);
                           if (arg0 instanceof SQLIntegerExpr) {
                              precision = ((SQLIntegerExpr)arg0).getNumber().intValue();
                           }
                        }

                        if (arguments.size() > 1) {
                           SQLExpr arg0 = (SQLExpr)arguments.get(1);
                           if (arg0 instanceof SQLIntegerExpr) {
                              scale = ((SQLIntegerExpr)arg0).getNumber().intValue();
                           }
                        }

                        if (number instanceof BigDecimal) {
                           Number var23 = MySqlUtils.decimal((BigDecimal)number, precision, scale);
                           numberExpr.setNumber(var23);
                        }
                     }

                     expr = numberExpr;
                  }

                  if (this.lexer.ch == ',') {
                     this.lexer.ch = this.lexer.charAt(++this.lexer.pos);
                     this.lexer.token = Token.COMMA;
                  } else {
                     this.lexer.nextTokenCommaValue();
                  }

                  if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                     expr.setParent(values);
                  }
               } else if (this.lexer.token == Token.NULL) {
                  if (optimizedForParameterized) {
                     expr = new SQLVariantRefExpr("?", parent);
                     values.incrementReplaceCount();
                  } else {
                     expr = new SQLNullExpr(parent);
                  }

                  this.lexer.nextTokenCommaValue();
                  if (this.lexer.token != Token.COMMA && this.lexer.token != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                     expr.setParent(values);
                  }
               } else {
                  expr = this.exprParser.expr();
                  expr.setParent(values);
               }

               if (this.lexer.token == Token.COMMA) {
                  valueExprList.add(expr);
                  if (this.lexer.ch == '\'') {
                     this.lexer.bufPos = 0;
                     if (this.dbType == DbType.mysql) {
                        this.lexer.scanString2();
                     } else {
                        this.lexer.scanString();
                     }
                  } else if (this.lexer.ch == '0') {
                     this.lexer.bufPos = 0;
                     if (this.lexer.charAt(this.lexer.pos + 1) == 'x') {
                        this.lexer.scanChar();
                        this.lexer.scanChar();
                        this.lexer.scanHexaDecimal();
                     } else {
                        this.lexer.scanNumber();
                     }
                  } else if (this.lexer.ch > '0' && this.lexer.ch <= '9') {
                     this.lexer.bufPos = 0;
                     this.lexer.scanNumber();
                  } else if (this.lexer.ch == '-' && this.lexer.charAt(this.lexer.pos + 1) != '-') {
                     this.lexer.bufPos = 0;
                     this.lexer.scanNumber();
                  } else {
                     this.lexer.nextTokenValue();
                  }
               } else {
                  if (this.lexer.token == Token.RPAREN) {
                     valueExprList.add(expr);
                     break;
                  }

                  expr = this.exprParser.primaryRest(expr);
                  if (this.lexer.token != Token.COMMA && this.lexer.token() != Token.RPAREN) {
                     expr = this.exprParser.exprRest(expr);
                  }

                  expr.setParent(values);
                  valueExprList.add(expr);
                  if (this.lexer.token != Token.COMMA) {
                     break;
                  }

                  this.lexer.nextTokenValue();
               }

               ++j;
            }

            if (this.lexer.isEnabled(SQLParserFeature.KeepInsertValueClauseOriginalString)) {
               j = this.lexer.pos();
               String orginalString = this.lexer.subString(startPos, j - startPos);
               values.setOriginalString(orginalString);
            }
         }

         valueClauseList.add(values);
         if (this.lexer.token != Token.RPAREN) {
            throw new ParserException("syntax error. " + this.lexer.info());
         }

         if (!this.parseCompleteValues && valueClauseList.size() >= this.parseValuesSize) {
            this.lexer.skipToEOF();
            break;
         }

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

         this.lexer.nextTokenLParen();
         if (values != null) {
            columnSize = values.getValues().size();
         }

         ++i;
      }

   }

   public SQLSelectListCache getSelectListCache() {
      return this.selectListCache;
   }

   public void setSelectListCache(SQLSelectListCache selectListCache) {
      this.selectListCache = selectListCache;
   }

   protected HiveInsertStatement parseHiveInsertStmt() {
      HiveInsertStatement insert = new HiveInsertStatement();
      insert.setDbType(this.dbType);
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         insert.addBeforeComment(this.lexer.readAndResetComments());
      }

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

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

      insert.setTableSource(this.exprParser.name());
      boolean columnsParsed = false;
      if (this.lexer.token == Token.LPAREN) {
         Lexer.SavePoint mark = this.lexer.mark();
         this.lexer.nextToken();
         if (this.lexer.token == Token.SELECT) {
            this.lexer.reset(mark);
         } else {
            this.parseInsertColumns(insert);
            columnsParsed = true;
            this.accept(Token.RPAREN);
         }
      }

      if (this.lexer.token == Token.PARTITION) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);

         while(true) {
            SQLAssignItem ptExpr = new SQLAssignItem();
            ptExpr.setTarget(this.exprParser.name());
            if (this.lexer.token == Token.EQ || this.lexer.token == Token.EQEQ) {
               this.lexer.nextTokenValue();
               SQLExpr ptValue = this.exprParser.expr();
               ptExpr.setValue(ptValue);
            }

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

            this.lexer.nextToken();
         }
      }

      if (!columnsParsed && this.lexer.token == Token.LPAREN) {
         Lexer.SavePoint savePoint = this.lexer.mark();
         this.lexer.nextToken();
         if (this.lexer.token != Token.SELECT) {
            this.parseInsertColumns(insert);
            this.accept(Token.RPAREN);
         } else {
            this.lexer.reset(savePoint);
         }
      }

      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.NOT);
         this.accept(Token.EXISTS);
         insert.setIfNotExists(true);
      }

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

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

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

            this.lexer.nextToken();
         }
      } else {
         SQLSelect query = selectParser.select();
         insert.setQuery(query);
      }

      return insert;
   }

   protected HiveInsert parseHiveInsert() {
      HiveInsert insert = new HiveInsert();
      if (this.lexer.isKeepComments() && this.lexer.hasComment()) {
         insert.addBeforeComment(this.lexer.readAndResetComments());
      }

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

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

      insert.setTableSource(this.exprParser.name());
      if (this.lexer.token == Token.PARTITION) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);

         while(true) {
            SQLAssignItem ptExpr = new SQLAssignItem();
            ptExpr.setTarget(this.exprParser.name());
            if (this.lexer.token == Token.EQ) {
               this.lexer.nextToken();
               SQLExpr ptValue = this.exprParser.expr();
               ptExpr.setValue(ptValue);
            }

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

            this.lexer.nextToken();
         }
      }

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

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

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

            this.lexer.nextToken();
         }
      } else {
         SQLSelect query = selectParser.select();
         insert.setQuery(query);
      }

      return insert;
   }

   protected SQLShowDatabasesStatement parseShowDatabases(boolean isPhysical) {
      SQLShowDatabasesStatement stmt = new SQLShowDatabasesStatement();
      stmt.setPhysical(isPhysical);
      if (this.lexer.token == Token.LIKE) {
         this.lexer.nextToken();
         SQLExpr like = this.exprParser.expr();
         stmt.setLike(like);
      }

      if (this.lexer.token == Token.WHERE) {
         this.lexer.nextToken();
         SQLExpr where = this.exprParser.expr();
         stmt.setWhere(where);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.EXTRA)) {
         this.lexer.nextToken();
         stmt.setExtra(true);
      }

      return stmt;
   }

   protected SQLShowTableGroupsStatement parseShowTableGroups() {
      SQLShowTableGroupsStatement stmt = new SQLShowTableGroupsStatement();
      if (this.lexer.token == Token.IN) {
         this.lexer.nextToken();
         SQLName db = this.exprParser.name();
         stmt.setDatabase(db);
      }

      return stmt;
   }

   protected SQLShowTablesStatement parseShowTables() {
      SQLShowTablesStatement stmt = new SQLShowTablesStatement();
      if (this.lexer.identifierEquals(FnvHash.Constants.SHOW)) {
         this.lexer.nextToken();
      }

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

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

      if (this.lexer.token == Token.FROM || this.lexer.token == Token.IN) {
         this.lexer.nextToken();
         SQLName database = this.exprParser.name();
         if (this.lexer.token == Token.SUB && database instanceof SQLIdentifierExpr) {
            this.lexer.mark();
            this.lexer.nextToken();
            String strVal = this.lexer.stringVal();
            this.lexer.nextToken();
            if (database instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr ident = (SQLIdentifierExpr)database;
               database = new SQLIdentifierExpr(ident.getName() + "-" + strVal);
            }
         }

         stmt.setDatabase(database);
      }

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

      if (this.lexer.token == Token.WHERE) {
         this.lexer.nextToken();
         SQLExpr where = this.exprParser.expr();
         stmt.setWhere(where);
      }

      return stmt;
   }

   protected SQLShowColumnsStatement parseShowColumns() {
      SQLShowColumnsStatement stmt = new SQLShowColumnsStatement();
      if (this.lexer.token == Token.FROM) {
         this.lexer.nextToken();
         SQLName table = this.exprParser.name();
         stmt.setTable(table);
         if (this.lexer.token == Token.FROM || this.lexer.token == Token.IN) {
            this.lexer.nextToken();
            SQLName database = this.exprParser.name();
            stmt.setDatabase(database);
         }
      } else if (this.lexer.token == Token.IN) {
         this.lexer.nextToken();
         SQLName table = this.exprParser.name();
         stmt.setTable(table);
      }

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

      if (this.lexer.token == Token.WHERE) {
         this.lexer.nextToken();
         SQLExpr where = this.exprParser.expr();
         stmt.setWhere(where);
      }

      return stmt;
   }

   protected SQLStatement parseAlterIndex() {
      this.accept(Token.ALTER);
      this.lexer.nextToken();
      SQLAlterIndexStatement stmt = new SQLAlterIndexStatement();
      stmt.setName(this.exprParser.name());
      if (this.lexer.identifierEquals("RENAME")) {
         this.lexer.nextToken();
         this.accept(Token.TO);
         stmt.setRenameTo(this.exprParser.name());
      }

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

         SQLName table = this.exprParser.name();
         stmt.setTable(table);
      }

      if (this.lexer.token == Token.PARTITION) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         this.parseAssignItems(stmt.getPartitions(), stmt);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.DBPARTITION)) {
         SQLPartitionBy partitionClause = this.getSQLCreateTableParser().parsePartitionBy();
         stmt.setDbPartitionBy(partitionClause);
      }

      if (this.lexer.token == Token.ENABLE) {
         this.lexer.nextToken();
         stmt.setEnable(true);
      }

      if (this.lexer.token == Token.DISABLE) {
         this.lexer.nextToken();
         stmt.setEnable(false);
      }

      if (this.lexer.hash_lower == FnvHash.Constants.UNUSABLE) {
         this.lexer.nextToken();
         stmt.setUnusable(true);
      }

      while(true) {
         while(!this.lexer.identifierEquals("rebuild")) {
            if (!this.lexer.identifierEquals("MONITORING")) {
               if (this.lexer.identifierEquals("PARALLEL")) {
                  this.lexer.nextToken();
                  stmt.setParallel(this.exprParser.expr());
               }

               return stmt;
            }

            this.lexer.nextToken();
            this.acceptIdentifier("USAGE");
            stmt.setMonitoringUsage(Boolean.TRUE);
         }

         this.lexer.nextToken();
         SQLAlterIndexStatement.Rebuild rebuild = new SQLAlterIndexStatement.Rebuild();
         stmt.setRebuild(rebuild);
      }
   }

   protected SQLStatement parseAnalyze() {
      this.lexer.nextToken();
      this.accept(Token.TABLE);
      SQLAnalyzeTableStatement stmt = new SQLAnalyzeTableStatement();
      SQLName table = this.exprParser.name();
      stmt.setTable(table);
      if (this.lexer.token() == Token.PARTITION) {
         stmt.setPartition(this.parsePartitionRef());
      }

      this.accept(Token.COMPUTE);
      this.acceptIdentifier("STATISTICS");
      stmt.setComputeStatistics(true);
      if (this.lexer.token == Token.FOR) {
         this.lexer.nextToken();
         this.acceptIdentifier("COLUMNS");
         stmt.setForColums(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.CACHE)) {
         this.lexer.nextToken();
         this.acceptIdentifier("METADATA");
         stmt.setCacheMetadata(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.NOSCAN)) {
         this.lexer.nextToken();
         stmt.setNoscan(true);
      }

      return stmt;
   }

   public SQLAlterSequenceStatement parseAlterSequence() {
      this.accept(Token.ALTER);
      this.accept(Token.SEQUENCE);
      SQLAlterSequenceStatement stmt = new SQLAlterSequenceStatement();
      stmt.setDbType(this.dbType);
      stmt.setName(this.exprParser.name());
      if (this.lexer.identifierEquals(FnvHash.Constants.CHANGE)) {
         this.lexer.nextToken();
         this.accept(Token.TO);
         if (this.lexer.identifierEquals(FnvHash.Constants.SIMPLE)) {
            stmt.setChangeToSimple(true);
            this.lexer.nextToken();
            if (this.lexer.hash_lower() == FnvHash.Constants.WITH) {
               this.lexer.nextToken();
               this.accept(Token.CACHE);
               stmt.setWithCache(true);
            }
         } else if (this.lexer.token == Token.GROUP) {
            stmt.setChangeToGroup(true);
            this.lexer.nextToken();
         } else {
            if (!this.lexer.identifierEquals(FnvHash.Constants.TIME)) {
               throw new ParserException("TODO " + this.lexer.info());
            }

            stmt.setChangeToTime(true);
            this.lexer.nextToken();
         }
      }

      while(true) {
         while(this.lexer.token() == Token.START || this.lexer.identifierEquals(FnvHash.Constants.START)) {
            this.lexer.nextToken();
            this.accept(Token.WITH);
            stmt.setStartWith(this.exprParser.expr());
         }

         if (this.lexer.identifierEquals("INCREMENT")) {
            this.lexer.nextToken();
            this.accept(Token.BY);
            stmt.setIncrementBy(this.exprParser.expr());
         } else if (this.lexer.token() == Token.CACHE) {
            this.lexer.nextToken();
            stmt.setCache(Boolean.TRUE);
            if (this.lexer.token() == Token.LITERAL_INT || this.lexer.token() == Token.QUES) {
               stmt.setCacheValue(this.exprParser.primary());
            }
         } else if (this.lexer.token() == Token.NOCACHE) {
            this.lexer.nextToken();
            stmt.setCache(Boolean.FALSE);
         } else if (this.lexer.token() == Token.ORDER) {
            this.lexer.nextToken();
            stmt.setOrder(Boolean.TRUE);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.RESTART)) {
            this.lexer.nextToken();
            stmt.setRestart(true);
            if (this.lexer.token != Token.WITH && this.lexer.token != Token.EQ) {
               if (this.lexer.token == Token.LITERAL_INT) {
                  stmt.setRestartWith(this.exprParser.primary());
               }
            } else {
               this.lexer.nextToken();
               stmt.setRestartWith(this.exprParser.primary());
            }
         } else if (this.lexer.identifierEquals("NOORDER")) {
            this.lexer.nextToken();
            stmt.setOrder(Boolean.FALSE);
         } else if (this.lexer.identifierEquals("CYCLE")) {
            this.lexer.nextToken();
            stmt.setCycle(Boolean.TRUE);
         } else if (this.lexer.identifierEquals("NOCYCLE")) {
            this.lexer.nextToken();
            stmt.setCycle(Boolean.FALSE);
         } else if (this.lexer.identifierEquals("MINVALUE")) {
            this.lexer.nextToken();
            stmt.setMinValue(this.exprParser.expr());
         } else if (this.lexer.identifierEquals("MAXVALUE")) {
            this.lexer.nextToken();
            stmt.setMaxValue(this.exprParser.expr());
         } else if (this.lexer.identifierEquals("NOMAXVALUE")) {
            this.lexer.nextToken();
            stmt.setNoMaxValue(true);
         } else if (this.lexer.identifierEquals("NOMINVALUE")) {
            this.lexer.nextToken();
            stmt.setNoMinValue(true);
         } else {
            if (!this.lexer.identifierEquals("CURRENT")) {
               return stmt;
            }

            this.lexer.nextToken();
            this.acceptIdentifier("VALUE");
            stmt.setCurrentValue(this.exprParser.expr());
         }
      }
   }

   public SQLStatementImpl parseAlterSession() {
      this.accept(Token.ALTER);
      this.accept(Token.SESSION);
      this.accept(Token.SET);
      if (this.lexer.identifierEquals("NLS_DATE_LANGUAGE")) {
         this.acceptIdentifier("NLS_DATE_LANGUAGE");
         this.accept(Token.EQ);
         SQLAlterSetNlsDateLanguageStatement stmt = new SQLAlterSetNlsDateLanguageStatement();
         stmt.setDbType(this.dbType);
         if (this.lexer.identifierEquals("AMERICAN")) {
            this.lexer.nextToken();
            stmt.setType(SQLAlterSetNlsDateLanguageStatement.Type.AMERICAN);
         } else if (this.lexer.identifierEquals("ENGLISH")) {
            this.lexer.nextToken();
            stmt.setType(SQLAlterSetNlsDateLanguageStatement.Type.ENGLISH);
         } else if (this.lexer.identifierEquals("SIMPLIFIED")) {
            this.lexer.nextToken();
            this.acceptIdentifier("CHINESE");
            stmt.setType(SQLAlterSetNlsDateLanguageStatement.Type.SIMPLIFIED_CHINESE);
         }

         return stmt;
      } else {
         SQLAlterSetDateFormatStatement dateFormatStatement = new SQLAlterSetDateFormatStatement();
         dateFormatStatement.setType(this.exprParser.name());
         this.accept(Token.EQ);
         dateFormatStatement.setFormat(this.exprParser.expr());
         if (this.lexer.token() == Token.PURGE) {
            dateFormatStatement.setPurge(true);
            this.lexer.nextToken();
         }

         return dateFormatStatement;
      }
   }

   public SQLStatement parseAlterSystem() {
      this.accept(Token.ALTER);
      this.acceptIdentifier("SYSTEM");
      SQLAlterSystemStatement stmt = new SQLAlterSystemStatement();
      if (this.lexer.identifierEquals("ARCHIVE")) {
         this.lexer.nextToken();
         this.acceptIdentifier("LOG");
         this.acceptIdentifier("CURRENT");
         stmt.setArchiveLogCurrent(true);
         return stmt;
      } else if (this.lexer.identifierEquals("SWITCH")) {
         this.lexer.nextToken();
         this.acceptIdentifier("LOGFILE");
         stmt.setSwitchLogfile(true);
         return stmt;
      } else {
         this.accept(Token.SET);
         stmt.setItem(this.exprParser.name());
         this.accept(Token.EQ);
         stmt.setValue(this.exprParser.expr());
         if (this.lexer.token == Token.DEFERRED) {
            stmt.setDeferred(true);
            this.lexer.nextToken();
         }

         if (this.lexer.identifierEquals("MEMORY")) {
            stmt.setOption(SQLAlterSystemStatement.Option.MEMORY);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("BOTH")) {
            stmt.setOption(SQLAlterSystemStatement.Option.BOTH);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals("SPFILE")) {
            stmt.setOption(SQLAlterSystemStatement.Option.SPFILE);
            this.lexer.nextToken();
         }

         return stmt;
      }
   }

   protected SQLStatement parseMsck() {
      this.lexer.nextToken();
      this.lexer.identifierEquals("REPAIR");
      this.lexer.nextToken();
      HiveMsckRepairStatement stmt = new HiveMsckRepairStatement();
      if (this.lexer.token() == Token.DATABASE || this.lexer.token() == Token.SCHEMA) {
         this.lexer.nextToken();
         SQLName name = this.exprParser.name();
         stmt.setDatabase(name);
      }

      if (this.lexer.token() == Token.TABLE) {
         this.lexer.nextToken();
         SQLExpr tableExpr = this.exprParser.expr();
         stmt.setTable(tableExpr);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.ADD)) {
         this.lexer.nextToken();
         this.acceptIdentifier("PARTITIONS");
         stmt.setAddPartitions(true);
      }

      return stmt;
   }

   protected SQLStatement parseCreateResourceGroup() {
      this.accept(Token.CREATE);
      this.acceptIdentifier("RESOURCE");
      this.accept(Token.GROUP);
      SQLCreateResourceGroupStatement stmt = new SQLCreateResourceGroupStatement();
      stmt.setName(this.exprParser.name());

      while(this.lexer.token() != Token.SEMI && this.lexer.token() != Token.EOF) {
         if (this.lexer.token() == Token.ENABLE) {
            this.lexer.nextToken();
            stmt.setEnable(true);
         } else if (this.lexer.token() == Token.DISABLE) {
            this.lexer.nextToken();
            stmt.setEnable(false);
         }

         Lexer.SavePoint m = this.lexer.mark();
         String name = this.lexer.stringVal();
         this.lexer.nextToken();
         if (this.lexer.token() != Token.EQ) {
            this.lexer.reset(m);
            break;
         }

         this.lexer.nextToken();
         SQLExpr value = this.exprParser.expr();
         if (this.lexer.token() != Token.COMMA) {
            stmt.addProperty(name, value);
         } else {
            SQLListExpr list = new SQLListExpr();
            list.addItem(value);

            while(this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
               list.addItem(this.exprParser.expr());
            }

            stmt.addProperty(name, list);
         }
      }

      return stmt;
   }

   protected SQLStatement parseAlterResourceGroup() {
      this.accept(Token.ALTER);
      this.acceptIdentifier("RESOURCE");
      this.accept(Token.GROUP);
      SQLAlterResourceGroupStatement stmt = new SQLAlterResourceGroupStatement();
      stmt.setName(this.exprParser.name());

      while(this.lexer.token() != Token.SEMI && this.lexer.token() != Token.EOF) {
         if (this.lexer.token() == Token.ENABLE) {
            this.lexer.nextToken();
            stmt.setEnable(true);
         } else if (this.lexer.token() == Token.DISABLE) {
            this.lexer.nextToken();
            stmt.setEnable(false);
         }

         Lexer.SavePoint m = this.lexer.mark();
         String name = this.lexer.stringVal();
         this.lexer.nextToken();
         if (this.lexer.token() != Token.EQ) {
            this.lexer.reset(m);
            break;
         }

         this.lexer.nextToken();
         SQLExpr value = this.exprParser.expr();
         if (this.lexer.token() != Token.COMMA) {
            stmt.addProperty(name, value);
         } else {
            SQLListExpr list = new SQLListExpr();
            list.addItem(value);

            while(this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
               list.addItem(this.exprParser.expr());
            }

            stmt.addProperty(name, list);
         }
      }

      return stmt;
   }

   public SQLStatement parseAlterMaterialized() {
      SQLAlterMaterializedViewStatement stmt = new SQLAlterMaterializedViewStatement();
      stmt.setDbType(this.dbType);
      this.accept(Token.ALTER);
      this.acceptIdentifier("MATERIALIZED");
      this.accept(Token.VIEW);
      stmt.setName(this.exprParser.name());
      if (this.lexer.token() == Token.EOF) {
         throw new ParserException("syntax error. " + this.lexer.info());
      } else {
         label74:
         while(this.lexer.identifierEquals("REFRESH")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EOF) {
               throw new ParserException("syntax error. " + this.lexer.info());
            }

            boolean refresh = false;

            while(true) {
               while(!this.lexer.identifierEquals("FAST")) {
                  if (this.lexer.identifierEquals("COMPLETE")) {
                     this.lexer.nextToken();
                     stmt.setRefreshComplete(true);
                     refresh = true;
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.FORCE)) {
                     this.lexer.nextToken();
                     stmt.setRefreshForce(true);
                     refresh = true;
                  } else if (this.lexer.token != Token.ON) {
                     if (this.lexer.identifierEquals(FnvHash.Constants.START)) {
                        this.lexer.nextToken();
                        this.accept(Token.WITH);
                        SQLExpr startWith = this.exprParser.expr();
                        stmt.setStartWith(startWith);
                        stmt.setRefreshStartWith(true);
                        refresh = true;
                     } else {
                        if (!this.lexer.identifierEquals(FnvHash.Constants.NEXT)) {
                           if (!refresh) {
                              throw new ParserException("refresh clause is empty. " + this.lexer.info());
                           }
                           continue label74;
                        }

                        this.lexer.nextToken();
                        SQLExpr next = this.exprParser.expr();
                        stmt.setNext(next);
                        stmt.setRefreshNext(true);
                        refresh = true;
                     }
                  } else {
                     this.lexer.nextToken();
                     if (this.lexer.token != Token.COMMIT && !this.lexer.identifierEquals(FnvHash.Constants.COMMIT)) {
                        if (this.lexer.identifierEquals(FnvHash.Constants.OVERWRITE)) {
                           this.lexer.nextToken();
                           stmt.setRefreshOnOverWrite(true);
                        } else {
                           this.acceptIdentifier("DEMAND");
                           stmt.setRefreshOnDemand(true);
                        }
                     } else {
                        this.lexer.nextToken();
                        stmt.setRefreshOnCommit(true);
                     }

                     refresh = true;
                  }
               }

               this.lexer.nextToken();
               stmt.setRefreshFast(true);
               refresh = true;
            }
         }

         Boolean enableQueryRewrite = null;
         if (this.lexer.token == Token.ENABLE) {
            this.lexer.nextToken();
            enableQueryRewrite = true;
         }

         if (this.lexer.token == Token.DISABLE) {
            this.lexer.nextToken();
            enableQueryRewrite = false;
         }

         if (enableQueryRewrite != null) {
            this.acceptIdentifier("QUERY");
            this.acceptIdentifier("REWRITE");
            stmt.setEnableQueryRewrite(enableQueryRewrite);
         }

         return stmt;
      }
   }

   public SQLCreateFunctionStatement parseHiveCreateFunction() {
      HiveCreateFunctionStatement stmt = new HiveCreateFunctionStatement();
      stmt.setDbType(this.dbType);
      this.accept(Token.CREATE);
      if (this.lexer.identifierEquals(FnvHash.Constants.TEMPORARY)) {
         this.lexer.nextToken();
         stmt.setTemporary(true);
      }

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

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

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

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

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

      return stmt;
   }

   protected SQLShowCreateTableStatement parseShowCreateTable() {
      this.lexer.nextToken();
      this.accept(Token.TABLE);
      SQLShowCreateTableStatement stmt = new SQLShowCreateTableStatement();
      stmt.setDbType(this.dbType);
      stmt.setName(this.exprParser.name());
      if (this.lexer.token() == Token.LIKE) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals(FnvHash.Constants.MAPPING)) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            SQLName name = this.exprParser.name();
            stmt.setLikeMapping(name);
            this.accept(Token.RPAREN);
         }
      }

      return stmt;
   }

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

      SQLDropSynonymStatement stmt = new SQLDropSynonymStatement();
      stmt.setDbType(this.dbType);
      if (this.lexer.identifierEquals(FnvHash.Constants.PUBLIC)) {
         this.lexer.nextToken();
         stmt.setPublic(true);
      }

      this.acceptIdentifier("SYNONYM");
      if (this.lexer.token == Token.IF) {
         this.lexer.nextToken();
         this.accept(Token.EXISTS);
         stmt.setIfExists(true);
      }

      stmt.setName(this.exprParser.name());
      if (this.lexer.identifierEquals(FnvHash.Constants.FORCE)) {
         this.lexer.nextToken();
         stmt.setForce(true);
      }

      if (this.lexer.token() == Token.CASCADE) {
         stmt.setCascade(true);
         this.lexer.nextToken();
      } else if (this.lexer.token() == Token.RESTRICT) {
         stmt.setRestrict(true);
         this.lexer.nextToken();
      }

      return stmt;
   }

   protected SQLStatement parseDropSession() {
      DB2DropSessionStatement stmt = new DB2DropSessionStatement();
      this.accept(Token.SESSION);
      stmt.setSessionId(this.exprParser.expr());
      return stmt;
   }
}
