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

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.ast.SQLArgument;
import com.chenyang.druid.sql.ast.SQLCommentHint;
import com.chenyang.druid.sql.ast.SQLDataType;
import com.chenyang.druid.sql.ast.SQLDataTypeImpl;
import com.chenyang.druid.sql.ast.SQLDeclareItem;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLHint;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObject;
import com.chenyang.druid.sql.ast.SQLParameter;
import com.chenyang.druid.sql.ast.SQLPartition;
import com.chenyang.druid.sql.ast.SQLPartitionBy;
import com.chenyang.druid.sql.ast.SQLPartitionValue;
import com.chenyang.druid.sql.ast.SQLRecordDataType;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.SQLStatementImpl;
import com.chenyang.druid.sql.ast.SQLSubPartition;
import com.chenyang.druid.sql.ast.expr.SQLCaseStatement;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLIntegerExpr;
import com.chenyang.druid.sql.ast.expr.SQLNullExpr;
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.SQLAlterDatabaseStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddSupplemental;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAlterColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDisableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableDisableTriggers;
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.SQLAlterTableDropPrimaryKey;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableEnableConstraint;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableItem;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRename;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableRenameColumn;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceAddDatafile;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceCache;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceDatafile;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceRename;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceRenameDatafile;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceResizeDatafile;
import com.chenyang.druid.sql.ast.statement.SQLAlterTablespaceStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterUserStatement;
import com.chenyang.druid.sql.ast.statement.SQLAlterViewRenameStatement;
import com.chenyang.druid.sql.ast.statement.SQLBlockStatement;
import com.chenyang.druid.sql.ast.statement.SQLCallStatement;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCommitStatement;
import com.chenyang.druid.sql.ast.statement.SQLConstraint;
import com.chenyang.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateProcedureStatement;
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.SQLDeclareStatement;
import com.chenyang.druid.sql.ast.statement.SQLDeleteStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropIndexStatement;
import com.chenyang.druid.sql.ast.statement.SQLDropUserStatement;
import com.chenyang.druid.sql.ast.statement.SQLExplainStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLIfStatement;
import com.chenyang.druid.sql.ast.statement.SQLInsertInto;
import com.chenyang.druid.sql.ast.statement.SQLLoopStatement;
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.SQLTableElement;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.ast.statement.SQLValuesTableSource;
import com.chenyang.druid.sql.ast.statement.SQLWhileStatement;
import com.chenyang.druid.sql.dialect.dm.ast.clause.DmReturningClause;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DMSQLAlterTableRebuildColumns;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterDatabaseAddLogfile;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterDatabaseArchivelog;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterDatabaseMoveDatafile;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterDatabaseRenameLogfile;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterDatabaseResizeLogfile;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterDatabaseStatus;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterIndexStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterPackageCompile;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableAddConstraint;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableAddIdentity;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableAddPartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableAlterColumn;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableCounter;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableDefaultDirectory;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableDropIdentity;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableDropPartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableEnableUsingLongRow;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableExchangePartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableLocation;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableMergePartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableModify;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableModifyConstraint;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableModifyDiskspace;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableModifyPartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableMovePartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableRenamePartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableRowMovement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableSetPartitionTemplate;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableSplitPartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTableTruncateSubpartition;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterTriggerStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmAlterViewStatment;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmContinueStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmCreateIndexStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmCreatePackageStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmCreateSchemaStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmCreateSynonymStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmCreateTriggerStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmDropUserStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmExceptionStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmExecuteImmediateStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmExitStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmGotoStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmInsertStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmLabelStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmLockTableStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmLogfile;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmMultiInsertStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmPrintStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmRaiseStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmSQLTruncateStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmSelectTableReference;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmSetTransactionStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmStatement;
import com.chenyang.druid.sql.dialect.dm.ast.stmt.DmUpdateStatement;
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.MySqlSelectQueryBlock;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlUpdatePlanCacheStatement;
import com.chenyang.druid.sql.dialect.mysql.parser.MySqlExprParser;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableDropPartition;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableTruncatePartition;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleCreateTypeStatement;
import com.chenyang.druid.sql.dialect.oracle.parser.OracleFunctionDataType;
import com.chenyang.druid.sql.dialect.oracle.parser.OracleProcedureDataType;
import com.chenyang.druid.sql.parser.Lexer;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.parser.SQLCreateTableParser;
import com.chenyang.druid.sql.parser.SQLExprParser;
import com.chenyang.druid.sql.parser.SQLStatementParser;
import com.chenyang.druid.sql.parser.Token;
import com.chenyang.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.List;

public class DmStatementParser extends SQLStatementParser {
   public DmStatementParser(String sql) {
      this(sql, DbType.dm);
   }

   public DmStatementParser(String sql, DbType dbType) {
      super((SQLExprParser)(new DmExprParser(sql)));
   }

