package com.alibaba.druid.sql.dialect.oracle.parser;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLArgument;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
import com.alibaba.druid.sql.ast.SQLDeclareItem;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLHint;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLParameter;
import com.alibaba.druid.sql.ast.SQLPartition;
import com.alibaba.druid.sql.ast.SQLPartitionByHash;
import com.alibaba.druid.sql.ast.SQLPartitionByRange;
import com.alibaba.druid.sql.ast.SQLRecordDataType;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLCaseStatement;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLNullExpr;
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
import com.alibaba.druid.sql.ast.statement.SQLAlterFunctionStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterProcedureStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddColumn;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddConstraint;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddSupplemental;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDisableConstraint;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropColumnItem;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropConstraint;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropIndex;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropPrimaryKey;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableEnableConstraint;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableRename;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterTypeStatement;
import com.alibaba.druid.sql.ast.statement.SQLAssignItem;
import com.alibaba.druid.sql.ast.statement.SQLBlockStatement;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCommitStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateFunctionStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateProcedureStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateSequenceStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeclareStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropMaterializedViewStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropProcedureStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropSequenceStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropSynonymStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTriggerStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTypeStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropUserStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprStatement;
import com.alibaba.druid.sql.ast.statement.SQLIfStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertInto;
import com.alibaba.druid.sql.ast.statement.SQLLoopStatement;
import com.alibaba.druid.sql.ast.statement.SQLReturnStatement;
import com.alibaba.druid.sql.ast.statement.SQLSavePointStatement;
import com.alibaba.druid.sql.ast.statement.SQLScriptCommitStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLSetStatement;
import com.alibaba.druid.sql.ast.statement.SQLShowErrorsStatement;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import com.alibaba.druid.sql.ast.statement.SQLTableSource;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.ast.statement.SQLWhileStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterSessionStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterSynonymStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableDropPartition;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableModify;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableMoveTablespace;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableSplitPartition;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTableTruncatePartition;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTablespaceAddDataFile;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTablespaceStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterTriggerStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleAlterViewStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleConstraint;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleContinueStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateDatabaseDbLinkStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateIndexStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreatePackageStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSynonymStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTypeStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDbLinkStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExceptionStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExecuteImmediateStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExitStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExplainStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleFileSpecification;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleForStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleGotoStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleLabelStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleLockTableStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePipeRowStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleRaiseStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleRunStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSetTransactionStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleStatement;
import com.alibaba.druid.sql.parser.Lexer;
import com.alibaba.druid.sql.parser.ParserException;
import com.alibaba.druid.sql.parser.SQLExprParser;
import com.alibaba.druid.sql.parser.SQLParserFeature;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import com.alibaba.druid.sql.parser.Token;
import com.alibaba.druid.util.FnvHash;
import java.util.ArrayList;
import java.util.List;

public class OracleStatementParser extends SQLStatementParser {
   public OracleStatementParser(String sql) {
      super((SQLExprParser)(new OracleExprParser(sql)));
   }

   public OracleStatementParser(String sql, SQLParserFeature... features) {
      super((SQLExprParser)(new OracleExprParser(sql, features)));
   }

   public OracleStatementParser(Lexer lexer) {
      super((SQLExprParser)(new OracleExprParser(lexer)));
   }

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

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

   protected void parseInsert0_hinits(SQLInsertInto insertStatement) {
      if (insertStatement instanceof OracleInsertStatement) {
         OracleInsertStatement stmt = (OracleInsertStatement)insertStatement;
         this.getExprParser().parseHints(stmt.getHints());
      } else {
         List<SQLHint> hints = new ArrayList(1);
         this.getExprParser().parseHints(hints);
      }

   }

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

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

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

         if (this.lexer.token() == Token.SEMI) {
            this.lexer.nextToken();
            if (statementList.size() > 0) {
               SQLStatement lastStmt = (SQLStatement)statementList.get(statementList.size() - 1);
               lastStmt.setAfterSemi(true);
            }
         } else if (this.lexer.token() == Token.SELECT) {
            SQLStatement stmt = new SQLSelectStatement((new OracleSelectParser(this.exprParser)).select(), DbType.oracle);
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.UPDATE) {
            SQLStatement stmt = this.parseUpdateStatement();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.CREATE) {
            SQLStatement stmt = this.parseCreate();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.INSERT) {
            SQLStatement stmt = this.parseInsert();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.DELETE) {
            SQLStatement stmt = this.parseDeleteStatement();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.SLASH) {
            this.lexer.nextToken();
            SQLStatement stmt = new SQLScriptCommitStatement();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.ALTER) {
            SQLStatement stmt = this.parserAlter();
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() == Token.WITH) {
            SQLSelectStatement stmt = new SQLSelectStatement(this.createSQLSelectParser().select(), this.dbType);
            stmt.setParent(parent);
            statementList.add(stmt);
         } else if (this.lexer.token() != Token.LBRACE && !this.lexer.identifierEquals("CALL")) {
            if (this.lexer.token() == Token.MERGE) {
               SQLStatement stmt = this.parseMerge();
               stmt.setParent(parent);
               statementList.add(stmt);
            } else if (this.lexer.token() != Token.BEGIN && this.lexer.token() != Token.DECLARE) {
               if (this.lexer.token() == Token.LOCK) {
                  SQLStatement stmt = this.parseLock();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.TRUNCATE) {
                  SQLStatement stmt = this.parseTruncate();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.VARIANT) {
                  SQLExpr variant = this.exprParser.primary();
                  if (variant instanceof SQLBinaryOpExpr) {
                     SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr)variant;
                     if (binaryOpExpr.getOperator() == SQLBinaryOperator.Assignment) {
                        SQLSetStatement stmt = new SQLSetStatement(binaryOpExpr.getLeft(), binaryOpExpr.getRight(), this.getDbType());
                        stmt.setParent(parent);
                        statementList.add(stmt);
                        continue;
                     }
                  }

                  this.accept(Token.COLONEQ);
                  SQLExpr value = this.exprParser.expr();
                  SQLSetStatement stmt = new SQLSetStatement(variant, value, this.getDbType());
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.EXCEPTION) {
                  OracleExceptionStatement stmt = this.parseException();
                  stmt.setParent(parent);
                  if (parent instanceof SQLBlockStatement) {
                     ((SQLBlockStatement)parent).setException(stmt);
                  } else {
                     statementList.add(stmt);
                  }
               } else if (this.lexer.identifierEquals("EXIT")) {
                  this.lexer.nextToken();
                  OracleExitStatement stmt = this.parseExit();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() == Token.CONTINUE) {
                  this.lexer.nextToken();
                  OracleContinueStatement stmt = new OracleContinueStatement();
                  if (this.lexer.token() == Token.IDENTIFIER) {
                     String label = this.lexer.stringVal();
                     this.lexer.nextToken();
                     stmt.setLabel(label);
                  }

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

                  stmt.setParent(parent);
                  statementList.add(stmt);
               } else if (this.lexer.token() != Token.FETCH && !this.lexer.identifierEquals("FETCH")) {
                  if (this.lexer.identifierEquals("ROLLBACK")) {
                     SQLStatement stmt = this.parseRollback();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.EXPLAIN) {
                     OracleExplainStatement stmt = this.parseExplain();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.IDENTIFIER) {
                     String strVal = this.lexer.stringVal();
                     if (strVal.equalsIgnoreCase("RAISE")) {
                        SQLStatement stmt = this.parseRaise();
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else if (strVal.equalsIgnoreCase("FORALL")) {
                        SQLStatement stmt = this.parseFor();
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else if (strVal.equalsIgnoreCase("RENAME")) {
                        SQLStatement stmt = this.parseRename();
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else if (strVal.equalsIgnoreCase("EXECUTE")) {
                        SQLStatement stmt = this.parseExecute();
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else if (strVal.equalsIgnoreCase("PIPE")) {
                        Lexer.SavePoint savePoint = this.lexer.mark();
                        this.lexer.nextToken();
                        if (this.lexer.token() == Token.ROW) {
                           this.lexer.reset(savePoint);
                           SQLStatement stmt = this.parsePipeRow();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else {
                           this.lexer.reset(savePoint);
                        }
                     } else if (strVal.equalsIgnoreCase("SHOW")) {
                        this.lexer.nextToken();
                        if (this.lexer.identifierEquals("ERR")) {
                           this.lexer.nextToken();
                        } else {
                           this.accept(Token.ERRORS);
                        }

                        SQLShowErrorsStatement stmt = new SQLShowErrorsStatement();
                        stmt.setDbType(this.dbType);
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else {
                        SQLExpr expr = this.exprParser.expr();
                        if (expr instanceof SQLBinaryOpExpr) {
                           SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr)expr;
                           if (binaryOpExpr.getOperator() == SQLBinaryOperator.Assignment) {
                              SQLSetStatement stmt = new SQLSetStatement();
                              stmt.setDbType(DbType.oracle);
                              stmt.setParent(parent);
                              SQLAssignItem assignItem = new SQLAssignItem(binaryOpExpr.getLeft(), binaryOpExpr.getRight());
                              assignItem.setParent(stmt);
                              stmt.getItems().add(assignItem);
                              statementList.add(stmt);
                              continue;
                           }
                        }

                        SQLExprStatement stmt = new SQLExprStatement(expr);
                        stmt.setDbType(this.dbType);
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     }
                  } else if (this.lexer.token() == Token.LPAREN) {
                     Lexer.SavePoint savePoint = this.lexer.mark();
                     this.lexer.nextToken();

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

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

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

                     for(int i = 0; i < parenCount; ++i) {
                        this.accept(Token.RPAREN);
                     }
                  } else if (this.lexer.token() == Token.SET) {
                     SQLStatement stmt = this.parseSet();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.GRANT) {
                     statementList.add(this.parseGrant());
                  } else if (this.lexer.token() == Token.REVOKE) {
                     statementList.add(this.parseRevoke());
                  } else if (this.lexer.token() == Token.COMMENT) {
                     statementList.add(this.parseComment());
                  } else if (this.lexer.token() == Token.FOR) {
                     OracleForStatement forStatement = this.parseFor();
                     forStatement.setParent(parent);
                     if (this.lexer.token() == Token.IDENTIFIER) {
                        String strVal = this.lexer.stringVal();
                        int stmtListSize = statementList.size();
                        if (stmtListSize > 0) {
                           SQLStatement lastStmt = (SQLStatement)statementList.get(stmtListSize - 1);
                           if (lastStmt instanceof OracleLabelStatement && ((OracleLabelStatement)lastStmt).getLabel().getSimpleName().equalsIgnoreCase(strVal)) {
                              SQLName endLabbel = this.exprParser.name();
                              forStatement.setEndLabel(endLabbel);
                           }
                        }
                     }

                     statementList.add(forStatement);
                  } else if (this.lexer.token() == Token.LOOP) {
                     SQLStatement stmt = this.parseLoop();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.IF) {
                     SQLStatement stmt = this.parseIf();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.GOTO) {
                     this.lexer.nextToken();
                     SQLName label = this.exprParser.name();
                     OracleGotoStatement stmt = new OracleGotoStatement(label);
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.COMMIT) {
                     this.lexer.nextToken();
                     if (this.lexer.identifierEquals("WORK")) {
                        this.lexer.nextToken();
                     }

                     SQLCommitStatement stmt = new SQLCommitStatement();
                     stmt.setParent(parent);
                     if (this.lexer.identifierEquals("WRITE")) {
                        stmt.setWrite(true);
                        this.lexer.nextToken();

                        label326:
                        while(true) {
                           while(this.lexer.token() != Token.WAIT) {
                              if (this.lexer.token() == Token.NOWAIT) {
                                 this.lexer.nextToken();
                                 stmt.setWait(Boolean.FALSE);
                              } else if (this.lexer.token() == Token.IMMEDIATE) {
                                 this.lexer.nextToken();
                                 stmt.setImmediate(Boolean.TRUE);
                              } else {
                                 if (!this.lexer.identifierEquals("BATCH")) {
                                    break label326;
                                 }

                                 this.lexer.nextToken();
                                 stmt.setImmediate(Boolean.FALSE);
                              }
                           }

                           this.lexer.nextToken();
                           stmt.setWait(Boolean.TRUE);
                        }
                     }

                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.SAVEPOINT) {
                     this.lexer.nextToken();
                     SQLSavePointStatement stmt = new SQLSavePointStatement();
                     stmt.setDbType(this.dbType);
                     stmt.setParent(parent);
                     if (this.lexer.token() == Token.TO) {
                        this.lexer.nextToken();
                        stmt.setName(this.exprParser.name());
                     } else if (this.lexer.token() != Token.SEMI) {
                        stmt.setName(this.exprParser.name());
                     }

                     this.accept(Token.SEMI);
                     stmt.setAfterSemi(true);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.LTLT) {
                     this.lexer.nextToken();
                     SQLName label = this.exprParser.name();
                     OracleLabelStatement stmt = new OracleLabelStatement(label);
                     this.accept(Token.GTGT);
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.DROP) {
                     Lexer.SavePoint savePoint = this.lexer.mark();
                     this.lexer.nextToken();
                     if (this.lexer.token() == Token.TABLE) {
                        SQLDropTableStatement stmt = this.parseDropTable(false);
                        stmt.setParent(parent);
                        statementList.add(stmt);
                     } else {
                        boolean isPublic = false;
                        if (this.lexer.identifierEquals("PUBLIC")) {
                           this.lexer.nextToken();
                           isPublic = true;
                        }

                        if (this.lexer.token() == Token.DATABASE) {
                           this.lexer.nextToken();
                           if (this.lexer.identifierEquals("LINK")) {
                              this.lexer.nextToken();
                              OracleDropDbLinkStatement stmt = new OracleDropDbLinkStatement();
                              if (isPublic) {
                                 stmt.setPublic(isPublic);
                              }

                              stmt.setName(this.exprParser.name());
                              statementList.add(stmt);
                              continue;
                           }
                        }

                        if (this.lexer.token() == Token.INDEX) {
                           SQLStatement stmt = this.parseDropIndex();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.token() == Token.VIEW) {
                           SQLStatement stmt = this.parseDropView(false);
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.token() == Token.SEQUENCE) {
                           SQLDropSequenceStatement stmt = this.parseDropSequence(false);
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.token() == Token.TRIGGER) {
                           SQLDropTriggerStatement stmt = this.parseDropTrigger(false);
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.token() == Token.USER) {
                           SQLDropUserStatement stmt = this.parseDropUser();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.token() == Token.PROCEDURE) {
                           SQLDropProcedureStatement stmt = this.parseDropProcedure(false);
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.identifierEquals(FnvHash.Constants.SYNONYM)) {
                           this.lexer.reset(savePoint);
                           SQLStatement stmt = this.parseDropSynonym();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
                           this.lexer.reset(savePoint);
                           SQLStatement stmt = this.parseDropType();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        } else {
                           if (!this.lexer.identifierEquals(FnvHash.Constants.MATERIALIZED)) {
                              throw new ParserException("TODO : " + this.lexer.info());
                           }

                           this.lexer.reset(savePoint);
                           SQLStatement stmt = this.parseDropMaterializedView();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                        }
                     }
                  } else if (this.lexer.token() == Token.NULL) {
                     this.lexer.nextToken();
                     SQLExprStatement stmt = new SQLExprStatement(new SQLNullExpr());
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.OPEN) {
                     SQLStatement stmt = this.parseOpen();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.CLOSE) {
                     SQLStatement stmt = this.parseClose();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.CASE) {
                     SQLStatement stmt = this.parseCase();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else if (this.lexer.token() == Token.PROCEDURE) {
                     SQLStatement stmt = this.parseCreateProcedure();
                     stmt.setParent(parent);
                     statementList.add(stmt);
                  } else {
                     if ((this.lexer.token() != Token.ELSIF || !(parent instanceof SQLIfStatement)) && (this.lexer.token() != Token.WHEN || !(parent instanceof OracleExceptionStatement.Item))) {
                        if (this.lexer.token() == Token.FUNCTION) {
                           SQLStatement stmt = this.parseFunction();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                           continue;
                        }

                        if (this.lexer.token() == Token.WHILE) {
                           SQLStatement stmt = this.parseWhile();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                           continue;
                        }

                        if (this.lexer.token() == Token.RETURN) {
                           SQLStatement stmt = this.parseReturn();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                           continue;
                        }

                        if (this.lexer.token() == Token.TRIGGER) {
                           SQLStatement stmt = this.parseCreateTrigger();
                           stmt.setParent(parent);
                           statementList.add(stmt);
                           continue;
                        }

                        if (this.lexer.token() == Token.MONKEYS_AT_AT) {
                           this.lexer.nextToken();
                           SQLExpr expr = this.exprParser.primary();
                           OracleRunStatement stmt = new OracleRunStatement(expr);
                           stmt.setParent(parent);
                           statementList.add(stmt);
                           continue;
                        }

                        if (this.lexer.token() == Token.QUES) {
                           SQLExpr expr = this.exprParser.expr();
                           SQLExprStatement stmt = new SQLExprStatement(expr);
                           stmt.setParent(parent);
                           statementList.add(stmt);
                           continue;
                        }

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

                     return;
                  }
               } else {
                  SQLStatement stmt = this.parseFetch();
                  stmt.setParent(parent);
                  statementList.add(stmt);
               }
            } else {
               SQLStatement stmt = this.parseBlock();
               stmt.setParent(parent);
               statementList.add(stmt);
            }
         } else {
            SQLStatement stmt = this.parseCall();
            stmt.setParent(parent);
            statementList.add(stmt);
         }
      }

   }

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

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

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

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

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

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

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

      return stmt;
   }

   public SQLStatement parsePipeRow() {
      OraclePipeRowStatement stmt = new OraclePipeRowStatement();
      this.acceptIdentifier("PIPE");
      this.accept(Token.ROW);
      this.accept(Token.LPAREN);
      this.exprParser.exprList(stmt.getParameters(), stmt);
      this.accept(Token.RPAREN);
      return stmt;
   }

   public SQLStatement parseExecute() {
      this.acceptIdentifier("EXECUTE");
      if (this.lexer.token() == Token.IMMEDIATE) {
         this.lexer.nextToken();
         OracleExecuteImmediateStatement stmt = new OracleExecuteImmediateStatement();
         SQLExpr dyanmiacSql = this.exprParser.primary();
         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 parseRename() {
      this.lexer.nextToken();
      SQLName from = this.exprParser.name();
      this.accept(Token.TO);
      SQLName to = this.exprParser.name();
      SQLAlterTableStatement stmt = new SQLAlterTableStatement(this.dbType);
      stmt.setTableSource((SQLExpr)from);
      SQLAlterTableRename toItem = new SQLAlterTableRename(to);
      stmt.addItem(toItem);
      return stmt;
   }

   private OracleExitStatement parseExit() {
      OracleExitStatement stmt = new OracleExitStatement();
      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 parseReturn() {
      this.accept(Token.RETURN);
      SQLReturnStatement stmt = new SQLReturnStatement();
      if (this.lexer.token() != Token.SEMI) {
         SQLExpr expr = this.exprParser.expr();
         stmt.setExpr(expr);
      }

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

   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);
      this.accept(Token.SEMI);
      return stmt;
   }

   public SQLCreateFunctionStatement parseCreateFunction() {
      SQLCreateFunctionStatement stmt = (SQLCreateFunctionStatement)this.parseFunction();
      stmt.setCreate(true);
      return stmt;
   }

   public SQLStatement parseFunction() {
      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);
         if (this.identifierEquals("PIPELINED")) {
            this.lexer.nextToken();
            stmt.setPipelined(true);
         }

         if (this.identifierEquals("DETERMINISTIC")) {
            this.lexer.nextToken();
            stmt.setDeterministic(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.identifierEquals("RESULT_CACHE")) {
            this.lexer.nextToken();
            stmt.setResultCache(true);
         }

         if (this.lexer.token() == Token.SEMI) {
            this.lexer.nextToken();
            return stmt;
         } else {
            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;
               } else {
                  throw new ParserException("TODO : " + this.lexer.info());
               }
            } else {
               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;
            }
         }
      }
   }

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

      this.accept(Token.SEMI);
      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 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 OracleForStatement parseFor() {
      OracleForStatement stmt = new OracleForStatement();
      if (this.lexer.token() == Token.FOR) {
         this.lexer.nextToken();
      } else {
         this.acceptIdentifier("FORALL");
         stmt.setAll(true);
      }

      stmt.setIndex(this.exprParser.name());
      this.accept(Token.IN);
      stmt.setRange(this.exprParser.expr());
      if (stmt.isAll()) {
         SQLStatement itemStmt = this.parseStatement();
         itemStmt.setParent(stmt);
         stmt.getStatements().add(itemStmt);
      } else {
         this.accept(Token.LOOP);
         this.parseStatementList(stmt.getStatements(), -1, stmt);
         this.accept(Token.END);
         this.accept(Token.LOOP);
         if (this.lexer.token() != Token.SEMI) {
            SQLName endLabel = this.exprParser.name();
            stmt.setEndLabel(endLabel);
         }

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

      return stmt;
   }

   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 parseSet() {
      this.accept(Token.SET);
      if (this.lexer.identifierEquals("TRANSACTION")) {
         this.lexer.nextToken();
         OracleSetTransactionStatement stmt = new OracleSetTransactionStatement();
         if (this.lexer.identifierEquals("READ")) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("ONLY")) {
               this.lexer.nextToken();
               stmt.setReadOnly(true);
            } else {
               this.acceptIdentifier("WRITE");
               stmt.setWrite(true);
            }
         }

         if (this.lexer.identifierEquals("NAME")) {
            this.lexer.nextToken();
            stmt.setName(this.exprParser.expr());
         }

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

   public SQLStatement parserAlter() {
      Lexer.SavePoint savePoint = this.lexer.mark();
      this.accept(Token.ALTER);
      if (this.lexer.token() == Token.SESSION) {
         this.lexer.nextToken();
         OracleAlterSessionStatement stmt = new OracleAlterSessionStatement();
         if (this.lexer.token() == Token.SET) {
            this.lexer.nextToken();
            this.parseAssignItems(stmt.getItems(), stmt);
            return stmt;
         } else {
            throw new ParserException("TODO : " + this.lexer.info());
         }
      } else if (this.lexer.token() == Token.PROCEDURE) {
         this.lexer.nextToken();
         SQLAlterProcedureStatement stmt = new SQLAlterProcedureStatement();
         stmt.setName(this.exprParser.name());
         if (this.lexer.identifierEquals("COMPILE")) {
            this.lexer.nextToken();
            stmt.setCompile(true);
         }

         if (this.lexer.identifierEquals("REUSE")) {
            this.lexer.nextToken();
            this.acceptIdentifier("SETTINGS");
            stmt.setReuseSettings(true);
         }

         return stmt;
      } else if (this.lexer.token() == Token.TABLE) {
         return this.parseAlterTable();
      } else if (this.lexer.token() == Token.INDEX) {
         this.lexer.reset(savePoint);
         return this.parseAlterIndex();
      } else if (this.lexer.token() == Token.TRIGGER) {
         this.lexer.nextToken();
         OracleAlterTriggerStatement stmt = new OracleAlterTriggerStatement();
         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")) {
                     return stmt;
                  }

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

            this.lexer.nextToken();
            stmt.setEnable(Boolean.TRUE);
         }
      } else if (this.lexer.identifierEquals(FnvHash.Constants.SYNONYM)) {
         this.lexer.nextToken();
         OracleAlterSynonymStatement stmt = new OracleAlterSynonymStatement();
         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")) {
                     return stmt;
                  }

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

            this.lexer.nextToken();
            stmt.setEnable(Boolean.TRUE);
         }
      } else if (this.lexer.token() == Token.VIEW) {
         this.lexer.nextToken();
         OracleAlterViewStatement stmt = new OracleAlterViewStatement();
         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")) {
                     return stmt;
                  }

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