   protected DmStatementParser(Lexer lexer, DbType dbType) {
      super(lexer, dbType);
   }

   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 stmtVar = new SQLSelectStatement(select);
         statementList.add(stmtVar);
         this.lexer.reset(29, '\u001a', Token.EOF);
      } else {
         boolean semi = false;
         SQLStatement stmtVar = null;
         Lexer.SavePoint sp = null;

         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();
            }

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

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

                  semi = true;
                  break;
               case WITH:
                  stmtVar = this.parseWith();
                  stmtVar.setParent(parent);
                  statementList.add(stmtVar);
                  break;
               case SELECT:
                  MySqlHintStatement hintStatement = null;
                  stmtVar = this.parseSelect();
                  stmtVar.setParent(parent);
                  if (hintStatement != null && stmtVar instanceof SQLStatementImpl) {
                     SQLStatementImpl stmtImpl = (SQLStatementImpl)stmtVar;
                     List<SQLCommentHint> hints = stmtImpl.getHeadHintsDirect();
                     if (hints == null) {
                        stmtImpl.setHeadHints(hintStatement.getHints());
                     } else {
                        hints.addAll(hintStatement.getHints());
                     }

                     statementList.set(statementList.size() - 1, stmtVar);
                  } else {
                     statementList.add(stmtVar);
                  }

                  semi = false;
                  break;
               case UPDATE:
                  sp = this.lexer.mark();
                  this.lexer.nextToken();
                  if (this.dbType == DbType.mysql && this.lexer.identifierEquals("PLANCACHE")) {
                     this.lexer.nextToken();
                     if (this.lexer.token() == Token.SELECT) {
                        stmtVar = new MySqlUpdatePlanCacheStatement();
                        SQLSelect fromSelect = this.createSQLSelectParser().select();
                        this.accept(Token.TO);
                        SQLSelect toSelect = this.createSQLSelectParser().select();
                        ((MySqlUpdatePlanCacheStatement)stmtVar).setFormSelect(fromSelect);
                        ((MySqlUpdatePlanCacheStatement)stmtVar).setToSelect(toSelect);
                        statementList.add(stmtVar);
                        break;
                     }
                  }

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

                           this.lexer.reset(mark);
                        }

                        if (this.lexer.identifierEquals("RENAME")) {
                           stmtVar = this.parseRename();
                           statementList.add(stmtVar);
                        } else if (this.lexer.token() == Token.EXCEPTION) {
                           DmExceptionStatement ex = this.parseException();
                           ex.setParent(parent);
                           if (parent instanceof SQLBlockStatement) {
                              ((SQLBlockStatement)parent).setException(ex);
                           } else {
                              statementList.add(ex);
                           }
                        } else if (this.lexer.identifierEquals("EXIT")) {
                           this.lexer.nextToken();
                           stmtVar = this.parseExit();
                           stmtVar.setParent(parent);
                           statementList.add(stmtVar);
                        } else if (this.lexer.token() == Token.CONTINUE) {
                           this.lexer.nextToken();
                           stmtVar = new DmContinueStatement();
                           if (this.lexer.token() == Token.IDENTIFIER) {
                              String label = this.lexer.stringVal();
                              this.lexer.nextToken();
                              ((DmContinueStatement)stmtVar).setLabel(label);
                           }

                           if (this.lexer.token() == Token.WHEN) {
                              this.lexer.nextToken();
                              ((DmContinueStatement)stmtVar).setWhen(this.exprParser.expr());
                           }

                           stmtVar.setParent(parent);
                           statementList.add(stmtVar);
                        } else if (this.lexer.identifierEquals("EXECUTE")) {
                           stmtVar = this.parseExecute();
                           stmtVar.setParent(parent);
                           statementList.add(stmtVar);
                        } else if (this.lexer.token() == Token.GOTO) {
                           this.lexer.nextToken();
                           SQLName label = this.exprParser.name();
                           stmtVar = new DmGotoStatement(label);
                           stmtVar.setParent(parent);
                           statementList.add(stmtVar);
                        } else if (this.lexer.token() == Token.LTLT) {
                           this.lexer.nextToken();
                           SQLName label = this.exprParser.name();
                           stmtVar = new DmLabelStatement(label);
                           this.accept(Token.GTGT);
                           stmtVar.setParent(parent);
                           statementList.add(stmtVar);
                        } else if (this.lexer.identifierEquals("RELEASE")) {
                           stmtVar = this.parseReleaseSavePoint();
                           statementList.add(stmtVar);
                        } else if (this.lexer.token() != Token.BEGIN && this.lexer.token() != Token.DECLARE) {
                           if (this.lexer.token() == Token.LOCK) {
                              stmtVar = this.parseLockTable();
                              statementList.add(stmtVar);
                           } else if (this.lexer.token() == Token.SAVEPOINT) {
                              stmtVar = this.parseSavePoint();
                              statementList.add(stmtVar);
                           } else if (this.lexer.identifierEquals("REFRESH")) {
                              stmtVar = this.parseRefresh();
                              statementList.add(stmtVar);
                           } else if (this.lexer.identifierEquals(FnvHash.Constants.COPY)) {
                              stmtVar = this.parseCopy();
                              statementList.add(stmtVar);
                           } else if (this.lexer.token() != Token.DESC && !this.lexer.identifierEquals(FnvHash.Constants.DESCRIBE)) {
                              if (this.lexer.identifierEquals("ROLLBACK")) {
                                 stmtVar = this.parseRollback();
                                 statementList.add(stmtVar);
                                 if (parent instanceof SQLBlockStatement && DbType.mysql == this.dbType) {
                                    return;
                                 }
                              } else if (this.lexer.identifierEquals("DUMP")) {
                                 stmtVar = this.parseDump();
                                 statementList.add(stmtVar);
                              } else if (this.lexer.token() == Token.COMMIT) {
                                 stmtVar = this.parseCommit();
                                 statementList.add(stmtVar);
                                 if (parent instanceof SQLBlockStatement && DbType.mysql == this.dbType) {
                                    return;
                                 }
                              } else if (this.lexer.identifierEquals(FnvHash.Constants.RETURN)) {
                                 stmtVar = this.parseReturn();
                                 statementList.add(stmtVar);
                              } else if (this.lexer.identifierEquals(FnvHash.Constants.PURGE)) {
                                 stmtVar = this.parsePurge();
                                 statementList.add(stmtVar);
                              } else if (this.lexer.identifierEquals(FnvHash.Constants.FLASHBACK)) {
                                 stmtVar = this.parseFlashback();
                                 statementList.add(stmtVar);
                              } else if (this.lexer.identifierEquals(FnvHash.Constants.WHO)) {
                                 stmtVar = this.parseWhoami();
                                 statementList.add(stmtVar);
                              } else if (this.lexer.token() == Token.FOR) {
                                 stmtVar = this.parseFor();
                                 statementList.add(stmtVar);
                                 stmtVar.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);
                                 stmtVar = this.parseSelect();
                                 statementList.add(stmtVar);
                              } else if (this.lexer.token() == Token.VALUES) {
                                 SQLValuesTableSource values = this.createSQLSelectParser().parseValues();
                                 SQLSelectStatement selectStmt = new SQLSelectStatement();
                                 selectStmt.setSelect(new SQLSelect(values));
                                 statementList.add(selectStmt);
                                 selectStmt.setParent(parent);
                              } else if (this.lexer.identifierEquals("PRINT")) {
                                 this.lexer.nextToken();
                                 DmPrintStatement printStmt = new DmPrintStatement();
                                 printStmt.setString(this.exprParser.expr());
                                 printStmt.setParent(parent);
                                 statementList.add(printStmt);
                              } else if (this.lexer.identifierEquals("RAISE")) {
                                 stmtVar = this.parseRaise();
                                 stmtVar.setParent(parent);
                                 statementList.add(stmtVar);
                              } else {
                                 if (this.lexer.token() == Token.IDENTIFIER || this.lexer.token() == Token.VARIANT) {
                                    sp = this.lexer.mark();
                                    if (this.lexer.token() == Token.VARIANT) {
                                       this.exprParser.name();
                                    } else {
                                       this.lexer.nextToken();
                                       if (this.lexer.token() == Token.DOT) {
                                          this.lexer.nextToken();
                                          this.lexer.nextToken();
                                       }
                                    }

                                    if (this.lexer.token() == Token.COLONEQ || this.lexer.token() == Token.EQ) {
                                       this.lexer.reset(sp);
                                       stmtVar = this.parseSet();
                                       stmtVar.setParent(parent);
                                       statementList.add(stmtVar);
                                       continue;
                                    }

                                    if (this.lexer.token() == Token.LPAREN) {
                                       this.lexer.reset(sp);
                                       SQLCallStatement call = this.parseCall();
                                       statementList.add(call);
                                       continue;
                                    }

                                    this.lexer.reset(sp);
                                 }

                                 int size = statementList.size();
                                 if (this.parseStatementListDialect(statementList)) {
                                    if (parent != null) {
                                       for(int j = size; j < statementList.size(); ++j) {
                                          SQLStatement dialectStmt = statementList.get(j);
                                          dialectStmt.setParent(parent);
                                       }
                                    }
                                 } else {
                                    this.printError(this.lexer.token());
                                 }
                              }
                           } else {
                              stmtVar = this.parseDescribe();
                              statementList.add(stmtVar);
                           }
                        } else {
                           stmtVar = this.parseBlock();
                           stmtVar.setParent(parent);
                           statementList.add(stmtVar);
                        }
                     }
                  } else {
                     SQLCallStatement call = this.parseCall();
                     statementList.add(call);
                  }
            }
         }

      }
   }

   public SQLCreateFunctionStatement parseCreateFunction() {
      SQLCreateFunctionStatement stmt = new SQLCreateFunctionStatement();
      stmt.setDbType(this.dbType);
      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);
         }
      } else {
         if (this.lexer.token() == Token.DECLARE) {
            this.lexer.nextToken();
         }

         stmt.setCreate(false);
      }

      this.accept(Token.FUNCTION);
      SQLName functionName = this.exprParser.name();
      stmt.setName(functionName);
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         this.parserParameters(stmt.getParameters(), stmt);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.WRAPPED)) {
         this.lexer.nextToken();
         int pos = this.lexer.text.indexOf(59, this.lexer.pos());
         if (pos != -1) {
            String wrappedString = this.lexer.subString(this.lexer.pos(), pos - this.lexer.pos());
            stmt.setWrappedSource(wrappedString);
            this.lexer.reset(pos, ';', Token.LITERAL_CHARS);
            this.lexer.nextToken();
            stmt.setAfterSemi(true);
            return stmt;
         } else {
            String wrappedString = this.lexer.text.substring(this.lexer.pos());
            stmt.setWrappedSource(wrappedString);
            this.lexer.reset(this.lexer.text.length(), '\u001a', Token.EOF);
            return stmt;
         }
      } else {
         this.accept(Token.RETURN);
         SQLDataType returnDataType = this.exprParser.parseDataType(false);
         stmt.setReturnDataType(returnDataType);

         while(true) {
            while(!this.identifierEquals("PIPELINED")) {
               if (this.identifierEquals("DETERMINISTIC")) {
                  this.lexer.nextToken();
                  stmt.setDeterministic(true);
               } else {
                  if (!this.identifierEquals("RESULT_CACHE")) {
                     if (this.lexer.identifierEquals(FnvHash.Constants.AUTHID)) {
                        this.lexer.nextToken();
                        String strVal = this.lexer.stringVal();
                        if (this.lexer.identifierEquals(FnvHash.Constants.CURRENT_USER)) {
                           this.lexer.nextToken();
                        } else {
                           this.acceptIdentifier("DEFINER");
                        }

                        SQLName authid = new SQLIdentifierExpr(strVal);
                        stmt.setAuthid(authid);
                     }

                     if (this.identifierEquals("RESULT_CACHE")) {
                        this.lexer.nextToken();
                        stmt.setResultCache(true);
                     }

                     if (this.lexer.token() == Token.SEMI) {
                        this.lexer.nextToken();
                        return stmt;
                     }

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

                     if (this.lexer.identifierEquals("LANGUAGE")) {
                        this.lexer.nextToken();
                        if (this.lexer.identifierEquals("JAVA")) {
                           this.lexer.nextToken();
                           this.acceptIdentifier("NAME");
                           String javaCallSpec = this.lexer.stringVal();
                           this.accept(Token.LITERAL_CHARS);
                           stmt.setJavaCallSpec(javaCallSpec);
                           return stmt;
                        }

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

                     if (this.lexer.identifierEquals("PARALLEL_ENABLE")) {
                        this.lexer.nextToken();
                        stmt.setParallelEnable(true);
                     }

                     if (this.lexer.identifierEquals("AGGREGATE")) {
                        this.lexer.nextToken();
                        stmt.setAggregate(true);
                     }

                     if (this.lexer.token() == Token.USING) {
                        this.lexer.nextToken();
                        SQLName using = this.exprParser.name();
                        stmt.setUsing(using);
                     }

                     SQLStatement block;
                     if (this.lexer.token() == Token.SEMI) {
                        stmt.setAfterSemi(true);
                        this.lexer.nextToken();
                        block = null;
                     } else {
                        block = this.parseBlock();
                     }

                     stmt.setBlock(block);
                     if (this.lexer.identifierEquals(functionName.getSimpleName())) {
                        this.lexer.nextToken();
                     }

                     if (this.lexer.identifierEquals(functionName.getSimpleName())) {
                        this.lexer.nextToken();
                     }

                     return stmt;
                  }

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

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

   private DmExceptionStatement parseException() {
      this.accept(Token.EXCEPTION);
      DmExceptionStatement stmt = new DmExceptionStatement();

      do {
         this.accept(Token.WHEN);
         DmExceptionStatement.Item item = new DmExceptionStatement.Item();
         item.setWhen(this.exprParser.expr());
         this.accept(Token.THEN);
         this.parseStatementList(item.getStatements(), -1, item);
         stmt.addItem(item);
         if (this.lexer.token() == Token.SEMI) {
            this.lexer.nextToken();
         }
      } while(this.lexer.token() == Token.WHEN);

      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("?"));
      }

      if (this.lexer.identifierEquals("CALL")) {
         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;
   }

   private DmLockTableStatement parseLockTable() {
      this.lexer.nextToken();
      this.accept(Token.TABLE);
      DmLockTableStatement stmt = new DmLockTableStatement();
      stmt.setName(this.getExprParser().name());
      this.accept(Token.IN);
      if (this.lexer.identifierEquals("INTENT")) {
         this.lexer.nextToken();
         stmt.setIntent(true);
      }

      if (this.lexer.token() == Token.SHARE) {
         this.lexer.nextToken();
         stmt.setShare(true);
      }

      if (this.lexer.token() == Token.EXCLUSIVE) {
         this.lexer.nextToken();
         stmt.setExclusive(true);
      }

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

      return stmt;
   }

   public SQLUpdateStatement parseUpdateStatement() {
      DmUpdateStatement udpateStatement = new DmUpdateStatement(this.getDbType());
      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.FROM) {
         this.lexer.nextToken();
         SQLTableSource from = this.exprParser.createSelectParser().parseTableSource();
         udpateStatement.setFrom(from);
      }

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

      udpateStatement.setDmReturning(this.parseReturningClause());
      return udpateStatement;
   }

   public DmStatement parseInsert() {
      if (this.lexer.token() == Token.LPAREN) {
         DmInsertStatement stmt = new DmInsertStatement();
         this.parseInsert0(stmt, false);
         stmt.setReturning(this.parseReturningClause());
         stmt.setErrorLogging(this.parseErrorLoggingClause());
         return stmt;
      } else {
         this.accept(Token.INSERT);
         List<SQLHint> hints = new ArrayList();
         this.parseHints(hints);
         if (this.lexer.token() == Token.INTO) {
            DmInsertStatement stmt = new DmInsertStatement();
            stmt.setHints(hints);
            this.parseInsert0(stmt);
            stmt.setReturning(this.parseReturningClause());
            stmt.setErrorLogging(this.parseErrorLoggingClause());
            return stmt;
         } else {
            DmMultiInsertStatement stmt = this.parseMultiInsert();
            stmt.setHints(hints);
            return stmt;
         }
      }
   }

   public DmMultiInsertStatement parseMultiInsert() {
      DmMultiInsertStatement stmt = new DmMultiInsertStatement();
      if (this.lexer.token() == Token.ALL) {
         this.lexer.nextToken();
         stmt.setOption(DmMultiInsertStatement.Option.ALL);
      } else if (this.lexer.token() == Token.FIRST || this.lexer.identifierEquals("FIRST")) {
         this.lexer.nextToken();
         stmt.setOption(DmMultiInsertStatement.Option.FIRST);
      }

      while(this.lexer.token() == Token.INTO) {
         DmMultiInsertStatement.InsertIntoClause clause = new DmMultiInsertStatement.InsertIntoClause();
         boolean acceptSubQuery = stmt.getEntries().size() == 0;
         this.parseInsert0(clause, acceptSubQuery);
         clause.setReturning(this.parseReturningClause());
         clause.setErrorLogging(this.parseErrorLoggingClause());
         stmt.addEntry(clause);
      }

      SQLSelect query = null;
      if (this.lexer.token() == Token.WHEN) {
         DmMultiInsertStatement.ConditionalInsertClause clause = new DmMultiInsertStatement.ConditionalInsertClause();

         while(this.lexer.token() == Token.WHEN) {
            this.lexer.nextToken();
            DmMultiInsertStatement.ConditionalInsertClauseItem item = new DmMultiInsertStatement.ConditionalInsertClauseItem();
            item.setWhen(this.exprParser.expr());
            this.accept(Token.THEN);
            DmMultiInsertStatement.InsertIntoClause insertInto = new DmMultiInsertStatement.InsertIntoClause();
            this.parseInsert0(insertInto);
            item.setThen(insertInto);
            clause.addItem(item);
         }

         if (clause.getItems().size() > 0) {
            DmMultiInsertStatement.ConditionalInsertClauseItem item = (DmMultiInsertStatement.ConditionalInsertClauseItem)clause.getItems().get(clause.getItems().size() - 1);
            DmMultiInsertStatement.InsertIntoClause insert = item.getThen();
            if (insert.getQuery() != null) {
               query = insert.getQuery();
               insert.setQueryNull();
            }
         }

         if (this.lexer.token() == Token.ELSE) {
            this.lexer.nextToken();
            DmMultiInsertStatement.InsertIntoClause insertInto = new DmMultiInsertStatement.InsertIntoClause();
            this.parseInsert0(insertInto, false);
            clause.setElseItem(insertInto);
            if (insertInto.getQuery() != null) {
               query = insertInto.getQuery();
               insertInto.setQueryNull();
            }
         }

         stmt.addEntry(clause);
      }

      if (query == null) {
         SQLSelect subQuery = this.createSQLSelectParser().select();
         stmt.setSubQuery(subQuery);
         subQuery.setParent(stmt);
      } else {
         stmt.setSubQuery(query);
      }

      return stmt;
   }

   public DmReturningClause parseReturningClause() {
      DmReturningClause clause = null;
      if (this.lexer.token() == Token.RETURNING || this.lexer.token() == Token.RETURN) {
         clause = new DmReturningClause();
         clause.setReturnType(this.lexer.token() == Token.RETURNING ? DmReturningClause.ReturnType.RETURNING : DmReturningClause.ReturnType.RETURN);
         this.lexer.nextToken();

         while(true) {
            SQLExpr item = this.exprParser.expr();
            clause.addItem(item);
            if (this.lexer.token() != Token.COMMA) {
               if (this.lexer.token() == Token.INTO) {
                  this.accept(Token.INTO);
                  clause.setIntoType(DmReturningClause.IntoType.INTO);
               } else {
                  this.accept(Token.BULK);
                  this.accept(Token.COLLECT);
                  this.accept(Token.INTO);
                  clause.setIntoType(DmReturningClause.IntoType.BULKCOLLECTINTO);
               }

               while(true) {
                  item = this.exprParser.expr();
                  clause.addValue(item);
                  if (this.lexer.token() != Token.COMMA) {
                     return clause;
                  }

                  this.lexer.nextToken();
               }
            }

            this.lexer.nextToken();
         }
      } else {
         return clause;
      }
   }

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

   protected void parseTable(SQLInsertInto insertStatement) {
      SQLName tableName = this.exprParser.name();
      DmSelectTableReference dmSelectTableReference = new DmSelectTableReference();
      dmSelectTableReference.setExpr(tableName);
      this.parsePartition(dmSelectTableReference);
      insertStatement.setTableSource((SQLExprTableSource)dmSelectTableReference);
   }

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

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

         if (this.lexer.token() == Token.LPAREN) {
            SQLTableSource tableSource = this.createSQLSelectParser().parseTableSource();
            deleteStatement.setTableSource(tableSource);
         } else {
            SQLName tableName = this.exprParser.name();
            deleteStatement.setTableName(tableName);
         }

         deleteStatement.setAlias(this.tableAlias());
      }

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

      return deleteStatement;
   }

   private SQLStatement parseAlterTable() {
      this.lexer.nextToken();
      SQLAlterTableStatement stmt = new SQLAlterTableStatement(this.getDbType());
      stmt.setName(this.exprParser.name());

      while(true) {
         if (this.lexer.identifierEquals(FnvHash.Constants.ADD)) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
               stmt.addItem(item);
               this.accept(Token.RPAREN);
            } else if (this.lexer.token() != Token.CONSTRAINT && this.lexer.token() != Token.FOREIGN && this.lexer.token() != Token.PRIMARY && this.lexer.token() != Token.UNIQUE && this.lexer.token() != Token.CHECK) {
               if (this.lexer.identifierEquals(FnvHash.Constants.SUPPLEMENTAL)) {
                  SQLTableElement element = this.getSQLCreateTableParser().parseCreateTableSupplementalLogingProps();
                  SQLAlterTableAddSupplemental item = new SQLAlterTableAddSupplemental();
                  item.setElement(element);
                  stmt.addItem(item);
               } else if (this.lexer.token() != Token.IDENTIFIER && this.lexer.token() != Token.COLUMN) {
                  if (this.lexer.token() != Token.LITERAL_ALIAS) {
                     if (this.lexer.token() != Token.PARTITION) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     DmAlterTableAddPartition item = new DmAlterTableAddPartition();
                     SQLPartition partition = ((DmExprParser)this.exprParser).parsePartition();
                     item.setPartition(partition);
                     stmt.addItem(item);
                     break;
                  }

                  SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                  stmt.addItem(item);
               } else {
                  if (this.lexer.token() == Token.COLUMN) {
                     this.accept(Token.COLUMN);
                  }

                  Lexer.SavePoint mark = this.lexer.mark();
                  if (this.parseAddIdentity(stmt)) {
                     break;
                  }

                  this.lexer.reset(mark);
                  this.acceptIf(Token.LPAREN);
                  SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                  stmt.addItem(item);
                  this.acceptIf(Token.RPAREN);
               }
            } else {
               SQLConstraint constraint = this.exprParser.parseConstaint();
               DmAlterTableAddConstraint item = new DmAlterTableAddConstraint();
               constraint.setParent(item);
               item.setParent(stmt);
               item.setConstraint(constraint);
               stmt.addItem(item);
               if (this.lexer.token() == Token.CHECK) {
                  this.lexer.nextToken();
                  item.setCheck(true);
               } else if (this.lexer.token() == Token.NOT) {
                  this.lexer.nextToken();
                  this.accept(Token.CHECK);
                  item.setCheck(false);
               }

               if (this.lexer.token() == Token.DISABLE) {
                  this.lexer.nextToken();
                  item.setEnable(false);
               } else if (this.lexer.token() == Token.ENABLE) {
                  this.lexer.nextToken();
                  item.setEnable(true);
               }
            }
         } else {
            if (this.lexer.identifierEquals("REBUILD")) {
               this.acceptIdentifier("REBUILD");
               this.acceptIdentifier("COLUMNS");
               DMSQLAlterTableRebuildColumns item = new DMSQLAlterTableRebuildColumns();
               stmt.addItem(item);
               break;
            }

            if (this.lexer.identifierEquals(FnvHash.Constants.MOVE)) {
               this.lexer.nextToken();
               DmAlterTableMovePartition item = new DmAlterTableMovePartition();
               if (this.lexer.token() == Token.PARTITION) {
                  item.setType(DmAlterTableModifyPartition.Type.PARTITION);
               } else {
                  item.setType(DmAlterTableModifyPartition.Type.SUBPARTITION);
               }

               this.lexer.nextToken();
               item.setName(this.exprParser.name());
               this.accept(Token.TABLESPACE);
               item.setTablespace(this.exprParser.name());
               stmt.addItem(item);
               break;
            }

            if (this.lexer.identifierEquals("RENAME")) {
               stmt.addItem(this.parseAlterTableRename());
               break;
            }

            if (this.lexer.identifierEquals("MODIFY")) {
               if (this.parseModify(stmt)) {
                  break;
               }
            } else if (this.lexer.identifierEquals("SPLIT")) {
               this.parseAlterTableSplit(stmt);
            } else if (this.lexer.token() == Token.TRUNCATE) {
               this.lexer.nextToken();
               if (this.lexer.token() == Token.PARTITION) {
                  this.lexer.nextToken();
                  this.acceptIf(Token.LPAREN);
                  OracleAlterTableTruncatePartition item = new OracleAlterTableTruncatePartition();
                  item.setName(this.exprParser.name());
                  stmt.addItem(item);
                  this.acceptIf(Token.RPAREN);
               } else {
                  if (!this.lexer.identifierEquals("SUBPARTITION")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  this.acceptIf(Token.LPAREN);
                  DmAlterTableTruncateSubpartition item = new DmAlterTableTruncateSubpartition();
                  item.setName(this.exprParser.name());
                  stmt.addItem(item);
                  this.acceptIf(Token.RPAREN);
               }
            } else if (this.lexer.token() == Token.DROP) {
               this.parseAlterDrop(stmt);
            } else {
               if (this.lexer.token() == Token.DISABLE) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.CONSTRAINT) {
                     this.lexer.nextToken();
                     SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
                     item.setConstraintName(this.exprParser.name());
                     stmt.addItem(item);
                     if (this.lexer.identifierEquals(Token.CASCADE.name)) {
                        this.lexer.nextToken();
                        item.setCascade(true);
                     } else if (this.lexer.identifierEquals(Token.RESTRICT.name)) {
                        this.lexer.nextToken();
                        item.setRestrict(true);
                     }
                  } else if (this.lexer.token() == Token.ALL) {
                     this.lexer.nextToken();
                     this.acceptIdentifier("TRIGGERS");
                     SQLAlterTableDisableTriggers item = new SQLAlterTableDisableTriggers();
                     stmt.addItem(item);
                  } else {
                     if (this.lexer.token() != Token.ROW) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     this.acceptIdentifier("MOVEMENT");
                     DmAlterTableRowMovement item = new DmAlterTableRowMovement();
                     item.setEnable(false);
                     stmt.addItem(item);
                  }
                  break;
               }

               if (this.lexer.token() == Token.ENABLE) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.CONSTRAINT) {
                     this.lexer.nextToken();
                     SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
                     item.setConstraintName(this.exprParser.name());
                     stmt.addItem(item);
                     if (this.lexer.token() == Token.CHECK) {
                        this.lexer.nextToken();
                        item.setCheck(true);
                     } else if (this.lexer.token() == Token.NOT) {
                        this.lexer.nextToken();
                        this.accept(Token.CHECK);
                        item.setCheck(false);
                     }
                  } else if (this.lexer.token() == Token.ALL) {
                     this.lexer.nextToken();
                     this.acceptIdentifier("TRIGGERS");
                     SQLAlterTableDisableTriggers item = new SQLAlterTableDisableTriggers();
                     stmt.addItem(item);
                  } else if (this.lexer.token() == Token.ROW) {
                     this.lexer.nextToken();
                     this.acceptIdentifier("MOVEMENT");
                     DmAlterTableRowMovement item = new DmAlterTableRowMovement();
                     item.setEnable(true);
                     stmt.addItem(item);
                  } else {
                     if (this.lexer.token() != Token.USING) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.accept(Token.USING);
                     this.acceptIdentifier("LONG");
                     this.accept(Token.ROW);
                     DmAlterTableEnableUsingLongRow item = new DmAlterTableEnableUsingLongRow();
                     stmt.addItem(item);
                  }
                  break;
               }

               if (this.lexer.token() == Token.ALTER) {
                  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) {
                        break;
                     }

                     this.lexer.nextToken();
                     continue;
                  }

                  if (this.lexer.token() == Token.LITERAL_ALIAS) {
                     SQLAlterTableAlterColumn alterColumn = this.parseAlterColumn();
                     stmt.addItem(alterColumn);
                  } else {
                     SQLAlterTableAlterColumn alterColumn = this.parseAlterColumn();
                     stmt.addItem(alterColumn);
                  }
                  break;
               }

               if (this.lexer.token() != Token.WITH && !this.lexer.identifierEquals(Token.WITHOUT.name)) {
                  if (this.lexer.identifierEquals("EXCHANGE")) {
                     this.lexer.nextToken();
                     DmAlterTableExchangePartition exchangePartition = new DmAlterTableExchangePartition();
                     if (this.lexer.token() == Token.PARTITION) {
                        this.accept(Token.PARTITION);
                        exchangePartition.setType(DmAlterTableModifyPartition.Type.PARTITION);
                     } else {
                        this.acceptIdentifier("SUBPARTITION");
                        exchangePartition.setType(DmAlterTableModifyPartition.Type.SUBPARTITION);
                     }

                     exchangePartition.setName(this.exprParser.name());
                     this.accept(Token.WITH);
                     this.accept(Token.TABLE);
                     exchangePartition.setTableSource(new SQLExprTableSource(this.exprParser.name()));
                     stmt.addItem(exchangePartition);
                  } else if (this.lexer.token() == Token.MERGE) {
                     this.accept(Token.MERGE);
                     this.acceptIdentifier("PARTITIONS");
                     DmAlterTableMergePartition mergePartition = new DmAlterTableMergePartition();
                     this.exprParser.exprList(mergePartition.getNameList(), mergePartition);
                     this.accept(Token.INTO);
                     this.accept(Token.PARTITION);
                     mergePartition.setInto(this.exprParser.name());
                     stmt.addItem(mergePartition);
                  } else if (this.lexer.token() == Token.SET) {
                     DmAlterTableSetPartitionTemplate item = this.parseSetTemplate();
                     stmt.addItem(item);
                  } else if (this.lexer.token() == Token.DEFAULT) {
                     this.lexer.nextToken();
                     this.acceptIdentifier("DIRECTORY");
                     DmAlterTableDefaultDirectory item = new DmAlterTableDefaultDirectory();
                     item.setDirectory(this.exprParser.name());
                     stmt.addItem(item);
                  } else if (this.lexer.identifierEquals("LOCATION")) {
                     this.lexer.nextToken();
                     this.accept(Token.LPAREN);
                     DmAlterTableLocation item = new DmAlterTableLocation();
                     item.setLocation(this.exprParser.name());
                     this.accept(Token.RPAREN);
                     stmt.addItem(item);
                  }
                  break;
               }

               DmAlterTableCounter item = new DmAlterTableCounter();
               if (this.lexer.token() == Token.WITH) {
                  item.setWith(true);
               } else {
                  item.setWith(false);
               }

               this.lexer.nextToken();
               this.acceptIdentifier("COUNTER");
               stmt.addItem(item);
               break;
            }
         }
      }

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

         this.lexer.nextToken();
         this.acceptIdentifier("INDEXES");
         stmt.setUpdateGlobalIndexes(true);
      }

      return stmt;
   }

   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 if (this.lexer.token() != Token.PARTITION && !this.lexer.identifierEquals("SUBPARTITION")) {
         throw new ParserException("TODO " + this.lexer.info());
      } else {
         DmAlterTableRenamePartition item = new DmAlterTableRenamePartition();
         if (this.lexer.token() == Token.PARTITION) {
            item.setType(DmAlterTableModifyPartition.Type.PARTITION);
         } else {
            item.setType(DmAlterTableModifyPartition.Type.SUBPARTITION);
         }

         this.lexer.nextToken();
         item.setName(this.exprParser.name());
         this.accept(Token.TO);
         item.setTo(this.exprParser.name());
         return item;
      }
   }

   private DmAlterTableSetPartitionTemplate parseSetTemplate() {
      this.accept(Token.SET);
      DmAlterTableSetPartitionTemplate item = new DmAlterTableSetPartitionTemplate();
      this.acceptIdentifier("SUBPARTITION");
      this.acceptIdentifier("TEMPLATE");
      if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITIONS)) {
         this.lexer.nextToken();
      }

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

         while(true) {
            SQLSubPartition subPartition = ((DmExprParser)this.exprParser).parseSubPartition();
            subPartition.setParent(item);
            item.getSubPartitionTemplate().add(subPartition);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      } else {
         SQLIntegerExpr countExpr = this.exprParser.integerExpr();
         item.setSubPartitionsCount(countExpr);
         if (this.lexer.token() == Token.STORE) {
            this.lexer.nextToken();
            this.accept(Token.IN);
            this.accept(Token.LPAREN);
            this.exprParser.names(item.getStoreIn(), item);
            this.accept(Token.RPAREN);
         }
      }

      return item;
   }

   private void parseAlterTableSplit(SQLAlterTableStatement stmt) {
      this.lexer.nextToken();
      if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();
         DmAlterTableSplitPartition item = new DmAlterTableSplitPartition();
         item.setName(this.exprParser.name());
         if (this.lexer.identifierEquals("AT")) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            this.exprParser.exprList(item.getAt(), item);
            this.accept(Token.RPAREN);
         } else if (this.lexer.token() == Token.VALUES) {
            this.accept(Token.VALUES);
            this.accept(Token.LPAREN);
            this.exprParser.exprList(item.getValues(), item);
            this.accept(Token.RPAREN);
         }

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

            while(true) {
               SQLPartition partition = this.getExprParser().parsePartition();
               item.getInto().add(partition);
               if (this.lexer.token() != Token.COMMA) {
                  this.accept(Token.RPAREN);
                  break;
               }

               this.lexer.nextToken();
            }
         }

         stmt.addItem(item);
      }

   }

   private boolean parseAddIdentity(SQLAlterTableStatement stmt) {
      if (this.lexer.token() == Token.IDENTIFIER) {
         SQLName name = this.exprParser.name();
         if (this.lexer.identifierEquals(Token.IDENTITY.name)) {
            this.lexer.nextToken();
            DmAlterTableAddIdentity item = new DmAlterTableAddIdentity();
            SQLColumnDefinition.Identity identity = new SQLColumnDefinition.Identity();
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               SQLIntegerExpr seed = (SQLIntegerExpr)this.exprParser.primary();
               this.accept(Token.COMMA);
               SQLIntegerExpr increment = (SQLIntegerExpr)this.exprParser.primary();
               this.accept(Token.RPAREN);
               identity.setSeed((Integer)seed.getNumber());
               identity.setIncrement((Integer)increment.getNumber());
            }

            item.setIdentity(identity);
            item.setName(name);
            stmt.addItem(item);
            return true;
         }
      }

      return false;
   }

   private boolean parseModify(SQLAlterTableStatement stmt) {
      this.lexer.nextToken();
      if (this.lexer.identifierEquals("PATH")) {
         this.lexer.nextToken();
         DmAlterTableModifyConstraint item = new DmAlterTableModifyConstraint();
         item.setExternalTableFilePath(this.exprParser.expr());
         stmt.addItem(item);
         return true;
      } else if (this.lexer.identifierEquals("DISKSPACE")) {
         this.lexer.nextToken();
         DmAlterTableModifyDiskspace item = new DmAlterTableModifyDiskspace();
         if (this.lexer.token() == Token.LIMIT) {
            this.accept(Token.LIMIT);
            item.setSpacelimit(this.lexer.integerValue().intValue());
            this.lexer.nextToken();
         } else {
            this.accept(Token.UNLIMITED);
            item.setUnlimited(true);
         }

         stmt.addItem(item);
         return true;
      } else if (this.lexer.token() == Token.CONSTRAINT) {
         DmAlterTableModifyConstraint item = new DmAlterTableModifyConstraint();
         SQLConstraint constraint = this.exprParser.parseConstaint();
         constraint.setParent(item);
         item.setParent(stmt);
         item.setConstraint(constraint);
         if (this.lexer.token() == Token.CHECK) {
            this.lexer.nextToken();
            item.setCheck(true);
         } else if (this.lexer.token() == Token.NOT) {
            this.lexer.nextToken();
            this.accept(Token.CHECK);
            item.setCheck(false);
         }

         stmt.addItem(item);
         if (this.lexer.identifierEquals(Token.CASCADE.name)) {
            this.lexer.nextToken();
            item.setCascade(true);
         } else if (this.lexer.identifierEquals(Token.RESTRICT.name)) {
            this.lexer.nextToken();
            item.setRestrict(true);
         }

         return true;
      } else if (this.lexer.token() != Token.PARTITION && !this.lexer.identifierEquals("SUBPARTITION")) {
         DmAlterTableModify item = new DmAlterTableModify();
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();

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

               this.lexer.nextToken();
            }
         } else {
            SQLColumnDefinition columnDef = this.exprParser.parseColumn();
            item.addColumn(columnDef);
         }

         stmt.addItem(item);
         return false;
      } else {
         DmAlterTableModifyPartition modifyPartition = new DmAlterTableModifyPartition();
         if (this.lexer.token() == Token.PARTITION) {
            modifyPartition.setType(DmAlterTableModifyPartition.Type.PARTITION);
         } else {
            modifyPartition.setType(DmAlterTableModifyPartition.Type.SUBPARTITION);
         }

         this.lexer.nextToken();
         SQLName name = this.exprParser.name();
         modifyPartition.setName(name);
         if (this.lexer.identifierEquals("ADD")) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("SUBPARTITION")) {
               SQLSubPartition subPartition = ((DmExprParser)this.exprParser).parseSubPartition();
               subPartition.setParent(modifyPartition);
               modifyPartition.setSubPartition(subPartition);
            } else {
               SQLPartitionValue values = ((DmExprParser)this.exprParser).parsePartitionValues();
               modifyPartition.setValues(values);
               modifyPartition.setAction(DmAlterTableModifyPartition.Action.ADD);
            }
         } else if (this.lexer.token() == Token.DROP) {
            this.lexer.nextToken();
            SQLPartitionValue values = this.exprParser.parsePartitionValues();
            modifyPartition.setValues(values);
            modifyPartition.setAction(DmAlterTableModifyPartition.Action.DROP);
         }

         stmt.addItem(modifyPartition);
         return true;
      }
   }

   protected SQLAlterTableAlterColumn parseAlterColumn() {
      if (this.lexer.token() == Token.COLUMN) {
         this.lexer.nextToken();
      }

      SQLColumnDefinition column = this.exprParser.parseColumn();
      DmAlterTableAlterColumn alterColumn = new DmAlterTableAlterColumn();
      alterColumn.setColumn(column);
      if (column.getDataType() == null && column.getConstraints().size() == 0) {
         if (this.lexer.token() == Token.SET) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.NOT) {
               this.lexer.nextToken();
               if (this.lexer.token() == Token.NULL) {
                  this.accept(Token.NULL);
                  alterColumn.setSetNotNull(true);
               } else {
                  this.acceptIdentifier("VISIBLE");
                  alterColumn.setSetNotVisible(true);
               }
            } else if (this.lexer.token() == Token.NULL) {
               this.accept(Token.NULL);
               alterColumn.setSetNull(true);
            } else if (this.lexer.identifierEquals("VISIBLE")) {
               this.acceptIdentifier("VISIBLE");
               alterColumn.setSetVisible(true);
            } else {
               this.accept(Token.DEFAULT);
               SQLExpr defaultValue = this.exprParser.expr();
               alterColumn.setSetDefault(defaultValue);
            }
         } else if (this.lexer.token() == Token.DROP) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.NOT) {
               this.lexer.nextToken();
               this.accept(Token.NULL);
               alterColumn.setDropNotNull(true);
            } else {
               this.accept(Token.DEFAULT);
               alterColumn.setDropDefault(true);
            }
         } else if (this.lexer.identifierEquals("RENAME")) {
            this.lexer.nextToken();
            this.accept(Token.TO);
            alterColumn.setNewColumnname(this.exprParser.name());
         }
      }

      return alterColumn;
   }

   public SQLStatement parseAlter() {
      Lexer.SavePoint mark = this.lexer.mark();
      this.accept(Token.ALTER);
      if (this.lexer.token() == Token.TABLE) {
         return this.parseAlterTable();
      } else 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 if (this.lexer.identifierEquals("COMPILE")) {
            this.lexer.nextToken();
            DmAlterViewStatment stmt = new DmAlterViewStatment();
            stmt.setName(viewName);
            stmt.setCompile(true);
            return stmt;
         } else {
            throw new ParserException("TODO " + this.lexer.info());
         }
      } else if (this.lexer.token() != Token.INDEX && !this.lexer.identifierEquals("CONTEXT")) {
         if (this.lexer.token() == Token.DATABASE) {
            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 if (this.lexer.token() == Token.USER) {
            this.lexer.reset(mark);
            return this.parseAlterUser();
         } else if (this.lexer.token() == Token.TABLESPACE) {
            this.lexer.reset(mark);
            SQLAlterTablespaceStatement stmt = this.parseTablespace();
            return stmt;
         } else if (this.lexer.token() == Token.SEQUENCE) {
            this.lexer.reset(mark);
            return this.parseAlterSequence();
         } else if (this.lexer.token() == Token.SESSION) {
            this.lexer.reset(mark);
            return this.parseAlterSession();
         } else if (this.lexer.identifierEquals("SYSTEM")) {
            this.lexer.reset(mark);
            return this.parseAlterSystem();
         } else if (this.lexer.token() != Token.TRIGGER) {
            if (this.lexer.identifierEquals("PACKAGE")) {
               this.lexer.nextToken();
               DmAlterPackageCompile stmt = new DmAlterPackageCompile();
               stmt.setName(this.exprParser.name());
               this.acceptIdentifier("COMPILE");
               if (this.lexer.identifierEquals("DEBUG")) {
                  this.lexer.nextToken();
                  stmt.setDebug(true);
               }

               return stmt;
            } else {
               throw new ParserException("TODO " + this.lexer.info());
            }
         } else {
            this.lexer.nextToken();
            DmAlterTriggerStatement stmt = new DmAlterTriggerStatement();
            stmt.setName(this.exprParser.name());

            while(true) {
               while(this.lexer.token() != Token.ENABLE) {
                  if (this.lexer.token() == Token.DISABLE) {
                     this.lexer.nextToken();
                     stmt.setEnable(Boolean.FALSE);
                  } else if (this.lexer.identifierEquals("COMPILE")) {
                     this.lexer.nextToken();
                     stmt.setCompile(true);
                  } else {
                     if (!this.lexer.identifierEquals("DEBUG")) {
                        return stmt;
                     }

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

               this.lexer.nextToken();
               stmt.setEnable(Boolean.TRUE);
            }
         }
      } else {
         this.lexer.reset(mark);
         return this.parseAlterIndex();
      }
   }

   private SQLAlterTablespaceStatement parseTablespace() {
      this.accept(Token.ALTER);
      this.accept(Token.TABLESPACE);
      SQLAlterTablespaceStatement stmt = new SQLAlterTablespaceStatement(this.dbType);
      stmt.setName(this.getExprParser().name());
      if (this.lexer.identifierEquals("ONLINE")) {
         this.lexer.nextToken();
         stmt.setStatus(SQLAlterTablespaceStatement.Status.ONLINE);
      } else if (this.lexer.identifierEquals("OFFLINE")) {
         this.lexer.nextToken();
         stmt.setStatus(SQLAlterTablespaceStatement.Status.OFFLINE);
      } else if (this.lexer.identifierEquals("CORRUPT")) {
         this.lexer.nextToken();
         stmt.setStatus(SQLAlterTablespaceStatement.Status.CORRUPT);
      }

      if (this.lexer.identifierEquals("RENAME")) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.TO) {
            this.lexer.nextToken();
            SQLAlterTablespaceRename item = new SQLAlterTablespaceRename();
            item.setName(this.getExprParser().name());
            stmt.setItem(item);
         }

         if (this.lexer.identifierEquals("DATAFILE")) {
            this.lexer.nextToken();
            SQLAlterTablespaceRenameDatafile item = new SQLAlterTablespaceRenameDatafile();

            while(true) {
               item.getFilepaths().add(this.getExprParser().name());
               if (this.lexer.token() != Token.COMMA) {
                  this.accept(Token.TO);

                  while(true) {
                     item.getToFilepaths().add(this.getExprParser().name());
                     if (this.lexer.token() != Token.COMMA) {
                        stmt.setItem(item);
                        return stmt;
                     }

                     this.lexer.nextToken();
                  }
               }

               this.lexer.nextToken();
            }
         }
      } else if (this.lexer.identifierEquals("ADD")) {
         this.lexer.nextToken();
         this.acceptIdentifier("DATAFILE");
         SQLAlterTablespaceAddDatafile item = new SQLAlterTablespaceAddDatafile();

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

            this.accept(Token.COMMA);
         }
      } else if (this.lexer.token() == Token.CACHE) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         SQLAlterTablespaceCache item = new SQLAlterTablespaceCache();
         item.setCacheName(this.exprParser.name());
         stmt.setItem(item);
      } else if (this.lexer.identifierEquals("RESIZE")) {
         this.lexer.nextToken();
         this.acceptIdentifier("DATAFILE");
         SQLAlterTablespaceResizeDatafile item = new SQLAlterTablespaceResizeDatafile();
         item.setFilepath(this.exprParser.name());
         this.accept(Token.TO);
         item.setFilesize(this.exprParser.expr());
         stmt.setItem(item);
      } else if (this.lexer.identifierEquals("DATAFILE")) {
         this.lexer.nextToken();
         SQLAlterTablespaceDatafile item = new SQLAlterTablespaceDatafile();

         while(true) {
            item.getFilepaths().add(this.exprParser.name());
            if (this.lexer.token() != Token.COMMA) {
               if (this.lexer.identifierEquals("autoextend")) {
                  this.acceptIdentifier("autoextend");
                  if (this.lexer.token() == Token.ON) {
                     item.setOn(true);
                     this.accept(Token.ON);
                     if (this.lexer.token() == Token.NEXT) {
                        this.accept(Token.NEXT);
                        item.setNext(this.exprParser.expr());
                     }

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

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

               stmt.setItem(item);
               break;
            }

            this.accept(Token.COMMA);
         }
      }

      return stmt;
   }

   public SQLStatement parseAlterDatabase() {
      this.accept(Token.DATABASE);
      SQLAlterDatabaseStatement stmt = new SQLAlterDatabaseStatement(this.dbType);
      this.parseDatabaseStatus(stmt);
      if (this.lexer.identifierEquals("RESIZE")) {
         DmAlterDatabaseResizeLogfile item = new DmAlterDatabaseResizeLogfile();
         this.lexer.nextToken();
         this.acceptIdentifier("LOGFILE");
         item.setFilepath(this.exprParser.name());
         this.accept(Token.TO);
         item.setFilesize(this.exprParser.expr());
         stmt.setItem(item);
         return stmt;
      } else {
         if (this.lexer.identifierEquals("ADD")) {
            DmAlterDatabaseAddLogfile item = new DmAlterDatabaseAddLogfile();
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("NODE")) {
               this.lexer.nextToken();
               item.setNode(true);
            }

            if (this.lexer.identifierEquals("ARCHIVELOG")) {
               DmAlterDatabaseArchivelog item2 = new DmAlterDatabaseArchivelog();
               item2.setAction(DmAlterDatabaseArchivelog.ACTION.ADD);
               this.acceptIdentifier("ARCHIVELOG");
               item2.setConfigure_clause(this.exprParser.name());
               stmt.setItem(item2);
               return stmt;
            }

            this.acceptIdentifier("LOGFILE");

            while(true) {
               DmLogfile logfile = new DmLogfile();
               logfile.setFilepath(this.exprParser.name());
               this.acceptIdentifier("SIZE");
               logfile.setFilesize(this.exprParser.expr());
               item.getFileItems().add(logfile);
               if (this.lexer.token() != Token.COMMA) {
                  stmt.setItem(item);
                  break;
               }

               this.lexer.nextToken();
            }
         }

         this.parseRenamelogfile(stmt);
         if (this.lexer.identifierEquals("MOVE")) {
            DmAlterDatabaseMoveDatafile item = new DmAlterDatabaseMoveDatafile();
            this.lexer.nextToken();
            this.acceptIdentifier("DATAFILE");
            item.setFilepath(this.exprParser.name());
            this.accept(Token.TO);
            item.setNewFilepath(this.exprParser.name());
            stmt.setItem(item);
            if (this.lexer.identifierEquals("KEEP")) {
               item.setKeep(true);
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("REUSE")) {
               item.setReuse(true);
               this.lexer.nextToken();
            }

            return stmt;
         } else if (this.lexer.token() != Token.DELETE && !this.lexer.identifierEquals("MODIFY")) {
            return stmt;
         } else {
            DmAlterDatabaseArchivelog item = new DmAlterDatabaseArchivelog();
            if (this.lexer.token() == Token.DELETE) {
               item.setAction(DmAlterDatabaseArchivelog.ACTION.DELETE);
            } else {
               item.setAction(DmAlterDatabaseArchivelog.ACTION.MODIFY);
            }

            this.lexer.nextToken();
            this.acceptIdentifier("ARCHIVELOG");
            item.setConfigure_clause(this.exprParser.name());
            stmt.setItem(item);
            return stmt;
         }
      }
   }

   private void parseRenamelogfile(SQLAlterDatabaseStatement stmt) {
      if (this.lexer.identifierEquals("RENAME")) {
         DmAlterDatabaseRenameLogfile item = new DmAlterDatabaseRenameLogfile();
         this.lexer.nextToken();
         this.acceptIdentifier("LOGFILE");

         while(true) {
            item.getFilepathItems().add(this.exprParser.name());
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.TO);

               while(true) {
                  item.getNewFilepathItems().add(this.exprParser.name());
                  if (this.lexer.token() != Token.COMMA) {
                     stmt.setItem(item);
                     return;
                  }

                  this.lexer.nextToken();
               }
            }

            this.lexer.nextToken();
         }
      }
   }

   private void parseDatabaseStatus(SQLAlterDatabaseStatement stmt) {
      DmAlterDatabaseStatus status = new DmAlterDatabaseStatus();
      if (this.lexer.identifierEquals("MOUNT")) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.MOUNT);
         stmt.setItem(status);
      } else if (this.lexer.token() == Token.OPEN) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.OPEN);
         if (this.lexer.identifierEquals("FORCE")) {
            this.lexer.nextToken();
            status.setForce(true);
         }

         stmt.setItem(status);
      } else if (this.lexer.identifierEquals("SUSPEND")) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.SUSPEND);
         stmt.setItem(status);
      } else if (this.lexer.identifierEquals("NORMAL")) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.NORMAL);
         stmt.setItem(status);
      } else if (this.lexer.token() == Token.PRIMARY) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.PRIMARY);
         stmt.setItem(status);
      } else if (this.lexer.identifierEquals("STANDBY")) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.STANDBY);
         stmt.setItem(status);
      } else if (this.lexer.identifierEquals("ARCHIVELOG")) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.ARCHIVELOG);
         if (this.lexer.identifierEquals("CURRENT")) {
            this.lexer.nextToken();
            status.setCurrent(true);
         }

         stmt.setItem(status);
      } else if (this.lexer.identifierEquals("NOARCHIVELOG")) {
         this.lexer.nextToken();
         status.setStatus(DmAlterDatabaseStatus.Status.NOARCHIVELOG);
         stmt.setItem(status);
      }

   }

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

         stmt.addItem(item);
      } else if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
         this.exprParser.names(item.getColumns());
         stmt.addItem(item);
         this.accept(Token.RPAREN);
      } else if (this.lexer.token() == Token.COLUMN) {
         this.lexer.nextToken();
         SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
         this.exprParser.names(item.getColumns());
         if (this.lexer.identifierEquals(Token.CASCADE.name)) {
            this.lexer.nextToken();
            item.setCascade(true);
         } else if (this.lexer.identifierEquals(Token.RESTRICT.name)) {
            this.lexer.nextToken();
            item.setCascade(false);
         }

         stmt.addItem(item);
      } else if (this.lexer.token() == Token.IDENTIFIER) {
         if (this.lexer.identifierEquals(Token.IDENTITY.name)) {
            this.lexer.nextToken();
            DmAlterTableDropIdentity item = new DmAlterTableDropIdentity();
            stmt.addItem(item);
            return;
         }

         if (this.lexer.identifierEquals("SUBPARTITION")) {
            this.lexer.nextToken();
            DmAlterTableDropPartition item = new DmAlterTableDropPartition();
            if (this.lexer.token() == Token.FOR) {
               this.lexer.nextToken();
               this.accept(Token.LPAREN);
               this.exprParser.exprList(item.getItems(), item);
               this.accept(Token.RPAREN);
            } else {
               item.setName(this.exprParser.name());
            }

            item.setType(DmAlterTableModifyPartition.Type.SUBPARTITION);
            stmt.addItem(item);
            return;
         }

         if (this.lexer.token() == Token.PARTITION) {
            this.lexer.nextToken();
            DmAlterTableDropPartition item = new DmAlterTableDropPartition();
            item.setName(this.exprParser.name());
            item.setType(DmAlterTableModifyPartition.Type.PARTITION);
            stmt.addItem(item);
         }

         SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
         this.exprParser.names(item.getColumns());
         if (this.lexer.identifierEquals(Token.CASCADE.name)) {
            this.lexer.nextToken();
            item.setCascade(true);
         } else if (this.lexer.identifierEquals(Token.RESTRICT.name)) {
            this.lexer.nextToken();
            item.setCascade(false);
         }

         stmt.addItem(item);
      } else if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();
         OracleAlterTableDropPartition item = new OracleAlterTableDropPartition();
         item.setName(this.exprParser.name());
         stmt.addItem(item);
      } 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);
      }

   }

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

   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());
      if (this.lexer.token() == Token.DEFAULT) {
         this.accept(Token.DEFAULT);
         this.accept(Token.TABLESPACE);
         stmt.setDefaultTableSpace(this.exprParser.name());
      }

      return stmt;
   }

   public SQLStatement parseAlterUser() {
      this.accept(Token.ALTER);
      this.accept(Token.USER);
      SQLAlterUserStatement stmt = new SQLAlterUserStatement();
      stmt.setUser(this.exprParser.name());
      this.acceptIdentifier("IDENTIFIED");
      this.accept(Token.BY);
      stmt.setPassword(this.exprParser.primary());
      if (this.lexer.token() == Token.DEFAULT) {
         this.accept(Token.DEFAULT);
         this.accept(Token.TABLESPACE);
         stmt.setDefaultTableSpace(this.exprParser.name());
      }

      return stmt;
   }

   public SQLDropUserStatement parseDropUser() {
      this.accept(Token.USER);
      DmDropUserStatement stmt = new DmDropUserStatement(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.identifierEquals(Token.CASCADE.name)) {
               this.lexer.nextToken();
               stmt.setCascade(true);
            } else if (this.lexer.identifierEquals(Token.RESTRICT.name)) {
               this.lexer.nextToken();
               stmt.setRestrict(true);
            }

            return stmt;
         }

         this.lexer.nextToken();
      }
   }

   public DmCreateSchemaStatement parseCreateSchema() {
      this.accept(Token.CREATE);
      this.accept(Token.SCHEMA);
      DmCreateSchemaStatement stmt = new DmCreateSchemaStatement(this.dbType);
      if (this.lexer.token() == Token.IDENTIFIER) {
         if (this.lexer.identifierEquals("AUTHORIZATION")) {
            this.lexer.nextToken();
            stmt.setAuthorization(true);
            SQLIdentifierExpr userName = (SQLIdentifierExpr)this.exprParser.expr();
            stmt.setUserName(userName);
         } else {
            SQLIdentifierExpr schemaName = (SQLIdentifierExpr)this.exprParser.expr();
            stmt.setSchemaName(schemaName);
            if (this.lexer.identifierEquals("AUTHORIZATION")) {
               this.lexer.nextToken();
               stmt.setAuthorization(true);
               SQLIdentifierExpr userName = (SQLIdentifierExpr)this.exprParser.expr();
               stmt.setUserName(userName);
            }
         }

         this.parseStatementList(stmt.getStmtList(), -1, (SQLObject)null);
         return stmt;
      } else {
         throw new ParserException("TODO " + this.lexer.info());
      }
   }

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

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

      SQLName name = this.exprParser.name();
      stmt.addTableSource(name);
      if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();
         this.acceptIf(Token.LPAREN);
         stmt.setPartitionName(this.exprParser.name());
         this.acceptIf(Token.RPAREN);
      }

      return stmt;
   }

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

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

      if (this.lexer.identifierEquals("CONTEXT")) {
         this.lexer.nextToken();
         stmt.setContext(true);
      }

      if (this.lexer.identifierEquals("CLUSTER")) {
         this.lexer.nextToken();
         stmt.setCluster(true);
      }

      if (this.lexer.token() == Token.NOT) {
         this.accept(Token.NOT);
         this.acceptIdentifier("PARTIAL");
         stmt.setNotPartial(true);
      }

      if (this.lexer.token() == Token.UNIQUE) {
         stmt.setType("UNIQUE");
         this.lexer.nextToken();
      } else if (this.lexer.identifierEquals("BITMAP")) {
         stmt.setType("BITMAP");
         this.lexer.nextToken();
      } else if (this.lexer.identifierEquals("SPATIAL")) {
         stmt.setType("SPATIAL");
         this.lexer.nextToken();
      }

      this.accept(Token.INDEX);
      stmt.setName(this.exprParser.name());
      this.accept(Token.ON);
      if (this.lexer.identifierEquals("CLUSTER")) {
         this.lexer.nextToken();
         stmt.setCluster(true);
      }

      stmt.setTable(this.exprParser.name());
      if (this.lexer.token() == Token.IDENTIFIER) {
         String alias = this.lexer.stringVal();
         stmt.getTable().setAlias(alias);
         this.lexer.nextToken();
      }

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

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

            this.lexer.nextToken();
         }
      }

      while(true) {
         ((DmExprParser)this.getExprParser()).parseSegmentAttributes(stmt);
         if (this.lexer.identifierEquals("ONLINE")) {
            this.lexer.nextToken();
            stmt.setOnline(true);
         } else if (this.lexer.identifierEquals("NOSORT")) {
            this.lexer.nextToken();
            stmt.setSort(Boolean.FALSE);
         } else if (this.lexer.identifierEquals("REVERSE")) {
            this.lexer.nextToken();
            stmt.setReverse(true);
         } else {
            if (!this.lexer.identifierEquals("GLOBAL")) {
               if (this.lexer.identifierEquals("LEXER")) {
                  this.lexer.nextToken();
                  stmt.setLexer(this.exprParser.name());
               }

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

               return stmt;
            }

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

   protected SQLStatement parseAlterIndex() {
      this.accept(Token.ALTER);
      DmAlterIndexStatement stmt = new DmAlterIndexStatement();
      if (this.lexer.identifierEquals("CONTEXT")) {
         this.lexer.nextToken();
         stmt.setContext(true);
      }

      this.lexer.nextToken();
      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.identifierEquals("INVISIBLE")) {
         this.lexer.nextToken();
         stmt.setVisible(false);
      }

      if (this.lexer.identifierEquals("VISIBLE")) {
         this.lexer.nextToken();
         stmt.setVisible(true);
      }

      if (this.lexer.identifierEquals("UNUSABLE")) {
         this.lexer.nextToken();
         stmt.setUnusable(true);
      }

      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);
      }

      while(true) {
         while(this.lexer.identifierEquals("rebuild")) {
            this.lexer.nextToken();
            SQLAlterIndexStatement.Rebuild rebuild = new SQLAlterIndexStatement.Rebuild();
            stmt.setRebuild(rebuild);
            if (this.lexer.identifierEquals("NOSORT")) {
               rebuild.setOption(this.exprParser.expr());
            } else if (this.lexer.identifierEquals("ONLINE")) {
               rebuild.setOption(this.exprParser.expr());
            }
         }

         if (!this.lexer.identifierEquals("INCREMENT")) {
            if (!this.lexer.identifierEquals("OPTIMIZE")) {
               if (!this.lexer.identifierEquals("MONITORING")) {
                  if (!this.lexer.identifierEquals("NOMONITORING")) {
                     if (this.lexer.identifierEquals("PARALLEL")) {
                        this.lexer.nextToken();
                        stmt.setParallel(this.exprParser.expr());
                        break;
                     }

                     if (!this.lexer.identifierEquals("ONLINE")) {
                        break;
                     }

                     this.lexer.nextToken();
                     stmt.setOnline(true);
                  } else {
                     this.lexer.nextToken();
                     this.acceptIdentifier("USAGE");
                     stmt.setNoMonitoringUsage(Boolean.TRUE);
                  }
               } else {
                  this.lexer.nextToken();
                  this.acceptIdentifier("USAGE");
                  stmt.setMonitoringUsage(Boolean.TRUE);
               }
            } else {
               this.lexer.nextToken();
               stmt.setOptimize(Boolean.TRUE);
            }
         } else {
            this.lexer.nextToken();
            stmt.setIncrement(Boolean.TRUE);
         }
      }

      if (this.lexer.identifierEquals("LEXER")) {
         this.lexer.nextToken();
         stmt.setLexer(this.exprParser.name());
      }

      return stmt;
   }

   public SQLStatement parseDropIndex() {
      this.acceptIf(Token.DROP);
      SQLDropIndexStatement stmt = new SQLDropIndexStatement(this.getDbType());
      if (this.lexer.identifierEquals("CONTEXT")) {
         this.lexer.nextToken();
         stmt.setContext(true);
      }

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

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

      return stmt;
   }

   public SQLStatement parseCommit() {
      this.accept(Token.COMMIT);
      SQLCommitStatement stmt = new SQLCommitStatement();
      if (this.lexer.identifierEquals("WORK")) {
         this.acceptIdentifier("WORK");
         stmt.setWork(true);
      }

      return stmt;
   }

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

   public SQLStatement parseSet() {
      boolean isSet = false;
      if (this.lexer.token() == Token.SET) {
         this.accept(Token.SET);
         isSet = true;
      }

      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 if (this.lexer.identifierEquals("TRANSACTION")) {
         this.lexer.nextToken();
         DmSetTransactionStatement stmt = new DmSetTransactionStatement();
         if (this.lexer.identifierEquals("READ")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.ONLY) {
               this.lexer.nextToken();
               stmt.setReadOnly(true);
            } else {
               this.acceptIdentifier("WRITE");
               stmt.setReadWrite(true);
            }
         } else {
            this.acceptIdentifier("ISOLATION");
            this.acceptIdentifier("LEVEL");
            if (this.lexer.identifierEquals("READ")) {
               this.lexer.nextToken();
               if (this.lexer.identifierEquals("COMMITTED")) {
                  this.lexer.nextToken();
                  stmt.setLevel(DmSetTransactionStatement.Level.READ_COMMITTED);
               } else {
                  this.acceptIdentifier("UNCOMMITTED");
                  stmt.setLevel(DmSetTransactionStatement.Level.READ_UNCOMMITTED);
               }
            } else {
               this.acceptIdentifier("SERIALIZABLE");
               stmt.setLevel(DmSetTransactionStatement.Level.SERIALIZABLE);
            }
         }

         return stmt;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.IDENTITY_INSERT)) {
         SQLSetStatement stmt = new SQLSetStatement(DbType.dm);
         stmt.setOption(SQLSetStatement.Option.IDENTITY_INSERT);
         this.lexer.nextToken();
         SQLName table = this.exprParser.name();
         if (this.lexer.token() == Token.ON) {
            SQLExpr value = new SQLIdentifierExpr("ON");
            stmt.set(table, value);
            this.lexer.nextToken();
         } else if (this.lexer.identifierEquals(FnvHash.Constants.OFF)) {
            SQLExpr value = new SQLIdentifierExpr("OFF");
            stmt.set(table, value);
            this.lexer.nextToken();
         }

         return stmt;
      } else {
         SQLSetStatement stmt = new SQLSetStatement(this.getDbType());
         stmt.putAttribute("parser.set", isSet);
         this.parseAssignItems(stmt.getItems(), stmt);
         return stmt;
      }
   }

   public SQLStatement parseIf() {
      this.accept(Token.IF);
      SQLIfStatement stmt = new SQLIfStatement();
      stmt.setDbType(this.dbType);
      stmt.setCondition(this.exprParser.expr());
      this.accept(Token.THEN);
      this.parseStatementList(stmt.getStatements(), -1, stmt);

      while(this.lexer.token() == Token.ELSIF) {
         this.lexer.nextToken();
         SQLIfStatement.ElseIf elseIf = new SQLIfStatement.ElseIf();
         elseIf.setCondition(this.exprParser.expr());
         elseIf.setParent(stmt);
         this.accept(Token.THEN);
         this.parseStatementList(elseIf.getStatements(), -1, stmt);
         stmt.getElseIfList().add(elseIf);
      }

      if (this.lexer.token() == Token.ELSE) {
         this.lexer.nextToken();
         SQLIfStatement.Else elseItem = new SQLIfStatement.Else();
         this.parseStatementList(elseItem.getStatements(), -1, elseItem);
         stmt.setElseItem(elseItem);
      }

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

   public SQLStatement parseCreateTrigger() {
      DmCreateTriggerStatement stmt = new DmCreateTriggerStatement();
      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.token() == Token.WITH) {
         this.lexer.nextToken();
         this.acceptIdentifier("ENCRYPTION");
         stmt.setWithEncryption(true);
      }

      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);
      }

      boolean isTimer = false;

      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);
         } else if (!this.lexer.identifierEquals("DDL") && this.lexer.token() != Token.CREATE && this.lexer.token() != Token.ALTER && this.lexer.token() != Token.DROP && this.lexer.token() != Token.GRANT && this.lexer.token() != Token.REVOKE && this.lexer.token() != Token.TRUNCATE && this.lexer.token() != Token.COMMENT && !this.lexer.identifierEquals("LOGIN") && !this.lexer.identifierEquals("LOGON") && !this.lexer.identifierEquals("LOGOUT") && !this.lexer.identifierEquals("LOGOFF") && !this.lexer.identifierEquals("SERERR") && !this.lexer.identifierEquals("AUDIT") && !this.lexer.identifierEquals("NOAUDIT") && !this.lexer.identifierEquals("TIMER") && !this.lexer.identifierEquals("STARTUP") && !this.lexer.identifierEquals("SHUTDOWN")) {
            if (this.lexer.identifierEquals("BACKUP")) {
               this.lexer.nextToken();
               this.accept(Token.DATABASE);
               SQLIdentifierExpr identifierExpr = new SQLIdentifierExpr("BACKUP DATABASE");
               stmt.getEvents().add(identifierExpr);
            } else if (this.lexer.identifierEquals("RESTORE")) {
               this.lexer.nextToken();
               this.accept(Token.DATABASE);
               SQLIdentifierExpr identifierExpr = new SQLIdentifierExpr("RESTORE DATABASE");
               stmt.getEvents().add(identifierExpr);
            }
         } else {
            if (this.lexer.identifierEquals("TIMER")) {
               isTimer = true;
            }

            SQLIdentifierExpr identifierExpr = new SQLIdentifierExpr(this.lexer.stringVal());
            this.lexer.nextToken();
            stmt.getEvents().add(identifierExpr);
         }

         if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.OR) {
            if (this.lexer.identifierEquals("LOCAL")) {
               this.acceptIdentifier("LOCAL");
               stmt.setLocal(true);
            }

            this.accept(Token.ON);
            if (this.lexer.token() == Token.DATABASE) {
               this.lexer.nextToken();
               stmt.setDatabase(true);
            } else if (this.lexer.token() == Token.SCHEMA) {
               this.lexer.nextToken();
               stmt.setSchema(true);
            } else {
               SQLName name = this.exprParser.name();
               if (name.toString().endsWith(".SCHEMA")) {
                  SQLName schema = new SQLIdentifierExpr(name.toString().split("\\.")[0]);
                  stmt.setSchemaName(schema);
                  stmt.setSchema(true);
               } else {
                  stmt.setOn(name);
               }
            }

            if (this.lexer.identifierEquals("REFERENCING")) {
               this.lexer.nextToken();
               this.parseReferencing(stmt);
               this.parseReferencing(stmt);
            }

            if (isTimer) {
               this.parseTimer(stmt);
            } else if (this.lexer.token() == Token.FOR) {
               this.lexer.nextToken();
               this.acceptIdentifier("EACH");
               if (this.lexer.token() == Token.ROW) {
                  this.accept(Token.ROW);
                  stmt.setForEachRow(true);
               } else {
                  this.acceptIdentifier("STATEMENT");
                  stmt.setForEachStatement(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();
      }
   }

   private void parseTimer(DmCreateTriggerStatement stmt) {
      DmCreateTriggerStatement.Timer timer = new DmCreateTriggerStatement.Timer();
      if (this.lexer.token() == Token.FOR) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("ONCE")) {
            this.acceptIdentifier("ONCE");
            this.acceptIdentifier("AT");
            this.acceptIdentifier("DATETIME");
            SQLExpr datetime = this.exprParser.expr();
            timer.setType(DmCreateTriggerStatement.Timer.Type.ONCE);
            timer.setDatetime(datetime);
            stmt.setTimer(timer);
         } else {
            this.acceptIdentifier("EACH");
            SQLExpr rate = this.exprParser.expr();
            timer.setType(DmCreateTriggerStatement.Timer.Type.RATE);
            if (this.lexer.identifierEquals("MONTH")) {
               this.lexer.nextToken();
               timer.setMonth_rate(rate);
               this.acceptIdentifier("DAY");
               SQLExpr day = this.exprParser.expr();
               if (this.lexer.token() == Token.OF) {
                  DmCreateTriggerStatement.Timer.DayInMonthWeek dayInMonthWeek = new DmCreateTriggerStatement.Timer.DayInMonthWeek();
                  this.lexer.nextToken();
                  this.acceptIdentifier("WEEK");
                  SQLExpr week = this.exprParser.expr();
                  dayInMonthWeek.setDay(day);
                  dayInMonthWeek.setWeek(week);
                  timer.setDay_in_month_week(dayInMonthWeek);
               } else {
                  timer.setDay_in_month(day);
               }
            } else if (this.lexer.identifierEquals("WEEK")) {
               this.lexer.nextToken();
               timer.setWeek_rate(rate);

               while(true) {
                  timer.getDay_of_week_list().add(this.exprParser.expr());
                  if (this.lexer.token() != Token.COMMA) {
                     break;
                  }

                  this.lexer.nextToken();
               }
            } else if (this.lexer.identifierEquals("DAY")) {
               this.lexer.nextToken();
               timer.setDay_rate(rate);
            }

            if (this.lexer.identifierEquals("AT")) {
               this.lexer.nextToken();
               this.acceptIdentifier("TIME");
               timer.setOnce_in_day(this.exprParser.expr());
            } else if (this.lexer.token() == Token.FROM) {
               this.lexer.nextToken();
               this.acceptIdentifier("TIME");
               DmCreateTriggerStatement.Timer.DuaringTime duaringTime = new DmCreateTriggerStatement.Timer.DuaringTime();
               duaringTime.setFrom(this.exprParser.expr());
               if (this.lexer.token() == Token.TO) {
                  this.lexer.nextToken();
                  this.acceptIdentifier("TIME");
                  duaringTime.setTo(this.exprParser.expr());
               }

               timer.setDuaringTime(duaringTime);
               this.accept(Token.FOR);
               this.acceptIdentifier("EACH");
               timer.setTimes_in_day(this.exprParser.expr());
               this.acceptIdentifier("MINUTE");
            } else if (this.lexer.token() == Token.FOR) {
               this.lexer.nextToken();
               this.acceptIdentifier("EACH");
               timer.setTimes_in_day(this.exprParser.expr());
               this.acceptIdentifier("MINUTE");
            }

            if (this.lexer.token() == Token.FROM) {
               this.lexer.nextToken();
               this.acceptIdentifier("DATETIME");
               DmCreateTriggerStatement.Timer.DuaringDate duaringDate = new DmCreateTriggerStatement.Timer.DuaringDate();
               duaringDate.setFrom(this.exprParser.expr());
               if (this.lexer.token() == Token.TO) {
                  this.lexer.nextToken();
                  this.acceptIdentifier("DATETIME");
                  duaringDate.setTo(this.exprParser.expr());
               }

               timer.setDuaringDate(duaringDate);
            }

            stmt.setTimer(timer);
         }

         if (this.lexer.identifierEquals("EXECUTE")) {
            this.lexer.nextToken();
            this.acceptIdentifier("AT");
            timer.setExec_ep_seqno(this.exprParser.expr());
         }
      }

   }

   private void parseReferencing(DmCreateTriggerStatement stmt) {
      if (this.lexer.identifierEquals("NEW")) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.ROW) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.AS) {
               this.lexer.nextToken();
            }
         }

         stmt.setNewRow(this.exprParser.name());
      } else if (this.lexer.identifierEquals("OLD")) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.ROW) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.AS) {
               this.lexer.nextToken();
            }
         }

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

   }

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

      if (this.lexer.identifierEquals("PUBLIC")) {
         this.lexer.nextToken();
         stmt.setPublic(true);
      }

      this.acceptIdentifier("SYNONYM");
      stmt.setName(this.exprParser.name());
      this.accept(Token.FOR);
      stmt.setObject(this.exprParser.name());
      return stmt;
   }

   public SQLStatement parseBlock() {
      SQLBlockStatement block = new SQLBlockStatement();
      block.setDbType(DbType.dm);
      Lexer.SavePoint savePoint = this.lexer.mark();
      if (this.lexer.token() == Token.DECLARE) {
         this.lexer.nextToken();
      }

      if (this.lexer.token() == Token.IDENTIFIER || this.lexer.token() == Token.CURSOR) {
         this.parserParameters(block.getParameters(), block);

         for(SQLParameter param : block.getParameters()) {
            param.setParent(block);
         }
      }

      if (this.lexer.token() != Token.PROCEDURE) {
         if (this.lexer.token() == Token.FUNCTION) {
            if (savePoint.token == Token.DECLARE) {
               this.lexer.reset(savePoint);
            }

            return this.parseCreateFunction();
         } else {
            this.accept(Token.BEGIN);
            this.parseStatementList(block.getStatementList(), -1, block);
            this.accept(Token.END);
            Token token = this.lexer.token();
            if (token == Token.EOF) {
               return block;
            } else {
               if (token != Token.SEMI) {
                  String endLabel = this.lexer.stringVal();
                  this.accept(Token.IDENTIFIER);
                  block.setEndLabel(endLabel);
               }

               this.accept(Token.SEMI);
               return block;
            }
         }
      } else {
         SQLCreateProcedureStatement stmt = this.parseCreateProcedure();

         for(SQLParameter param : block.getParameters()) {
            param.setParent(stmt);
            stmt.getParameters().add(param);
         }

         return stmt;
      }
   }

   private void parserParameters(List<SQLParameter> parameters, SQLObject parent) {
      Token token;
      do {
         SQLParameter parameter = new SQLParameter();
         parameter.setParent(parent);
         if (parent instanceof OracleCreateTypeStatement) {
            if (this.lexer.identifierEquals(FnvHash.Constants.MAP)) {
               this.lexer.nextToken();
               parameter.setMap(true);
            } else if (this.lexer.token() == Token.ORDER) {
               this.lexer.nextToken();
               parameter.setOrder(true);
            }
         }

         SQLDataType dataType = null;
         SQLName name;
         if (this.lexer.token() == Token.CURSOR) {
            this.lexer.nextToken();
            dataType = new SQLDataTypeImpl();
            dataType.setName("CURSOR");
            name = this.exprParser.name();
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               this.parserParameters(parameter.getCursorParameters(), parameter);
               this.accept(Token.RPAREN);
            }

            this.accept(Token.IS);
            SQLSelect select = this.createSQLSelectParser().select();
            parameter.setDefaultValue(new SQLQueryExpr(select));
         } else {
            if (this.lexer.token() == Token.PROCEDURE || this.lexer.token() == Token.END || this.lexer.token() == Token.TABLE) {
               return;
            }

            if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
               this.lexer.nextToken();
               name = this.exprParser.name();
               this.accept(Token.IS);
               if (this.lexer.identifierEquals("REF")) {
                  this.lexer.nextToken();
                  this.accept(Token.CURSOR);
                  dataType = new SQLDataTypeImpl("REF CURSOR");
                  dataType.setDbType(this.dbType);
               } else if (this.lexer.token() == Token.TABLE) {
                  this.lexer.nextToken();
                  this.accept(Token.OF);
                  name = this.exprParser.name();
                  if (this.lexer.token() == Token.PERCENT) {
                     this.lexer.nextToken();
                     String typeName;
                     if (this.lexer.identifierEquals(FnvHash.Constants.ROWTYPE)) {
                        this.lexer.nextToken();
                        typeName = "TABLE OF " + name.toString() + "%ROWTYPE";
                     } else {
                        this.acceptIdentifier("TYPE");
                        typeName = "TABLE OF " + name.toString() + "%TYPE";
                     }

                     dataType = new SQLDataTypeImpl(typeName);
                  } else if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     String typeName = name.toString();
                     SQLIntegerExpr lenExpr = (SQLIntegerExpr)this.exprParser.expr();
                     int len = lenExpr.getNumber().intValue();
                     dataType = new SQLDataTypeImpl(typeName, len);
                     this.accept(Token.RPAREN);
                     if (this.lexer.token() == Token.INDEX) {
                        this.lexer.nextToken();
                        this.accept(Token.BY);
                        SQLExpr indexBy = this.exprParser.primary();
                        ((SQLDataTypeImpl)dataType).setIndexBy(indexBy);
                     }
                  }

                  dataType.setDbType(this.dbType);
               } else {
                  if (!this.lexer.identifierEquals("VARRAY")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  this.accept(Token.LPAREN);
                  int len = this.exprParser.acceptInteger();
                  this.accept(Token.RPAREN);
                  this.accept(Token.OF);
                  if (this.lexer.identifierEquals("NUMBER")) {
                     this.lexer.nextToken();
                     String typeName = "VARRAY(" + len + ") OF NUMBER";
                     if (this.lexer.token() == Token.LPAREN) {
                        this.accept(Token.LPAREN);
                        int numLen = this.exprParser.acceptInteger();
                        this.accept(Token.RPAREN);
                        typeName = typeName + "(" + numLen + ")";
                     }

                     dataType = new SQLDataTypeImpl(typeName);
                     dataType.setDbType(this.dbType);
                  } else {
                     if (!this.lexer.identifierEquals("VARCHAR2")) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     String typeName = "VARRAY(" + len + ") OF VARCHAR2";
                     dataType = new SQLDataTypeImpl(typeName);
                     dataType.setDbType(this.dbType);
                     if (this.lexer.token() == Token.LPAREN) {
                        this.lexer.nextToken();
                        this.exprParser.exprList(dataType.getArguments(), dataType);
                        this.accept(Token.RPAREN);
                     }
                  }
               }
            } else {
               if (this.lexer.token() == Token.KEY) {
                  name = new SQLIdentifierExpr(this.lexer.stringVal());
                  this.lexer.nextToken();
               } else {
                  name = this.exprParser.name();
               }

               if (this.lexer.token() == Token.IN) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.OUT) {
                     this.lexer.nextToken();
                     parameter.setParamType(SQLParameter.ParameterType.INOUT);
                  } else {
                     parameter.setParamType(SQLParameter.ParameterType.IN);
                  }
               } else if (this.lexer.token() == Token.OUT) {
                  this.lexer.nextToken();
                  parameter.setParamType(SQLParameter.ParameterType.OUT);
               } else if (this.lexer.token() == Token.INOUT) {
                  this.lexer.nextToken();
                  parameter.setParamType(SQLParameter.ParameterType.INOUT);
               }

               if (this.lexer.identifierEquals("NOCOPY")) {
                  this.lexer.nextToken();
                  parameter.setNoCopy(true);
               }

               if (this.lexer.identifierEquals("CONSTANT")) {
                  this.lexer.nextToken();
                  parameter.setConstant(true);
               }

               if ((name.nameHashCode64() == FnvHash.Constants.MEMBER || name.nameHashCode64() == FnvHash.Constants.STATIC) && this.lexer.token() == Token.FUNCTION) {
                  if (name.nameHashCode64() == FnvHash.Constants.MEMBER) {
                     parameter.setMember(true);
                  }

                  OracleFunctionDataType functionDataType = new OracleFunctionDataType();
                  functionDataType.setStatic(name.nameHashCode64() == FnvHash.Constants.STATIC);
                  this.lexer.nextToken();
                  functionDataType.setName(this.lexer.stringVal());
                  this.accept(Token.IDENTIFIER);
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     this.parserParameters(functionDataType.getParameters(), functionDataType);
                     this.accept(Token.RPAREN);
                  }

                  this.accept(Token.RETURN);
                  functionDataType.setReturnDataType(this.exprParser.parseDataType(false));
                  dataType = functionDataType;
                  name = null;
                  if (this.lexer.token() == Token.IS) {
                     this.lexer.nextToken();
                     SQLStatement block = this.parseBlock();
                     functionDataType.setBlock(block);
                  }
               } else if ((name.nameHashCode64() == FnvHash.Constants.MEMBER || name.nameHashCode64() == FnvHash.Constants.STATIC) && this.lexer.token() == Token.PROCEDURE) {
                  if (name.nameHashCode64() == FnvHash.Constants.MEMBER) {
                     parameter.setMember(true);
                  }

                  OracleProcedureDataType procedureDataType = new OracleProcedureDataType();
                  procedureDataType.setStatic(name.nameHashCode64() == FnvHash.Constants.STATIC);
                  this.lexer.nextToken();
                  procedureDataType.setName(this.lexer.stringVal());
                  this.accept(Token.IDENTIFIER);
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     this.parserParameters(procedureDataType.getParameters(), procedureDataType);
                     this.accept(Token.RPAREN);
                  }

                  dataType = procedureDataType;
                  name = null;
                  if (this.lexer.token() == Token.IS) {
                     this.lexer.nextToken();
                     SQLStatement block = this.parseBlock();
                     procedureDataType.setBlock(block);
                  }
               } else {
                  dataType = this.exprParser.parseDataType(false);
               }

               if (this.lexer.token() == Token.COLONEQ || this.lexer.token() == Token.DEFAULT || this.lexer.token() == Token.FOR) {
                  this.lexer.nextToken();
                  parameter.setDefaultValue(this.exprParser.expr());
               }
            }
         }

         parameter.setName(name);
         parameter.setDataType(dataType);
         parameters.add(parameter);
         token = this.lexer.token();
         if (token == Token.COMMA || token == Token.SEMI || token == Token.IS) {
            this.lexer.nextToken();
         }

         token = this.lexer.token();
      } while(token != Token.BEGIN && token != Token.RPAREN && token != Token.EOF && token != Token.FUNCTION && !this.lexer.identifierEquals("DETERMINISTIC"));

   }

   public SQLStatement parseWhile() {
      this.accept(Token.WHILE);
      SQLWhileStatement stmt = new SQLWhileStatement();
      stmt.setDbType(this.dbType);
      stmt.setCondition(this.exprParser.expr());
      this.accept(Token.LOOP);
      this.parseStatementList(stmt.getStatements(), -1, stmt);
      this.accept(Token.END);
      this.accept(Token.LOOP);
      if (this.lexer.token() == Token.IDENTIFIER) {
         String label = this.lexer.stringVal();
         stmt.setLabelName(label);
         this.lexer.nextToken();
      }

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

   private DmExitStatement parseExit() {
      DmExitStatement stmt = new DmExitStatement();
      if (this.lexer.token() == Token.IDENTIFIER) {
         String label = this.lexer.stringVal();
         stmt.setLabel(label);
         this.lexer.nextToken();
      }

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

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

   public SQLStatement parseExecute() {
      this.acceptIdentifier("EXECUTE");
      if (this.lexer.token() == Token.IMMEDIATE) {
         this.lexer.nextToken();
         DmExecuteImmediateStatement stmt = new DmExecuteImmediateStatement();
         SQLExpr dyanmiacSql = this.exprParser.expr();
         stmt.setDynamicSql(dyanmiacSql);
         if (this.lexer.token() == Token.INTO) {
            this.lexer.nextToken();
            this.exprParser.exprList(stmt.getInto(), stmt);
         }

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

            while(true) {
               SQLArgument arg = new SQLArgument();
               if (this.lexer.token() == Token.IN) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.OUT) {
                     this.lexer.nextToken();
                     arg.setType(SQLParameter.ParameterType.INOUT);
                  } else {
                     arg.setType(SQLParameter.ParameterType.IN);
                  }
               } else if (this.lexer.token() == Token.OUT) {
                  this.lexer.nextToken();
                  arg.setType(SQLParameter.ParameterType.OUT);
               }

               arg.setExpr(this.exprParser.primary());
               arg.setParent(stmt);
               stmt.getArguments().add(arg);
               if (this.lexer.token() != Token.COMMA) {
                  break;
               }

               this.lexer.nextToken();
            }
         }

         if (this.lexer.token() == Token.RETURNING) {
            this.lexer.nextToken();
            this.accept(Token.INTO);
            this.exprParser.exprList(stmt.getReturnInto(), stmt);
         }

         return stmt;
      } else {
         throw new ParserException("TODO : " + this.lexer.info());
      }
   }

   public SQLStatement parseRaise() {
      this.lexer.nextToken();
      DmRaiseStatement stmt = new DmRaiseStatement();
      if (this.lexer.token() != Token.SEMI) {
         stmt.setException(this.exprParser.expr());
      }

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

   public SQLCreateProcedureStatement parseCreateProcedure() {
      SQLCreateProcedureStatement stmt = new SQLCreateProcedureStatement();
      stmt.setDbType(this.dbType);
      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);
         }
      } else {
         stmt.setCreate(false);
      }

      this.accept(Token.PROCEDURE);
      SQLName procedureName = this.exprParser.name();
      stmt.setName(procedureName);
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.RPAREN) {
            this.accept(Token.RPAREN);
         } else {
            this.parserParameters(stmt.getParameters(), stmt);
            this.accept(Token.RPAREN);
         }
      }

      if (this.lexer.identifierEquals("AUTHID")) {
         this.lexer.nextToken();
         String strVal = this.lexer.stringVal();
         if (this.lexer.identifierEquals("CURRENT_USER")) {
            this.lexer.nextToken();
         } else {
            this.acceptIdentifier("DEFINER");
         }

         SQLName authid = new SQLIdentifierExpr(strVal);
         stmt.setAuthid(authid);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.WRAPPED)) {
         this.lexer.nextToken();
         int pos = this.lexer.text.indexOf(59, this.lexer.pos());
         if (pos != -1) {
            String wrappedString = this.lexer.subString(this.lexer.pos(), pos - this.lexer.pos());
            stmt.setWrappedSource(wrappedString);
            this.lexer.reset(pos, ';', Token.LITERAL_CHARS);
            this.lexer.nextToken();
            stmt.setAfterSemi(true);
         } else {
            String wrappedString = this.lexer.text.substring(this.lexer.pos());
            stmt.setWrappedSource(wrappedString);
            this.lexer.reset(this.lexer.text.length(), '\u001a', Token.EOF);
         }

         return stmt;
      } else if (this.lexer.token() == Token.SEMI) {
         this.lexer.nextToken();
         return stmt;
      } else {
         if (this.lexer.token() == Token.IS) {
            this.lexer.nextToken();
         } else {
            this.accept(Token.AS);
         }

         if (this.lexer.identifierEquals("LANGUAGE")) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("JAVA")) {
               this.lexer.nextToken();
               this.acceptIdentifier("NAME");
               String javaCallSpec = this.lexer.stringVal();
               this.accept(Token.LITERAL_CHARS);
               stmt.setJavaCallSpec(javaCallSpec);
               return stmt;
            } else {
               throw new ParserException("TODO : " + this.lexer.info());
            }
         } else {
            SQLStatement block = this.parseBlock();
            stmt.setBlock(block);
            if (this.lexer.identifierEquals(procedureName.getSimpleName())) {
               this.lexer.nextToken();
            }

            return stmt;
         }
      }
   }

   public SQLStatement parseCase() {
      SQLCaseStatement caseStmt = new SQLCaseStatement();
      caseStmt.setDbType(this.dbType);
      this.lexer.nextToken();
      if (this.lexer.token() != Token.WHEN) {
         caseStmt.setValueExpr(this.exprParser.expr());
      }

      this.accept(Token.WHEN);
      SQLExpr testExpr = this.exprParser.expr();
      this.accept(Token.THEN);
      SQLStatement stmt = this.parseStatement();
      if (this.lexer.token() == Token.SEMI) {
         this.lexer.nextToken();
      }

      SQLCaseStatement.Item caseItem = new SQLCaseStatement.Item(testExpr, stmt);
      caseStmt.addItem(caseItem);

      while(this.lexer.token() == Token.WHEN) {
         this.lexer.nextToken();
         testExpr = this.exprParser.expr();
         this.accept(Token.THEN);
         stmt = this.parseStatement();
         if (this.lexer.token() == Token.SEMI) {
            this.lexer.nextToken();
         }

         caseItem = new SQLCaseStatement.Item(testExpr, stmt);
         caseStmt.addItem(caseItem);
      }

      if (this.lexer.token() == Token.ELSE) {
         this.lexer.nextToken();
         this.parseStatementList(caseStmt.getElseStatements(), -1, caseStmt);
      }

      this.accept(Token.END);
      this.accept(Token.CASE);
      this.accept(Token.SEMI);
      return caseStmt;
   }

   public SQLLoopStatement parseLoop() {
      this.accept(Token.LOOP);
      SQLLoopStatement stmt = new SQLLoopStatement();
      this.parseStatementList(stmt.getStatements(), -1, stmt);
      this.accept(Token.END);
      this.accept(Token.LOOP);
      if (this.lexer.token() == Token.IDENTIFIER) {
         String label = this.lexer.stringVal();
         stmt.setLabelName(label);
         this.lexer.nextToken();
      }

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

   public SQLStatement parseCreatePackage() {
      this.accept(Token.CREATE);
      boolean repalce = false;
      if (this.lexer.token() == Token.OR) {
         this.lexer.nextToken();
         this.accept(Token.REPLACE);
         repalce = true;
      }

      this.acceptIdentifier("PACKAGE");
      DmCreatePackageStatement stmt = new DmCreatePackageStatement();
      stmt.setOrReplace(repalce);
      if (this.lexer.identifierEquals("BODY")) {
         this.lexer.nextToken();
         stmt.setBody(true);
      }

      SQLName pkgName = this.exprParser.name();
      stmt.setName(pkgName);
      if (this.lexer.token() == Token.WITH) {
         this.lexer.nextToken();
         this.acceptIdentifier("ENCRYPTION");
         stmt.setWithEncryption(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.AUTHID)) {
         this.lexer.nextToken();
         String strVal = this.lexer.stringVal();
         if (this.lexer.identifierEquals(FnvHash.Constants.CURRENT_USER)) {
            this.lexer.nextToken();
         } else {
            this.acceptIdentifier("DEFINER");
         }

         SQLName authid = new SQLIdentifierExpr(strVal);
         stmt.setAuthid(authid);
      }

      if (this.lexer.token() == Token.IS) {
         this.lexer.nextToken();
      } else {
         this.accept(Token.AS);
      }

      while(true) {
         while(this.lexer.token() != Token.IDENTIFIER) {
            if (this.lexer.token() == Token.FUNCTION) {
               SQLStatement function = this.parseCreateFunction();
               function.setParent(stmt);
               stmt.getStatements().add(function);
            } else {
               if (this.lexer.token() != Token.PROCEDURE) {
                  if (this.lexer.token() != Token.END) {
                     if (this.lexer.token() != Token.BEGIN) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     SQLBlockStatement block = new SQLBlockStatement();
                     this.parseStatementList(block.getStatementList(), -1, block);
                     this.accept(Token.END);
                     block.setParent(stmt);
                     stmt.getStatements().add(block);
                     if (this.lexer.identifierEquals(pkgName.getSimpleName())) {
                        this.lexer.nextToken();
                        this.accept(Token.SEMI);
                        return stmt;
                     }
                  }

                  this.accept(Token.END);
                  if (this.lexer.identifierEquals(pkgName.getSimpleName())) {
                     this.lexer.nextToken();
                  }

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

               SQLStatement proc = this.parseCreateProcedure();
               proc.setParent(stmt);
               stmt.getStatements().add(proc);
            }
         }

         SQLDeclareStatement varDecl = new SQLDeclareStatement();
         varDecl.setDbType(this.dbType);
         varDecl.setParent(stmt);
         SQLDeclareItem varItem = new SQLDeclareItem();
         boolean type = false;
         if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
            this.lexer.nextToken();
            type = true;
         }

         SQLName name = this.exprParser.name();
         varItem.setName(name);
         if (!type) {
            varItem.setDataType(this.exprParser.parseDataType(false));
         } else {
            this.accept(Token.IS);
            if (!this.lexer.identifierEquals(FnvHash.Constants.RECORD)) {
               this.acceptIdentifier("REF");
               this.accept(Token.CURSOR);
               varItem.setDataType(new SQLDataTypeImpl("REF CURSOR"));
            } else {
               this.lexer.nextToken();
               SQLRecordDataType recordDataType = new SQLRecordDataType();
               this.accept(Token.LPAREN);

               while(true) {
                  SQLColumnDefinition column = this.exprParser.parseColumn();
                  recordDataType.addColumn(column);
                  if (this.lexer.token() != Token.COMMA) {
                     this.accept(Token.RPAREN);
                     varItem.setDataType(recordDataType);
                     break;
                  }

                  this.lexer.nextToken();
               }
            }
         }

         varItem.setParent(varDecl);
         if (this.lexer.token() == Token.COLONEQ || this.lexer.token() == Token.EQ || this.lexer.token() == Token.DEFAULT || this.lexer.identifierEquals("ASSIGN") || this.lexer.token() == Token.FOR) {
            this.lexer.nextToken();
            SQLExpr defaultVal = this.exprParser.expr();
            varItem.setValue(defaultVal);
         }

         varDecl.getItems().add(varItem);
         this.accept(Token.SEMI);
         varDecl.setAfterSemi(true);
         stmt.getStatements().add(varDecl);
      }
   }
}