            this.lexer.nextToken();
            stmt.setEnable(Boolean.TRUE);
         }
      } else if (this.lexer.token() != Token.TABLESPACE) {
         if (this.lexer.token() == Token.FUNCTION) {
            this.lexer.reset(savePoint);
            return this.parseAlterFunction();
         } else if (this.lexer.token() == Token.SEQUENCE) {
            this.lexer.reset(savePoint);
            return this.parseAlterSequence();
         } else if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
            this.lexer.reset(savePoint);
            return this.parseAlterType();
         } else {
            throw new ParserException("TODO : " + this.lexer.info());
         }
      } else {
         this.lexer.nextToken();
         OracleAlterTablespaceStatement stmt = new OracleAlterTablespaceStatement();
         stmt.setName(this.exprParser.name());
         if (!this.lexer.identifierEquals("ADD")) {
            throw new ParserException("TODO : " + this.lexer.info());
         } else {
            this.lexer.nextToken();
            if (!this.lexer.identifierEquals("DATAFILE")) {
               throw new ParserException("TODO : " + this.lexer.info());
            } else {
               this.lexer.nextToken();
               OracleAlterTablespaceAddDataFile item = new OracleAlterTablespaceAddDataFile();

               while(true) {
                  OracleFileSpecification file = new OracleFileSpecification();

                  while(true) {
                     SQLExpr fileName = this.exprParser.expr();
                     file.getFileNames().add(fileName);
                     if (this.lexer.token() != Token.COMMA) {
                        if (this.lexer.identifierEquals("SIZE")) {
                           this.lexer.nextToken();
                           file.setSize(this.exprParser.expr());
                        }

                        if (this.lexer.identifierEquals("AUTOEXTEND")) {
                           this.lexer.nextToken();
                           if (this.lexer.identifierEquals("OFF")) {
                              this.lexer.nextToken();
                              file.setAutoExtendOff(true);
                           } else {
                              if (!this.lexer.identifierEquals("ON")) {
                                 throw new ParserException("TODO : " + this.lexer.info());
                              }

                              this.lexer.nextToken();
                              file.setAutoExtendOn(this.exprParser.expr());
                           }
                        }

                        item.getFiles().add(file);
                        if (this.lexer.token() != Token.COMMA) {
                           stmt.setItem(item);
                           return stmt;
                        }

                        this.lexer.nextToken();
                        break;
                     }

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

   protected SQLStatement parseAlterType() {
      this.accept(Token.ALTER);
      this.acceptIdentifier("TYPE");
      SQLAlterTypeStatement stmt = new SQLAlterTypeStatement();
      stmt.setDbType(this.dbType);
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      if (this.lexer.identifierEquals("COMPILE")) {
         stmt.setCompile(true);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals("DEBUG")) {
         stmt.setDebug(true);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals("BODY")) {
         stmt.setBody(true);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals("REUSE")) {
         stmt.setReuseSettings(true);
         this.lexer.nextToken();
         this.acceptIdentifier("SETTINGS");
      }

      return stmt;
   }

   protected SQLStatement parseAlterFunction() {
      this.accept(Token.ALTER);
      this.accept(Token.FUNCTION);
      SQLAlterFunctionStatement stmt = new SQLAlterFunctionStatement();
      stmt.setDbType(this.dbType);
      SQLName name = this.exprParser.name();
      stmt.setName(name);
      this.acceptIdentifier("COMPILE");
      if (this.lexer.identifierEquals("DEBUG")) {
         stmt.setDebug(true);
         this.lexer.nextToken();
      }

      if (this.lexer.identifierEquals("REUSE")) {
         stmt.setReuseSettings(true);
         this.lexer.nextToken();
         this.acceptIdentifier("SETTINGS");
      }

      return stmt;
   }

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

      while(true) {
         while(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) {
                  SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                  stmt.addItem(item);
               } else {
                  if (this.lexer.token() != Token.LITERAL_ALIAS) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  SQLAlterTableAddColumn item = this.parseAlterTableAddColumn();
                  stmt.addItem(item);
               }
            } else {
               OracleConstraint constraint = ((OracleExprParser)this.exprParser).parseConstaint();
               SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint();
               constraint.setParent(item);
               item.setParent(stmt);
               item.setConstraint(constraint);
               stmt.addItem(item);
            }
         }

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

            this.lexer.nextToken();
            OracleAlterTableMoveTablespace item = new OracleAlterTableMoveTablespace();
            item.setName(this.exprParser.name());
            stmt.addItem(item);
            break;
         }

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

         if (!this.lexer.identifierEquals("MODIFY")) {
            if (this.lexer.identifierEquals("SPLIT")) {
               this.parseAlterTableSplit(stmt);
            } else if (this.lexer.token() == Token.TRUNCATE) {
               this.lexer.nextToken();
               if (this.lexer.token() != Token.PARTITION) {
                  throw new ParserException("TODO : " + this.lexer.info());
               }

               this.lexer.nextToken();
               OracleAlterTableTruncatePartition item = new OracleAlterTableTruncatePartition();
               item.setName(this.exprParser.name());
               stmt.addItem(item);
            } else {
               if (this.lexer.token() != Token.DROP) {
                  if (this.lexer.token() == Token.DISABLE) {
                     this.lexer.nextToken();
                     if (this.lexer.token() != Token.CONSTRAINT) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
                     item.setConstraintName(this.exprParser.name());
                     stmt.addItem(item);
                  } else if (this.lexer.token() == Token.ENABLE) {
                     this.lexer.nextToken();
                     if (this.lexer.token() != Token.CONSTRAINT) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
                     item.setConstraintName(this.exprParser.name());
                     stmt.addItem(item);
                  }
                  break;
               }

               this.parseAlterDrop(stmt);
            }
         } else {
            this.lexer.nextToken();
            OracleAlterTableModify item = new OracleAlterTableModify();
            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);
         }
      }

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

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

   }

   private void parseAlterTableSplit(SQLAlterTableStatement stmt) {
      this.lexer.nextToken();
      if (this.lexer.token() == Token.PARTITION) {
         this.lexer.nextToken();
         OracleAlterTableSplitPartition item = new OracleAlterTableSplitPartition();
         item.setName(this.exprParser.name());
         if (!this.lexer.identifierEquals("AT")) {
            throw new ParserException("TODO : " + this.lexer.info());
         } else {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            this.exprParser.exprList(item.getAt(), item);
            this.accept(Token.RPAREN);
            if (this.lexer.token() == Token.INTO) {
               this.lexer.nextToken();
               this.accept(Token.LPAREN);

               while(true) {
                  OracleAlterTableSplitPartition.NestedTablePartitionSpec spec = new OracleAlterTableSplitPartition.NestedTablePartitionSpec();
                  this.accept(Token.PARTITION);
                  spec.setPartition(this.exprParser.name());

                  while(this.lexer.token() == Token.TABLESPACE) {
                     this.lexer.nextToken();
                     SQLName tablespace = this.exprParser.name();
                     spec.getSegmentAttributeItems().add(new OracleAlterTableSplitPartition.TableSpaceItem(tablespace));
                  }

                  if (this.lexer.identifierEquals("PCTREE")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  if (this.lexer.identifierEquals("PCTUSED")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  if (this.lexer.identifierEquals("INITRANS")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  if (this.lexer.identifierEquals("STORAGE")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  if (this.lexer.identifierEquals("LOGGING")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  if (this.lexer.identifierEquals("NOLOGGING")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  if (this.lexer.identifierEquals("FILESYSTEM_LIKE_LOGGING")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

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

                  this.lexer.nextToken();
               }
            }

            if (this.lexer.token() == Token.UPDATE) {
               this.lexer.nextToken();
               this.acceptIdentifier("INDEXES");
               OracleAlterTableSplitPartition.UpdateIndexesClause updateIndexes = new OracleAlterTableSplitPartition.UpdateIndexesClause();
               item.setUpdateIndexes(updateIndexes);
            }

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

   public OracleLockTableStatement parseLock() {
      this.accept(Token.LOCK);
      this.accept(Token.TABLE);
      OracleLockTableStatement stmt = new OracleLockTableStatement();
      stmt.setTable(this.exprParser.name());
      if (Token.PARTITION == this.lexer.token()) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         stmt.setPartition(this.exprParser.expr());
         this.accept(Token.RPAREN);
      }

      this.accept(Token.IN);
      Token token = this.lexer.token();
      if (token == Token.SHARE) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.ROW) {
            this.lexer.nextToken();
            this.accept(Token.EXCLUSIVE);
            stmt.setLockMode(OracleLockTableStatement.LockMode.SHARE_ROW_EXCLUSIVE);
         } else if (this.lexer.token() == Token.UPDATE) {
            this.lexer.nextToken();
            stmt.setLockMode(OracleLockTableStatement.LockMode.SHARE_UPDATE);
         } else {
            stmt.setLockMode(OracleLockTableStatement.LockMode.SHARE);
         }
      } else if (token == Token.EXCLUSIVE) {
         stmt.setLockMode(OracleLockTableStatement.LockMode.EXCLUSIVE);
         this.lexer.nextToken();
      } else {
         if (token != Token.ROW) {
            throw new ParserException(this.lexer.info());
         }

         this.lexer.nextToken();
         token = this.lexer.token();
         if (token == Token.SHARE) {
            stmt.setLockMode(OracleLockTableStatement.LockMode.ROW_SHARE);
            this.lexer.nextToken();
         } else {
            if (token != Token.EXCLUSIVE) {
               throw new ParserException(this.lexer.info());
            }

            stmt.setLockMode(OracleLockTableStatement.LockMode.ROW_EXCLUSIVE);
            this.lexer.nextToken();
         }
      }

      this.accept(Token.MODE);
      if (this.lexer.token() == Token.NOWAIT) {
         this.lexer.nextToken();
         stmt.setNoWait(true);
      } else if (this.lexer.token() == Token.WAIT) {
         this.lexer.nextToken();
         stmt.setWait(this.exprParser.expr());
      }

      return stmt;
   }

   public SQLStatement parseBlock() {
      SQLBlockStatement block = new SQLBlockStatement();
      block.setDbType(DbType.oracle);
      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();
                  if (this.lexer.token() == Token.IN) {
                     this.lexer.nextToken();
                     parameter.setParamType(SQLParameter.ParameterType.INOUT);
                  } else {
                     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.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 OracleSelectParser createSQLSelectParser() {
      OracleSelectParser parser = new OracleSelectParser(this.exprParser, this.selectListCache);
      parser.setDbType(DbType.oracle);
      return parser;
   }

   public OracleStatement parseInsert() {
      if (this.lexer.token() == Token.LPAREN) {
         OracleInsertStatement stmt = new OracleInsertStatement();
         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) {
            OracleInsertStatement stmt = new OracleInsertStatement();
            stmt.setHints(hints);
            this.parseInsert0(stmt);
            stmt.setReturning(this.parseReturningClause());
            stmt.setErrorLogging(this.parseErrorLoggingClause());
            return stmt;
         } else {
            OracleMultiInsertStatement stmt = this.parseMultiInsert();
            stmt.setHints(hints);
            return stmt;
         }
      }
   }

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

      while(this.lexer.token() == Token.INTO) {
         OracleMultiInsertStatement.InsertIntoClause clause = new OracleMultiInsertStatement.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) {
         OracleMultiInsertStatement.ConditionalInsertClause clause = new OracleMultiInsertStatement.ConditionalInsertClause();

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

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

         if (this.lexer.token() == Token.ELSE) {
            this.lexer.nextToken();
            OracleMultiInsertStatement.InsertIntoClause insertInto = new OracleMultiInsertStatement.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;
   }

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

      do {
         this.accept(Token.WHEN);
         OracleExceptionStatement.Item item = new OracleExceptionStatement.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 OracleReturningClause parseReturningClause() {
      OracleReturningClause clause = null;
      if (this.lexer.token() == Token.RETURNING) {
         this.lexer.nextToken();
         clause = new OracleReturningClause();

         while(true) {
            SQLExpr item = this.exprParser.expr();
            clause.addItem(item);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.INTO);

               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 OracleExplainStatement parseExplain() {
      this.accept(Token.EXPLAIN);
      this.acceptIdentifier("PLAN");
      OracleExplainStatement stmt = new OracleExplainStatement();
      if (this.lexer.token() == Token.SET) {
         this.lexer.nextToken();
         this.acceptIdentifier("STATEMENT_ID");
         this.accept(Token.EQ);
         stmt.setStatementId(this.exprParser.primary());
      }

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

      this.accept(Token.FOR);
      stmt.setStatement(this.parseStatement());
      return stmt;
   }

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

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

         if (this.lexer.identifierEquals("ONLY")) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            SQLName tableName = this.exprParser.name();
            deleteStatement.setTableName(tableName);
            this.accept(Token.RPAREN);
         } else 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());
      }

      if (this.lexer.token() == Token.RETURNING) {
         OracleReturningClause clause = this.parseReturningClause();
         deleteStatement.setReturning(clause);
      }

      if (!this.lexer.identifierEquals("RETURN") && !this.lexer.identifierEquals("RETURNING")) {
         if (this.lexer.identifierEquals("LOG")) {
            throw new ParserException("TODO. " + this.lexer.info());
         } else {
            return deleteStatement;
         }
      } else {
         throw new ParserException("TODO. " + this.lexer.info());
      }
   }

   public SQLStatement parseCreateDbLink() {
      this.accept(Token.CREATE);
      OracleCreateDatabaseDbLinkStatement dbLink = new OracleCreateDatabaseDbLinkStatement();
      if (this.lexer.identifierEquals("SHARED")) {
         dbLink.setShared(true);
         this.lexer.nextToken();
      }

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

      this.accept(Token.DATABASE);
      this.acceptIdentifier("LINK");
      dbLink.setName(this.exprParser.name());
      if (this.lexer.token() == Token.CONNECT) {
         this.lexer.nextToken();
         this.accept(Token.TO);
         dbLink.setUser(this.exprParser.name());
         if (this.lexer.identifierEquals(FnvHash.Constants.IDENTIFIED)) {
            this.lexer.nextToken();
            this.accept(Token.BY);
            dbLink.setPassword(this.lexer.stringVal());
            if (this.lexer.token() == Token.IDENTIFIER) {
               this.lexer.nextToken();
            } else {
               this.accept(Token.LITERAL_ALIAS);
            }
         }
      }

      if (this.lexer.identifierEquals("AUTHENTICATED")) {
         this.lexer.nextToken();
         this.accept(Token.BY);
         dbLink.setAuthenticatedUser(this.exprParser.name());
         this.acceptIdentifier("IDENTIFIED");
         this.accept(Token.BY);
         dbLink.setPassword(this.lexer.stringVal());
         this.accept(Token.IDENTIFIER);
      }

      if (this.lexer.token() == Token.USING) {
         this.lexer.nextToken();
         dbLink.setUsing(this.exprParser.expr());
      }

      return dbLink;
   }

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

      OracleCreateIndexStatement stmt = new OracleCreateIndexStatement();
      if (this.lexer.token() == Token.UNIQUE) {
         stmt.setType("UNIQUE");
         this.lexer.nextToken();
      } else if (this.lexer.identifierEquals("BITMAP")) {
         stmt.setType("BITMAP");
         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();
         }
      }

      label158:
      while(true) {
         this.getExprParser().parseSegmentAttributes(stmt);
         if (this.lexer.token() != Token.COMPUTE) {
            if (this.lexer.token() != Token.ENABLE) {
               if (this.lexer.token() != Token.DISABLE) {
                  if (!this.lexer.identifierEquals("ONLINE")) {
                     if (!this.lexer.identifierEquals("NOPARALLEL")) {
                        if (!this.lexer.identifierEquals("PARALLEL")) {
                           if (this.lexer.token() != Token.INDEX) {
                              if (!this.lexer.identifierEquals("SORT")) {
                                 if (!this.lexer.identifierEquals("NOSORT")) {
                                    if (!this.lexer.identifierEquals("REVERSE")) {
                                       if (!this.lexer.identifierEquals("LOCAL")) {
                                          if (!this.lexer.identifierEquals("GLOBAL")) {
                                             break;
                                          }

                                          this.lexer.nextToken();
                                          stmt.setGlobal(true);
                                          if (this.lexer.token() != Token.PARTITION) {
                                             break;
                                          }

                                          this.lexer.nextToken();
                                          this.accept(Token.BY);
                                          if (this.lexer.identifierEquals("RANGE")) {
                                             SQLPartitionByRange partitionByRange = this.getExprParser().partitionByRange();
                                             this.getExprParser().partitionClauseRest(partitionByRange);
                                             partitionByRange.setParent(stmt);
                                             stmt.getGlobalPartitions().add(partitionByRange);
                                          } else {
                                             if (!this.lexer.identifierEquals("HASH")) {
                                                break;
                                             }

                                             SQLPartitionByHash partitionByHash = this.getExprParser().partitionByHash();
                                             this.getExprParser().partitionClauseRest(partitionByHash);
                                             if (this.lexer.token() == Token.LPAREN) {
                                                this.lexer.nextToken();

                                                while(true) {
                                                   SQLPartition partition = this.getExprParser().parsePartition();
                                                   partitionByHash.addPartition(partition);
                                                   if (this.lexer.token() != Token.COMMA) {
                                                      if (this.lexer.token() != Token.RPAREN) {
                                                         throw new ParserException("TODO : " + this.lexer.info());
                                                      }

                                                      this.lexer.nextToken();
                                                      break;
                                                   }

                                                   this.lexer.nextToken();
                                                }
                                             }

                                             partitionByHash.setParent(stmt);
                                             stmt.getGlobalPartitions().add(partitionByHash);
                                          }
                                       } else {
                                          this.lexer.nextToken();
                                          stmt.setLocal(true);

                                          while(true) {
                                             while(this.lexer.token() != Token.STORE) {
                                                if (this.lexer.token() != Token.LPAREN) {
                                                   continue label158;
                                                }

                                                this.lexer.nextToken();

                                                while(true) {
                                                   SQLPartition partition = this.getExprParser().parsePartition();
                                                   partition.setParent(stmt);
                                                   stmt.getLocalPartitions().add(partition);
                                                   if (this.lexer.token() != Token.COMMA) {
                                                      if (this.lexer.token() != Token.RPAREN) {
                                                         throw new ParserException("TODO : " + this.lexer.info());
                                                      }

                                                      this.lexer.nextToken();
                                                      break;
                                                   }

                                                   this.lexer.nextToken();
                                                }
                                             }

                                             this.lexer.nextToken();
                                             this.accept(Token.IN);
                                             this.accept(Token.LPAREN);
                                             this.exprParser.names(stmt.getLocalStoreIn(), stmt);
                                             this.accept(Token.RPAREN);
                                          }
                                       }
                                    } else {
                                       this.lexer.nextToken();
                                       stmt.setReverse(true);
                                    }
                                 } else {
                                    this.lexer.nextToken();
                                    stmt.setSort(Boolean.FALSE);
                                 }
                              } else {
                                 this.lexer.nextToken();
                                 stmt.setSort(Boolean.TRUE);
                              }
                           } else {
                              this.lexer.nextToken();
                              this.acceptIdentifier("ONLY");
                              this.acceptIdentifier("TOPLEVEL");
                              stmt.setIndexOnlyTopLevel(true);
                           }
                        } else {
                           this.lexer.nextToken();
                           stmt.setParallel(this.exprParser.expr());
                        }
                     } else {
                        this.lexer.nextToken();
                        stmt.setNoParallel(true);
                     }
                  } else {
                     this.lexer.nextToken();
                     stmt.setOnline(true);
                  }
               } else {
                  this.lexer.nextToken();
                  stmt.setEnable(false);
               }
            } else {
               this.lexer.nextToken();
               stmt.setEnable(true);
            }
         } else {
            this.lexer.nextToken();
            this.acceptIdentifier("STATISTICS");
            stmt.setComputeStatistics(true);
         }
      }

      return stmt;
   }

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

      this.accept(Token.SEQUENCE);
      SQLCreateSequenceStatement stmt = new SQLCreateSequenceStatement();
      stmt.setDbType(DbType.oracle);
      stmt.setName(this.exprParser.name());

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

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

         this.lexer.nextToken();
         this.accept(Token.WITH);
         stmt.setStartWith(this.exprParser.expr());
      }
   }

   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();
         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 SQLUpdateStatement parseUpdateStatement() {
      return (new OracleUpdateParser(this.lexer)).parseUpdateStatement();
   }

   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");
      OracleCreatePackageStatement stmt = new OracleCreatePackageStatement();
      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.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.parseFunction();
               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.nextToken();
            SQLExpr defaultVal = this.exprParser.expr();
            varItem.setValue(defaultVal);
         }

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

   public SQLStatement parseCreateSynonym() {
      OracleCreateSynonymStatement stmt = new OracleCreateSynonymStatement();
      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 parseCreateType() {
      OracleCreateTypeStatement stmt = new OracleCreateTypeStatement();
      this.accept(Token.CREATE);
      if (this.lexer.token() == Token.OR) {
         this.lexer.nextToken();
         this.accept(Token.REPLACE);
         stmt.setOrReplace(true);
      }

      this.acceptIdentifier("TYPE");
      if (this.lexer.identifierEquals("BODY")) {
         this.lexer.nextToken();
         stmt.setBody(true);
      }

      SQLName name = this.exprParser.name();
      stmt.setName(name);
      if (this.lexer.identifierEquals(FnvHash.Constants.UNDER)) {
         this.lexer.nextToken();
         SQLName under = this.exprParser.name();
         stmt.setUnder(under);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.AUTHID)) {
         this.lexer.nextToken();
         SQLName authId = this.exprParser.name();
         stmt.setAuthId(authId);
      }

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

      if (this.lexer.identifierEquals("OBJECT")) {
         this.lexer.nextToken();
         stmt.setObject(true);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.STATIC)) {
         this.parserParameters(stmt.getParameters(), stmt);
      } else if (this.lexer.token() == Token.TABLE) {
         this.lexer.nextToken();
         this.accept(Token.OF);
         SQLDataType dataType = this.exprParser.parseDataType();
         stmt.setTableOf(dataType);
      } else if (this.lexer.identifierEquals(FnvHash.Constants.VARRAY)) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         SQLExpr sizeLimit = this.exprParser.primary();
         stmt.setVarraySizeLimit(sizeLimit);
         this.accept(Token.RPAREN);
         this.accept(Token.OF);
         SQLDataType dataType = this.exprParser.parseDataType();
         stmt.setVarrayDataType(dataType);
      } else if (this.lexer.identifierEquals(FnvHash.Constants.WRAPPED)) {
         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();
         }
      } else if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         this.parserParameters(stmt.getParameters(), stmt);
         stmt.setParen(true);
         this.accept(Token.RPAREN);
      } else {
         this.parserParameters(stmt.getParameters(), stmt);
         if (this.lexer.token() == Token.END) {
            this.lexer.nextToken();
         }
      }

      while(true) {
         while(this.lexer.token() != Token.NOT) {
            if (this.lexer.identifierEquals(FnvHash.Constants.FINAL)) {
               this.lexer.nextToken();
               stmt.setFinal(true);
            } else {
               if (!this.lexer.identifierEquals(FnvHash.Constants.INSTANTIABLE)) {
                  if (this.lexer.token() == Token.SEMI) {
                     this.lexer.nextToken();
                     stmt.setAfterSemi(true);
                  }

                  return stmt;
               }

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

         this.lexer.nextToken();
         if (this.lexer.identifierEquals(FnvHash.Constants.FINAL)) {
            this.lexer.nextToken();
            stmt.setFinal(false);
         } else {
            this.acceptIdentifier("INSTANTIABLE");
            stmt.setInstantiable(false);
         }
      }
   }
}
