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

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.AutoIncrementType;
import com.alibaba.druid.sql.ast.SQLCommentHint;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLKeep;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.SQLOver;
import com.alibaba.druid.sql.ast.SQLPartition;
import com.alibaba.druid.sql.ast.SQLPartitionBy;
import com.alibaba.druid.sql.ast.SQLPartitionByHash;
import com.alibaba.druid.sql.ast.SQLPartitionByList;
import com.alibaba.druid.sql.ast.SQLPartitionByRange;
import com.alibaba.druid.sql.ast.SQLPartitionValue;
import com.alibaba.druid.sql.ast.SQLSubPartition;
import com.alibaba.druid.sql.ast.SQLSubPartitionBy;
import com.alibaba.druid.sql.ast.SQLSubPartitionByHash;
import com.alibaba.druid.sql.ast.SQLSubPartitionByList;
import com.alibaba.druid.sql.ast.SQLSubPartitionByRange;
import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;
import com.alibaba.druid.sql.ast.expr.SQLAggregateOption;
import com.alibaba.druid.sql.ast.expr.SQLArrayExpr;
import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLDbLinkExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNumberExpr;
import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.expr.SQLSequenceExpr;
import com.alibaba.druid.sql.ast.expr.SQLSizeExpr;
import com.alibaba.druid.sql.ast.expr.SQLTimestampExpr;
import com.alibaba.druid.sql.ast.expr.SQLUnaryExpr;
import com.alibaba.druid.sql.ast.expr.SQLUnaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
import com.alibaba.druid.sql.ast.statement.SQLCharacterDataType;
import com.alibaba.druid.sql.ast.statement.SQLColumnCheck;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLColumnPrimaryKey;
import com.alibaba.druid.sql.ast.statement.SQLColumnReference;
import com.alibaba.druid.sql.ast.statement.SQLColumnUniqueKey;
import com.alibaba.druid.sql.ast.statement.SQLConstraint;
import com.alibaba.druid.sql.ast.statement.SQLConstraintImpl;
import com.alibaba.druid.sql.ast.statement.SQLForeignKeyConstraint;
import com.alibaba.druid.sql.ast.statement.SQLForeignKeyImpl;
import com.alibaba.druid.sql.ast.statement.SQLNotNullConstraint;
import com.alibaba.druid.sql.ast.statement.SQLNullConstraint;
import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLUnique;
import com.alibaba.druid.sql.dialect.dm.ast.clause.DmStorageClause;
import com.alibaba.druid.sql.dialect.dm.ast.expr.DMIntervalExpr;
import com.alibaba.druid.sql.dialect.dm.ast.expr.DMIntervalType;
import com.alibaba.druid.sql.dialect.dm.ast.expr.DmAnalytic;
import com.alibaba.druid.sql.dialect.dm.ast.expr.DmAnalyticWindowing;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DMIndexTablespacleConstraint;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmConstraint;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmDataTypeIntervalYear;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmForeignKeyImpl;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmSQLColumnPrimaryKey;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmSQLColumnReference;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmSQLPrimaryKey;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.DmSQLUniqueKey;
import com.alibaba.druid.sql.dialect.dm.ast.stmt.SQLNotVisibleConstraint;
import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay;
import com.alibaba.druid.sql.dialect.oracle.ast.OracleSegmentAttributes;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleBinaryDoubleExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleBinaryFloatExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleCursorExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleDatetimeExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleIntervalExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleIntervalType;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleIsOfTypeExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleIsSetExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleMethodInvokeAccessExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleOuterExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleRangeExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSysdateExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleTreatExpr;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleXmlContentExpr;
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.Token;
import com.alibaba.druid.util.FnvHash;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;

public class DmExprParser extends SQLExprParser {
   public boolean allowStringAdditive;
   public static final String[] AGGREGATE_FUNCTIONS;
   public static final long[] AGGREGATE_FUNCTIONS_CODES;

   public DmExprParser(Lexer lexer) {
      super(lexer);
      this.allowStringAdditive = false;
      this.aggregateFunctions = AGGREGATE_FUNCTIONS;
      this.aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
      this.dbType = DbType.dm;
   }

   public DmExprParser(String text) {
      this((Lexer)(new DmLexer(text)));
      this.lexer.nextToken();
      this.dbType = DbType.dm;
   }

   public DmExprParser(String text, SQLParserFeature... features) {
      this((Lexer)(new DmLexer(text, features)));
      this.lexer.nextToken();
      this.dbType = DbType.dm;
   }

   protected SQLExpr parseInterval() {
      this.accept(Token.INTERVAL);
      DMIntervalExpr interval = new DMIntervalExpr();
      if (this.lexer.token() == Token.LITERAL_CHARS) {
         interval.setValue(new SQLCharExpr(this.lexer.stringVal()));
      } else if (this.lexer.token() == Token.VARIANT) {
         interval.setValue(new SQLVariantRefExpr(this.lexer.stringVal()));
      } else {
         if (this.lexer.token() != Token.QUES) {
            return new SQLIdentifierExpr("INTERVAL");
         }

         interval.setValue(new SQLVariantRefExpr("?"));
      }

      this.lexer.nextToken();
      DMIntervalType type;
      if (this.lexer.identifierEquals(FnvHash.Constants.YEAR)) {
         this.lexer.nextToken();
         type = DMIntervalType.YEAR;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.MONTH)) {
         this.lexer.nextToken();
         type = DMIntervalType.MONTH;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.DAY)) {
         this.lexer.nextToken();
         type = DMIntervalType.DAY;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.HOUR)) {
         this.lexer.nextToken();
         type = DMIntervalType.HOUR;
      } else if (this.lexer.identifierEquals(FnvHash.Constants.MINUTE)) {
         this.lexer.nextToken();
         type = DMIntervalType.MINUTE;
      } else {
         if (!this.lexer.identifierEquals(FnvHash.Constants.SECOND)) {
            throw new ParserException("illegal interval type. " + this.lexer.info());
         }

         this.lexer.nextToken();
         type = DMIntervalType.SECOND;
      }

      interval.setType(type);
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         if (this.lexer.token() != Token.LITERAL_INT && this.lexer.token() != Token.VARIANT) {
            throw new ParserException("syntax error. " + this.lexer.info());
         }

         interval.setPrecision(this.primary());
         if (this.lexer.token() == Token.COMMA) {
            this.lexer.nextToken();
            if (this.lexer.token() != Token.LITERAL_INT) {
               throw new ParserException("syntax error. " + this.lexer.info());
            }

            interval.setFactionalSecondsPrecision(this.lexer.integerValue().intValue());
            this.lexer.nextToken();
         }

         this.accept(Token.RPAREN);
      }

      if (this.lexer.token() == Token.TO) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals("SECOND")) {
            this.lexer.nextToken();
            interval.setToType(DMIntervalType.SECOND);
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               if (this.lexer.token() != Token.LITERAL_INT && this.lexer.token() != Token.VARIANT) {
                  throw new ParserException("syntax error. " + this.lexer.info());
               }

               interval.setToFactionalSecondsPrecision(this.primary());
               this.accept(Token.RPAREN);
            }
         } else {
            interval.setToType(DMIntervalType.MONTH);
            this.lexer.nextToken();
         }
      }

      return interval;
   }

   protected boolean isCharType(long hash) {
      return hash == FnvHash.Constants.CHAR || hash == FnvHash.Constants.CHARACTER || hash == FnvHash.Constants.NCHAR || hash == FnvHash.Constants.VARCHAR || hash == FnvHash.Constants.VARCHAR2 || hash == FnvHash.Constants.NVARCHAR || hash == FnvHash.Constants.NVARCHAR2 || hash == FnvHash.Constants.CHARACTER;
   }

   public SQLDataType parseDataType(boolean restrict) {
      if (this.lexer.token() != Token.CONSTRAINT && this.lexer.token() != Token.COMMA) {
         if (this.lexer.token() != Token.DEFAULT && this.lexer.token() != Token.NOT && this.lexer.token() != Token.NULL) {
            if (this.lexer.token() == Token.INTERVAL) {
               this.lexer.nextToken();
               if (this.lexer.identifierEquals("YEAR")) {
                  this.lexer.nextToken();
                  DmDataTypeIntervalYear interval = new DmDataTypeIntervalYear();
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     interval.addArgument(this.expr());
                     this.accept(Token.RPAREN);
                  }

                  if (this.lexer.token() == Token.TO) {
                     this.accept(Token.TO);
                     this.acceptIdentifier("MONTH");
                     interval.setToMonth(true);
                  }

                  return interval;
               } else {
                  this.acceptIdentifier("DAY");
                  OracleDataTypeIntervalDay interval = new OracleDataTypeIntervalDay();
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     interval.addArgument(this.expr());
                     this.accept(Token.RPAREN);
                  }

                  this.accept(Token.TO);
                  this.acceptIdentifier("SECOND");
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     interval.getFractionalSeconds().add(this.expr());
                     this.accept(Token.RPAREN);
                  }

                  return interval;
               }
            } else {
               String typeName;
               if (this.lexer.token() == Token.EXCEPTION) {
                  typeName = "EXCEPTION";
                  this.lexer.nextToken();
               } else if (this.lexer.identifierEquals(FnvHash.Constants.LONG)) {
                  this.lexer.nextToken();
                  if (this.lexer.identifierEquals(FnvHash.Constants.RAW)) {
                     this.lexer.nextToken();
                     typeName = "LONG RAW";
                  } else {
                     typeName = "LONG";
                  }
               } else if (this.lexer.token() == Token.CURSOR) {
                  typeName = "CURSOR";
                  this.lexer.nextToken();
               } else {
                  SQLName typeExpr = this.name();
                  typeName = typeExpr.toString();
               }

               if ("TIMESTAMP".equalsIgnoreCase(typeName)) {
                  SQLDataTypeImpl timestamp = new SQLDataTypeImpl(typeName);
                  timestamp.setDbType(this.dbType);
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     timestamp.addArgument(this.expr());
                     this.accept(Token.RPAREN);
                  }

                  if (this.lexer.token() == Token.WITH) {
                     this.lexer.nextToken();
                     if (this.lexer.identifierEquals("LOCAL")) {
                        this.lexer.nextToken();
                        timestamp.setWithLocalTimeZone(true);
                     }

                     timestamp.setWithTimeZone(true);
                     this.acceptIdentifier("TIME");
                     this.acceptIdentifier("ZONE");
                  }

                  return timestamp;
               } else if ("national".equalsIgnoreCase(typeName) && this.isCharType(this.lexer.hash_lower())) {
                  typeName = typeName + ' ' + this.lexer.stringVal();
                  this.lexer.nextToken();
                  if (this.lexer.identifierEquals("VARYING")) {
                     typeName = typeName + ' ' + this.lexer.stringVal();
                     this.lexer.nextToken();
                  }

                  SQLCharacterDataType charType = new SQLCharacterDataType(typeName);
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     SQLExpr arg = this.expr();
                     arg.setParent(charType);
                     charType.addArgument(arg);
                     this.accept(Token.RPAREN);
                  }

                  charType = (SQLCharacterDataType)this.parseCharTypeRest(charType);
                  if (this.lexer.token() == Token.HINT) {
                     List<SQLCommentHint> hints = this.parseHints();
                     charType.setHints(hints);
                  }

                  return charType;
               } else if (this.isCharType(typeName)) {
                  if (this.lexer.identifierEquals("VARYING")) {
                     typeName = typeName + ' ' + this.lexer.stringVal();
                     this.lexer.nextToken();
                  }

                  SQLCharacterDataType charType = new SQLCharacterDataType(typeName);
                  if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     charType.addArgument(this.expr());
                     if (this.lexer.identifierEquals("CHAR")) {
                        this.lexer.nextToken();
                        charType.setCharType("CHAR");
                     } else if (this.lexer.identifierEquals("BYTE")) {
                        this.lexer.nextToken();
                        charType.setCharType("BYTE");
                     }

                     this.accept(Token.RPAREN);
                  } else {
                     if (this.lexer.token() == Token.COMMA) {
                        return this.parseCharTypeRest(charType);
                     }

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

                  return this.parseCharTypeRest(charType);
               } else {
                  if (this.lexer.token() == Token.PERCENT) {
                     this.lexer.nextToken();
                     if (this.lexer.identifierEquals("TYPE")) {
                        this.lexer.nextToken();
                        typeName = typeName + "%TYPE";
                     } else {
                        if (!this.lexer.identifierEquals("ROWTYPE")) {
                           throw new ParserException("syntax error : " + this.lexer.info());
                        }

                        this.lexer.nextToken();
                        typeName = typeName + "%ROWTYPE";
                     }
                  }

                  SQLDataTypeImpl dataType = new SQLDataTypeImpl(typeName);
                  dataType.setDbType(this.dbType);
                  return this.parseDataTypeRest(dataType);
               }
            }
         } else {
            return null;
         }
      } else {
         return null;
      }
   }

   public SQLExpr primary() {
      Token tok = this.lexer.token();
      SQLExpr sqlExpr = null;
      switch (tok) {
         case BINARY_FLOAT:
            OracleBinaryFloatExpr floatExpr = new OracleBinaryFloatExpr();
            floatExpr.setValue(Float.parseFloat(this.lexer.numberString()));
            this.lexer.nextToken();
            return this.primaryRest(floatExpr);
         case BINARY_DOUBLE:
            OracleBinaryDoubleExpr doubleExpr = new OracleBinaryDoubleExpr();
            doubleExpr.setValue(Double.parseDouble(this.lexer.numberString()));
            this.lexer.nextToken();
            return this.primaryRest(doubleExpr);
         case LPAREN:
         case IDENTIFIER:
         case VARIANT:
         case QUES:
         default:
            return super.primary();
         case LITERAL_ALIAS:
            String alias = this.lexer.stringVal();
            this.lexer.nextToken();
            return this.primaryRest(new SQLIdentifierExpr(alias));
         case SYSDATE:
            this.lexer.nextToken();
            OracleSysdateExpr sysdate = new OracleSysdateExpr();
            if (this.lexer.token() == Token.MONKEYS_AT) {
               this.lexer.nextToken();
               this.accept(Token.BANG);
               sysdate.setOption("!");
            }

            return this.primaryRest(sysdate);
         case PRIOR:
            this.lexer.nextToken();
            sqlExpr = this.expr();
            SQLExpr var18 = new SQLUnaryExpr(SQLUnaryOperator.Prior, sqlExpr);
            return this.primaryRest(var18);
         case COLON:
            this.lexer.nextToken();
            if (this.lexer.token() == Token.LITERAL_INT) {
               String name = ":" + this.lexer.numberString();
               this.lexer.nextToken();
               return new SQLVariantRefExpr(name);
            } else {
               if (this.lexer.token() == Token.IDENTIFIER) {
                  String name = this.lexer.stringVal();
                  if (name.charAt(0) != 'B' && name.charAt(0) != 'b') {
                     throw new ParserException("syntax error : " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  return new SQLVariantRefExpr(":" + name);
               }

               throw new ParserException("syntax error : " + this.lexer.info());
            }
         case TABLE:
            this.lexer.nextToken();
            return this.primaryRest(new SQLIdentifierExpr("TABLE"));
         case PLUS:
            this.lexer.nextToken();
            switch (this.lexer.token()) {
               case LITERAL_INT:
                  sqlExpr = new SQLIntegerExpr(this.lexer.integerValue());
                  this.lexer.nextToken();
                  break;
               case LITERAL_FLOAT:
                  sqlExpr = this.lexer.numberExpr();
                  this.lexer.nextToken();
                  break;
               case BINARY_FLOAT:
                  sqlExpr = new OracleBinaryFloatExpr(Float.parseFloat(this.lexer.numberString()));
                  this.lexer.nextToken();
                  break;
               case BINARY_DOUBLE:
                  sqlExpr = new OracleBinaryDoubleExpr(Double.parseDouble(this.lexer.numberString()));
                  this.lexer.nextToken();
                  break;
               case LPAREN:
                  this.lexer.nextToken();
                  sqlExpr = this.expr();
                  this.accept(Token.RPAREN);
                  sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Plus, sqlExpr);
                  break;
               case IDENTIFIER:
                  sqlExpr = this.expr();
                  sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Plus, sqlExpr);
                  break;
               default:
                  throw new ParserException("TODO " + this.lexer.info());
            }

            return this.primaryRest(sqlExpr);
         case SUB:
            this.lexer.nextToken();
            switch (this.lexer.token()) {
               case LITERAL_INT:
                  Number integerValue = this.lexer.integerValue();
                  Object var22;
                  if (integerValue instanceof Integer) {
                     int intVal = (Integer)integerValue;
                     if (intVal == Integer.MIN_VALUE) {
                        var22 = (long)intVal * -1L;
                     } else {
                        var22 = intVal * -1;
                     }
                  } else if (integerValue instanceof Long) {
                     long longVal = (Long)integerValue;
                     if (longVal == 2147483648L) {
                        var22 = (int)(longVal * -1L);
                     } else {
                        var22 = longVal * -1L;
                     }
                  } else {
                     var22 = ((BigInteger)integerValue).negate();
                  }

                  sqlExpr = new SQLIntegerExpr((Number)var22);
                  this.lexer.nextToken();
                  break;
               case LITERAL_FLOAT:
                  sqlExpr = this.lexer.numberExpr(true);
                  this.lexer.nextToken();
                  break;
               case BINARY_FLOAT:
                  sqlExpr = new OracleBinaryFloatExpr(Float.parseFloat(this.lexer.numberString()) * -1.0F);
                  this.lexer.nextToken();
                  break;
               case BINARY_DOUBLE:
                  sqlExpr = new OracleBinaryDoubleExpr(Double.parseDouble(this.lexer.numberString()) * (double)-1.0F);
                  this.lexer.nextToken();
                  break;
               case LPAREN:
                  this.lexer.nextToken();
                  sqlExpr = this.expr();
                  this.accept(Token.RPAREN);
                  sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, sqlExpr);
                  break;
               case IDENTIFIER:
               case VARIANT:
               case QUES:
               case LITERAL_ALIAS:
                  sqlExpr = this.expr();
                  sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, sqlExpr);
                  break;
               default:
                  throw new ParserException("TODO " + this.lexer.info());
            }

            return this.primaryRest(sqlExpr);
         case CURSOR:
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            SQLSelect select = this.createSelectParser().select();
            OracleCursorExpr cursorExpr = new OracleCursorExpr(select);
            this.accept(Token.RPAREN);
            return this.primaryRest(cursorExpr);
         case PCTFREE:
         case INITRANS:
         case MAXTRANS:
         case SEGMENT:
         case CREATION:
         case IMMEDIATE:
         case DEFERRED:
         case STORAGE:
         case NEXT:
         case MINEXTENTS:
         case MAXEXTENTS:
         case MAXSIZE:
         case PCTINCREASE:
         case FLASH_CACHE:
         case CELL_FLASH_CACHE:
         case NONE:
         case LOB:
         case STORE:
         case ROW:
         case CHUNK:
         case CACHE:
         case NOCACHE:
         case LOGGING:
         case NOCOMPRESS:
         case KEEP_DUPLICATES:
         case EXCEPTIONS:
         case PURGE:
         case OUTER:
            SQLExpr var10 = new SQLIdentifierExpr(this.lexer.stringVal());
            this.lexer.nextToken();
            return this.primaryRest(var10);
         case DOCUMENT:
         case CONTENT:
            OracleXmlContentExpr expr = new OracleXmlContentExpr();
            expr.setPlaceholder(new SQLIdentifierExpr(this.lexer.stringVal()));
            this.lexer.nextToken();
            expr.setValue(this.expr());
            if (this.lexer.token() == Token.WELLFORMED) {
               expr.setWellformed(true);
               this.lexer.nextToken();
            }

            return expr;
      }
   }

   protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) {
      if (acceptLPAREN) {
         this.accept(Token.LPAREN);
      }

      if (this.lexer.token() == Token.PLUS) {
         this.lexer.nextToken();
         this.accept(Token.RPAREN);
         return new OracleOuterExpr(expr);
      } else {
         if (expr instanceof SQLIdentifierExpr) {
            String methodName = ((SQLIdentifierExpr)expr).getName();
            new SQLMethodInvokeExpr(methodName);
            if ("treat".equalsIgnoreCase(methodName)) {
               OracleTreatExpr treatExpr = new OracleTreatExpr();
               treatExpr.setExpr(this.expr());
               this.accept(Token.AS);
               if (this.lexer.identifierEquals("REF")) {
                  treatExpr.setRef(true);
                  this.lexer.nextToken();
               }

               treatExpr.setType(this.expr());
               this.accept(Token.RPAREN);
               return this.primaryRest(treatExpr);
            }
         }

         SQLExpr method = super.methodRest(expr, false);
         this.lexer.mark();
         this.lexer.nextToken();
         if (this.lexer.token() == Token.DOT) {
            SQLExpr call = this.expr();
            OracleMethodInvokeAccessExpr omiaExpr = new OracleMethodInvokeAccessExpr();
            omiaExpr.setMethod(method);
            omiaExpr.setCall(call);
            return omiaExpr;
         } else {
            this.lexer.reset();
            return method;
         }
      }
   }

   public SQLExpr primaryRest(SQLExpr expr) {
      if (expr instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr identExpr = (SQLIdentifierExpr)expr;
         String ident = ((SQLIdentifierExpr)expr).getName();
         long hashCode64 = identExpr.hashCode64();
         if (hashCode64 == FnvHash.Constants.TIMESTAMP) {
            if (this.lexer.token() != Token.LITERAL_ALIAS && this.lexer.token() != Token.LITERAL_CHARS) {
               return new SQLIdentifierExpr("TIMESTAMP");
            }

            SQLTimestampExpr timestamp = new SQLTimestampExpr();
            String literal = this.lexer.stringVal();
            timestamp.setLiteral(literal);
            this.accept(Token.LITERAL_CHARS);
            if (this.lexer.identifierEquals("AT")) {
               this.lexer.nextToken();
               this.acceptIdentifier("TIME");
               this.acceptIdentifier("ZONE");
               String timezone = this.lexer.stringVal();
               timestamp.setTimeZone(timezone);
               this.accept(Token.LITERAL_CHARS);
            }

            return this.primaryRest(timestamp);
         }
      }

      if (this.lexer.token() == Token.IDENTIFIER && expr instanceof SQLNumericLiteralExpr) {
         String ident = this.lexer.stringVal();
         if (ident.length() == 1) {
            char unit = ident.charAt(0);
            switch (unit) {
               case 'E':
               case 'G':
               case 'K':
               case 'M':
               case 'P':
               case 'T':
               case 'e':
               case 'g':
               case 'k':
               case 'm':
               case 'p':
               case 't':
                  expr = new SQLSizeExpr(expr, SQLSizeExpr.Unit.valueOf(ident.toUpperCase()));
                  this.lexer.nextToken();
               case 'F':
               case 'H':
               case 'I':
               case 'J':
               case 'L':
               case 'N':
               case 'O':
               case 'Q':
               case 'R':
               case 'S':
               case 'U':
               case 'V':
               case 'W':
               case 'X':
               case 'Y':
               case 'Z':
               case '[':
               case '\\':
               case ']':
               case '^':
               case '_':
               case '`':
               case 'a':
               case 'b':
               case 'c':
               case 'd':
               case 'f':
               case 'h':
               case 'i':
               case 'j':
               case 'l':
               case 'n':
               case 'o':
               case 'q':
               case 'r':
               case 's':
            }
         }
      }

      if (this.lexer.token() == Token.DOTDOT) {
         this.lexer.nextToken();
         SQLExpr upBound = this.expr();
         return new OracleRangeExpr(expr, upBound);
      } else {
         if (this.lexer.token() == Token.MONKEYS_AT) {
            this.lexer.nextToken();
            SQLDbLinkExpr dblink = new SQLDbLinkExpr();
            dblink.setExpr(expr);
            if (this.lexer.token() == Token.BANG) {
               dblink.setDbLink("!");
               this.lexer.nextToken();
            } else {
               String link = this.lexer.stringVal();
               if (this.lexer.token() == Token.LITERAL_ALIAS) {
                  this.accept(Token.LITERAL_ALIAS);
               } else {
                  this.accept(Token.IDENTIFIER);
               }

               dblink.setDbLink(link);
               if (this.lexer.token() == Token.LPAREN) {
                  SQLExpr dblinkExpr = dblink.getExpr();
                  if (dblinkExpr.getClass() == SQLPropertyExpr.class) {
                     SQLExpr methodInvokeExpr = new SQLMethodInvokeExpr();
                     SQLMethodInvokeExpr expr2 = (SQLMethodInvokeExpr)this.methodRest(methodInvokeExpr, true);
                     expr2.setOwner(((SQLPropertyExpr)expr).getOwner());
                     expr2.setMethodName(((SQLPropertyExpr)expr).getName());
                     dblink.setExpr(expr2);
                  }
               }
            }

            expr = dblink;
         }

         if (this.lexer.token() == Token.LBRACKET) {
            SQLArrayExpr arrayExpr = new SQLArrayExpr();
            arrayExpr.setExpr(expr);
            this.lexer.nextToken();
            this.exprList(arrayExpr.getValues(), arrayExpr);
            this.accept(Token.RBRACKET);
            expr = this.primaryRest(arrayExpr);
         }

         if (this.lexer.identifierEquals("SECOND") || this.lexer.identifierEquals("MINUTE") || this.lexer.identifierEquals("HOUR") || this.lexer.identifierEquals("DAY") || this.lexer.identifierEquals("MONTH") || this.lexer.identifierEquals("YEAR")) {
            Lexer.SavePoint savePoint = this.lexer.mark();
            String name = this.lexer.stringVal();
            this.lexer.nextToken();
            if (this.lexer.token() == Token.COMMA) {
               this.lexer.reset(savePoint);
               return expr;
            }

            OracleIntervalExpr interval = new OracleIntervalExpr();
            interval.setValue(expr);
            OracleIntervalType type = OracleIntervalType.valueOf(name.toUpperCase());
            interval.setType(type);
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               if (this.lexer.token() != Token.LITERAL_INT) {
                  throw new ParserException("syntax error. " + this.lexer.info());
               }

               interval.setPrecision(this.lexer.integerValue().intValue());
               this.lexer.nextToken();
               this.accept(Token.RPAREN);
            }

            if (this.lexer.token() != Token.TO) {
               this.lexer.reset(savePoint);
               return expr;
            }

            this.lexer.nextToken();
            if (this.lexer.identifierEquals("SECOND")) {
               this.lexer.nextToken();
               interval.setToType(OracleIntervalType.SECOND);
               if (this.lexer.token() == Token.LPAREN) {
                  this.lexer.nextToken();
                  if (this.lexer.token() != Token.LITERAL_INT) {
                     throw new ParserException("syntax error. " + this.lexer.info());
                  }

                  interval.setFactionalSecondsPrecision(this.lexer.integerValue().intValue());
                  this.lexer.nextToken();
                  this.accept(Token.RPAREN);
               }
            } else {
               interval.setToType(OracleIntervalType.MONTH);
               this.lexer.nextToken();
            }

            expr = interval;
         }

         if (this.lexer.identifierEquals("AT")) {
            Lexer.SavePoint mark = this.lexer.mark();
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("LOCAL")) {
               this.lexer.nextToken();
               expr = new OracleDatetimeExpr(expr, new SQLIdentifierExpr("LOCAL"));
            } else {
               if (!this.lexer.identifierEquals("TIME")) {
                  this.lexer.reset(mark);
                  return expr;
               }

               this.lexer.nextToken();
               this.acceptIdentifier("ZONE");
               SQLExpr timeZone = this.primary();
               expr = new OracleDatetimeExpr(expr, timeZone);
            }
         }

         SQLExpr restExpr = super.primaryRest(expr);
         if (restExpr != expr && restExpr instanceof SQLMethodInvokeExpr) {
            SQLMethodInvokeExpr methodInvoke = (SQLMethodInvokeExpr)restExpr;
            if (methodInvoke.getArguments().size() == 1) {
               SQLExpr paramExpr = (SQLExpr)methodInvoke.getArguments().get(0);
               if (paramExpr instanceof SQLIdentifierExpr && "+".equals(((SQLIdentifierExpr)paramExpr).getName())) {
                  OracleOuterExpr outerExpr = new OracleOuterExpr();
                  if (methodInvoke.getOwner() == null) {
                     outerExpr.setExpr(new SQLIdentifierExpr(methodInvoke.getMethodName()));
                  } else {
                     outerExpr.setExpr(new SQLPropertyExpr(methodInvoke.getOwner(), methodInvoke.getMethodName()));
                  }

                  return outerExpr;
               }
            }
         }

         return restExpr;
      }
   }

   protected SQLExpr dotRest(SQLExpr expr) {
      if (this.lexer.token() == Token.LITERAL_ALIAS) {
         String name = this.lexer.stringVal();
         this.lexer.nextToken();
         expr = new SQLPropertyExpr(expr, name);
         if (this.lexer.token() == Token.DOT) {
            this.lexer.nextToken();
            expr = this.dotRest(expr);
         }

         return expr;
      } else {
         if (this.lexer.identifierEquals(FnvHash.Constants.NEXTVAL)) {
            if (expr instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr identExpr = (SQLIdentifierExpr)expr;
               SQLSequenceExpr seqExpr = new SQLSequenceExpr(identExpr, SQLSequenceExpr.Function.NextVal);
               this.lexer.nextToken();
               return seqExpr;
            }
         } else if (this.lexer.identifierEquals(FnvHash.Constants.CURRVAL) && expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identExpr = (SQLIdentifierExpr)expr;
            SQLSequenceExpr seqExpr = new SQLSequenceExpr(identExpr, SQLSequenceExpr.Function.CurrVal);
            this.lexer.nextToken();
            return seqExpr;
         }

         return super.dotRest(expr);
      }
   }

   protected SQLAggregateExpr parseAggregateExpr(String methodName) {
      SQLAggregateExpr aggregateExpr;
      if (this.lexer.token() == Token.UNIQUE) {
         aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.UNIQUE);
         this.lexer.nextToken();
      } else if (this.lexer.token() == Token.ALL) {
         aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.ALL);
         this.lexer.nextToken();
      } else if (this.lexer.token() == Token.DISTINCT) {
         aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.DISTINCT);
         this.lexer.nextToken();
      } else {
         aggregateExpr = new SQLAggregateExpr(methodName);
      }

      this.exprList(aggregateExpr.getArguments(), aggregateExpr);
      if (this.lexer.stringVal().equalsIgnoreCase("IGNORE")) {
         this.lexer.nextToken();
         this.acceptIdentifier("NULLS");
         aggregateExpr.setIgnoreNulls(true);
      } else if (this.lexer.identifierEquals(FnvHash.Constants.RESPECT)) {
         this.lexer.nextToken();
         this.acceptIdentifier("NULLS");
         aggregateExpr.setIgnoreNulls(false);
      }

      this.accept(Token.RPAREN);
      if (this.lexer.stringVal().equalsIgnoreCase("IGNORE")) {
         this.lexer.nextToken();
         this.acceptIdentifier("NULLS");
         aggregateExpr.setIgnoreNulls(true);
      } else if (this.lexer.identifierEquals(FnvHash.Constants.RESPECT)) {
         this.lexer.nextToken();
         this.acceptIdentifier("NULLS");
         aggregateExpr.setIgnoreNulls(false);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.WITHIN)) {
         this.lexer.nextToken();
         this.accept(Token.GROUP);
         this.accept(Token.LPAREN);
         SQLOrderBy orderBy = this.parseOrderBy();
         aggregateExpr.setWithinGroup(true);
         aggregateExpr.setOrderBy(orderBy);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.identifierEquals("KEEP")) {
         this.lexer.nextToken();
         SQLKeep keep = new SQLKeep();
         this.accept(Token.LPAREN);
         this.acceptIdentifier("DENSE_RANK");
         if (this.lexer.identifierEquals("FIRST")) {
            this.lexer.nextToken();
            keep.setDenseRank(SQLKeep.DenseRank.FIRST);
         } else {
            this.acceptIdentifier("LAST");
            keep.setDenseRank(SQLKeep.DenseRank.LAST);
         }

         SQLOrderBy orderBy = this.parseOrderBy();
         keep.setOrderBy(orderBy);
         aggregateExpr.setKeep(keep);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.token() == Token.OVER) {
         DmAnalytic over = new DmAnalytic();
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         if (this.lexer.token() == Token.PARTITION) {
            this.lexer.nextToken();
            this.accept(Token.BY);
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();
               this.exprList(over.getPartitionBy(), over);
               this.accept(Token.RPAREN);
            } else {
               this.exprList(over.getPartitionBy(), over);
            }
         }

         SQLOrderBy orderBy = this.parseOrderBy();
         if (orderBy != null) {
            over.setOrderBy(orderBy);
            DmAnalyticWindowing windowing = null;
            if (this.lexer.identifierEquals(FnvHash.Constants.ROWS)) {
               this.lexer.nextToken();
               windowing = new DmAnalyticWindowing();
               windowing.setType(DmAnalyticWindowing.Type.ROWS);
            } else if (this.lexer.identifierEquals(FnvHash.Constants.RANGE)) {
               this.lexer.nextToken();
               windowing = new DmAnalyticWindowing();
               windowing.setType(DmAnalyticWindowing.Type.RANGE);
            }

            if (windowing != null) {
               if (this.lexer.identifierEquals(FnvHash.Constants.CURRENT)) {
                  this.lexer.nextToken();
                  this.accept(Token.ROW);
                  windowing.setExpr(new SQLIdentifierExpr("CURRENT ROW"));
                  over.setWindowing(windowing);
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.UNBOUNDED)) {
                  this.lexer.nextToken();
                  if (!this.lexer.stringVal().equalsIgnoreCase("PRECEDING")) {
                     throw new ParserException("syntax error. " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  windowing.setExpr(new SQLIdentifierExpr("UNBOUNDED PRECEDING"));
               } else if (this.lexer.token() == Token.BETWEEN) {
                  this.lexer.nextToken();
                  SQLExpr beginExpr;
                  if (this.lexer.identifierEquals(FnvHash.Constants.CURRENT)) {
                     this.lexer.nextToken();
                     this.accept(Token.ROW);
                     beginExpr = new SQLIdentifierExpr("CURRENT ROW");
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.UNBOUNDED)) {
                     this.lexer.nextToken();
                     if (!this.lexer.stringVal().equalsIgnoreCase("PRECEDING")) {
                        throw new ParserException("syntax error. " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     beginExpr = new SQLIdentifierExpr("UNBOUNDED PRECEDING");
                  } else {
                     beginExpr = this.relational();
                  }

                  SQLOver.WindowingBound beginBound = this.parseWindowingBound();
                  if (beginBound != null) {
                     over.setWindowingBetweenBeginBound(beginBound);
                  }

                  this.accept(Token.AND);
                  SQLExpr endExpr;
                  if (this.lexer.identifierEquals(FnvHash.Constants.CURRENT)) {
                     this.lexer.nextToken();
                     this.accept(Token.ROW);
                     endExpr = new SQLIdentifierExpr("CURRENT ROW");
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.UNBOUNDED)) {
                     this.lexer.nextToken();
                     if (!this.lexer.stringVal().equalsIgnoreCase("PRECEDING")) {
                        throw new ParserException("syntax error. " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     endExpr = new SQLIdentifierExpr("UNBOUNDED PRECEDING");
                  } else {
                     endExpr = this.relational();
                  }

                  SQLExpr expr = new SQLBetweenExpr((SQLExpr)null, beginExpr, endExpr);
                  windowing.setExpr(expr);
               } else {
                  SQLExpr expr = this.expr();
                  windowing.setExpr(expr);
                  this.acceptIdentifier("PRECEDING");
                  over.setWindowingPreceding(true);
               }

               over.setWindowing(windowing);
            }
         }

         this.accept(Token.RPAREN);
         aggregateExpr.setOver(over);
      }

      return aggregateExpr;
   }

   private OracleIntervalType parseIntervalType() {
      String currentTokenUpperValue = this.lexer.stringVal();
      this.lexer.nextToken();
      if (currentTokenUpperValue.equals("YEAR")) {
         return OracleIntervalType.YEAR;
      } else if (currentTokenUpperValue.equals("MONTH")) {
         return OracleIntervalType.MONTH;
      } else if (currentTokenUpperValue.equals("HOUR")) {
         return OracleIntervalType.HOUR;
      } else if (currentTokenUpperValue.equals("MINUTE")) {
         return OracleIntervalType.MINUTE;
      } else if (currentTokenUpperValue.equals("SECOND")) {
         return OracleIntervalType.SECOND;
      } else {
         throw new ParserException("syntax error. " + this.lexer.info());
      }
   }

   public DmSelectParser createSelectParser() {
      return new DmSelectParser(this);
   }

   public SQLExpr relationalRest(SQLExpr expr) {
      if (this.lexer.token() != Token.IS) {
         return super.relationalRest(expr);
      } else {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.NOT) {
            this.lexer.nextToken();
            SQLExpr rightExpr = this.primary();
            expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.IsNot, rightExpr, this.getDbType());
         } else if (this.lexer.identifierEquals("A")) {
            this.lexer.nextToken();
            this.accept(Token.SET);
            expr = new OracleIsSetExpr(expr);
         } else if (this.lexer.token() == Token.OF) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
               this.lexer.nextToken();
            }

            OracleIsOfTypeExpr isOf = new OracleIsOfTypeExpr();
            isOf.setExpr(expr);
            this.accept(Token.LPAREN);

            while(true) {
               boolean only = this.lexer.identifierEquals(FnvHash.Constants.ONLY);
               if (only) {
                  this.lexer.nextToken();
               }

               SQLExpr type = this.name();
               if (only) {
                  type.putAttribute("ONLY", true);
               }

               type.setParent(isOf);
               isOf.getTypes().add(type);
               if (this.lexer.token() != Token.COMMA) {
                  this.accept(Token.RPAREN);
                  expr = isOf;
                  break;
               }

               this.lexer.nextToken();
            }
         } else {
            SQLExpr rightExpr = this.primary();
            expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Is, rightExpr, this.getDbType());
         }

         return expr;
      }
   }

   public SQLName name() {
      SQLName name = super.name();
      if (this.lexer.token() != Token.MONKEYS_AT) {
         return name;
      } else {
         this.lexer.nextToken();
         if (this.lexer.token() != Token.IDENTIFIER) {
            throw new ParserException("syntax error, expect identifier, but " + this.lexer.token() + ", " + this.lexer.info());
         } else {
            SQLDbLinkExpr dbLink = new SQLDbLinkExpr();
            dbLink.setExpr(name);
            String link = this.lexer.stringVal();
            this.lexer.nextToken();

            while(this.lexer.token() == Token.DOT) {
               this.lexer.nextToken();
               String stringVal = this.lexer.stringVal();
               this.accept(Token.IDENTIFIER);
               link = link + "." + stringVal;
            }

            dbLink.setDbLink(link);
            return dbLink;
         }
      }
   }

   public SQLExpr exprRest(SQLExpr expr) {
      expr = super.exprRest(expr);
      if (this.lexer.token() == Token.COLONEQ) {
         this.lexer.nextToken();
         SQLExpr right = this.expr();
         expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Assignment, right, this.getDbType());
      }

      return expr;
   }

   public SQLPartition parsePartition() {
      this.accept(Token.PARTITION);
      SQLPartition partition = new SQLPartition();
      partition.setName(this.name());
      SQLPartitionValue values = this.parsePartitionValues();
      if (values != null) {
         this.parseSegmentAttributes(partition);
      }

      if (values != null) {
         partition.setValues(values);
      }

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

         while(true) {
            SQLSubPartition subPartition = this.parseSubPartition();
            this.parseSegmentAttributes(subPartition);
            partition.addSubPartition(subPartition);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      } else if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITIONS)) {
         this.lexer.nextToken();
         SQLExpr subPartitionsCount = this.primary();
         partition.setSubPartitionsCount(subPartitionsCount);
      }

      while(true) {
         this.parseSegmentAttributes(partition);
         if (this.lexer.token() != Token.SEGMENT && !this.lexer.identifierEquals("SEGMENT")) {
            if (this.lexer.token() == Token.LPAREN) {
               this.lexer.nextToken();

               while(true) {
                  SQLSubPartition subPartition = this.parseSubPartition();
                  this.parseSegmentAttributes(subPartition);
                  partition.addSubPartition(subPartition);
                  if (this.lexer.token() != Token.COMMA) {
                     this.accept(Token.RPAREN);
                     break;
                  }

                  this.lexer.nextToken();
               }
            }

            return partition;
         }

         this.lexer.nextToken();
         this.accept(Token.CREATION);
         if (this.lexer.token() == Token.IMMEDIATE) {
            this.lexer.nextToken();
            partition.setSegmentCreationImmediate(true);
         } else if (this.lexer.token() == Token.DEFERRED) {
            this.lexer.nextToken();
            partition.setSegmentCreationDeferred(true);
         }
      }
   }

   protected SQLPartitionBy parsePartitionBy() {
      this.lexer.nextToken();
      this.accept(Token.BY);
      if (this.lexer.identifierEquals("RANGE")) {
         return this.partitionByRange();
      } else if (!this.lexer.identifierEquals("HASH")) {
         if (this.lexer.identifierEquals("LIST")) {
            SQLPartitionByList partitionByList = this.partitionByList();
            this.partitionClauseRest((SQLPartitionBy)partitionByList);
            return partitionByList;
         } else {
            throw new ParserException("TODO : " + this.lexer.info());
         }
      } else {
         SQLPartitionByHash partitionByHash = this.partitionByHash();
         this.partitionClauseRest((SQLPartitionBy)partitionByHash);
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();

            while(true) {
               SQLPartition partition = this.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();
            }
         }

         return partitionByHash;
      }
   }

   protected SQLPartitionByList partitionByList() {
      this.acceptIdentifier("LIST");
      SQLPartitionByList partitionByList = new SQLPartitionByList();
      this.accept(Token.LPAREN);
      partitionByList.addColumn(this.expr());
      this.accept(Token.RPAREN);
      this.parsePartitionByRest(partitionByList);
      return partitionByList;
   }

   protected SQLSubPartition parseSubPartition() {
      this.acceptIdentifier("SUBPARTITION");
      SQLSubPartition subPartition = new SQLSubPartition();
      SQLName name = this.name();
      subPartition.setName(name);
      SQLPartitionValue values = this.parsePartitionValues();
      if (values != null) {
         subPartition.setValues(values);
      }

      if (this.lexer.token() == Token.TABLESPACE) {
         this.lexer.nextToken();
         subPartition.setTableSpace(this.name());
      }

      if (this.lexer.token() == Token.STORAGE || this.lexer.identifierEquals("STORAGE")) {
         DmStorageClause storage = this.parseStorage();
         subPartition.setStorage(storage);
      }

      return subPartition;
   }

   public void parseSegmentAttributes(OracleSegmentAttributes attributes) {
      while(true) {
         if (this.lexer.token() == Token.TABLESPACE) {
            this.lexer.nextToken();
            attributes.setTablespace(this.name());
         } else if (this.lexer.token() != Token.NOCOMPRESS && !this.lexer.identifierEquals("NOCOMPRESS")) {
            if (this.lexer.identifierEquals(FnvHash.Constants.COMPRESS)) {
               this.lexer.nextToken();
               attributes.setCompress(Boolean.TRUE);
               if (this.lexer.token() == Token.LITERAL_INT) {
                  int compressLevel = this.parseIntValue();
                  attributes.setCompressLevel(compressLevel);
               } else if (this.lexer.identifierEquals("BASIC")) {
                  this.lexer.nextToken();
               } else if (this.lexer.token() == Token.FOR) {
                  this.lexer.nextToken();
                  if (!this.lexer.identifierEquals("OLTP")) {
                     throw new ParserException("TODO : " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  attributes.setCompressForOltp(true);
               }
            } else if (this.lexer.identifierEquals("NOCOMPRESS")) {
               this.lexer.nextToken();
               attributes.setCompress(Boolean.FALSE);
            } else if (this.lexer.token() != Token.LOGGING && !this.lexer.identifierEquals("LOGGING")) {
               if (this.lexer.identifierEquals("NOLOGGING")) {
                  this.lexer.nextToken();
                  attributes.setLogging(Boolean.FALSE);
               } else if (this.lexer.token() == Token.INITRANS) {
                  this.lexer.nextToken();
                  attributes.setInitrans(this.parseIntValue());
               } else if (this.lexer.token() == Token.MAXTRANS) {
                  this.lexer.nextToken();
                  attributes.setMaxtrans(this.parseIntValue());
               } else if (this.lexer.token() == Token.PCTINCREASE) {
                  this.lexer.nextToken();
                  attributes.setPctincrease(this.parseIntValue());
               } else if (this.lexer.token() == Token.PCTFREE) {
                  this.lexer.nextToken();
                  attributes.setPctfree(this.parseIntValue());
               } else if (this.lexer.token() != Token.STORAGE && !this.lexer.identifierEquals("STORAGE")) {
                  if (this.lexer.identifierEquals(FnvHash.Constants.PCTUSED)) {
                     this.lexer.nextToken();
                     attributes.setPctused(this.parseIntValue());
                  } else {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.USAGE)) {
                        return;
                     }

                     this.lexer.nextToken();
                     this.acceptIdentifier("QUEUE");
                  }
               } else {
                  DmStorageClause storage = this.parseStorage();
                  attributes.setStorage(storage);
               }
            } else {
               this.lexer.nextToken();
               attributes.setLogging(Boolean.TRUE);
            }
         } else {
            this.lexer.nextToken();
            attributes.setCompress(Boolean.FALSE);
         }
      }
   }

   protected SQLPartitionByRange partitionByRange() {
      this.acceptIdentifier("RANGE");
      this.accept(Token.LPAREN);
      SQLPartitionByRange clause = new SQLPartitionByRange();

      while(true) {
         SQLName column = this.name();
         clause.addColumn(column);
         if (this.lexer.token() != Token.COMMA) {
            this.accept(Token.RPAREN);
            if (this.lexer.token() == Token.INTERVAL) {
               this.lexer.nextToken();
               this.accept(Token.LPAREN);
               clause.setInterval(this.expr());
               this.accept(Token.RPAREN);
            }

            this.parsePartitionByRest(clause);
            return clause;
         }

         this.lexer.nextToken();
      }
   }

   protected void parsePartitionByRest(SQLPartitionBy clause) {
      if (this.lexer.token() == Token.STORE) {
         this.lexer.nextToken();
         this.accept(Token.IN);
         this.accept(Token.LPAREN);

         while(true) {
            SQLName tablespace = this.name();
            clause.getStoreIn().add(tablespace);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITION)) {
         SQLSubPartitionBy subPartitionBy = this.subPartitionBy();
         clause.setSubPartitionBy(subPartitionBy);
         if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITIONS)) {
            this.lexer.nextToken();
            Number intValue = this.lexer.integerValue();
            SQLNumberExpr numExpr = new SQLNumberExpr(intValue);
            subPartitionBy.setSubPartitionsCount(numExpr);
            this.lexer.nextToken();
         }
      }

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

         while(true) {
            SQLPartition partition = this.parsePartition();
            clause.addPartition(partition);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               break;
            }

            this.lexer.nextToken();
         }
      }

   }

   protected SQLSubPartitionBy subPartitionBy() {
      this.lexer.nextToken();
      this.accept(Token.BY);
      if (this.lexer.identifierEquals(FnvHash.Constants.HASH)) {
         this.lexer.nextToken();
         this.accept(Token.LPAREN);
         SQLSubPartitionByHash byHash = new SQLSubPartitionByHash();
         SQLExpr expr = this.expr();
         byHash.setExpr(expr);
         this.accept(Token.RPAREN);
         this.parseSubpartitionsTemplate(byHash);
         return byHash;
      } else if (!this.lexer.identifierEquals(FnvHash.Constants.RANGE)) {
         if (this.lexer.identifierEquals(FnvHash.Constants.LIST)) {
            this.lexer.nextToken();
            this.accept(Token.LPAREN);
            SQLSubPartitionByList byList = new SQLSubPartitionByList();
            SQLName column = this.name();
            byList.setColumn(column);
            this.accept(Token.RPAREN);
            this.parseSubpartitionsTemplate(byList);
            if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITIONS)) {
               this.lexer.nextToken();
               Number intValue = this.lexer.integerValue();
               SQLNumberExpr numExpr = new SQLNumberExpr(intValue);
               byList.setSubPartitionsCount(numExpr);
               this.lexer.nextToken();
            }

            return byList;
         } else {
            throw new ParserException("TODO : " + this.lexer.info());
         }
      } else {
         this.lexer.nextToken();
         SQLSubPartitionByRange range = new SQLSubPartitionByRange();
         this.accept(Token.LPAREN);

         while(true) {
            SQLName column = this.name();
            range.getColumns().add(column);
            if (this.lexer.token() != Token.COMMA) {
               this.accept(Token.RPAREN);
               this.parseSubpartitionsTemplate(range);
               return range;
            }

            this.lexer.nextToken();
         }
      }
   }

   private void parseSubpartitionsTemplate(SQLSubPartitionBy by) {
      if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITION)) {
         this.lexer.nextToken();
         this.acceptIdentifier("TEMPLATE");
         if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITIONS)) {
            if (by instanceof SQLSubPartitionByHash) {
               this.partitionClauseRest((SQLSubPartitionByHash)by);
            }
         } else {
            this.accept(Token.LPAREN);

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

               this.lexer.nextToken();
            }
         }
      }

   }

   protected void partitionClauseRest(SQLSubPartitionByHash clause) {
      if (this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITIONS)) {
         this.lexer.nextToken();
         SQLIntegerExpr countExpr = this.integerExpr();
         clause.setSubPartitionsCount(countExpr);
      }

      if (this.lexer.token() == Token.STORE) {
         this.lexer.nextToken();
         this.accept(Token.IN);
         this.accept(Token.LPAREN);
         this.names(clause.getStoreIn(), clause);
         this.accept(Token.RPAREN);
      }

   }

   protected void partitionClauseRest(SQLPartitionBy clause) {
      if (this.lexer.identifierEquals(FnvHash.Constants.PARTITIONS)) {
         this.lexer.nextToken();
         SQLIntegerExpr countExpr = this.integerExpr();
         clause.setPartitionsCount(countExpr);
      }

      if (this.lexer.token() == Token.STORE) {
         this.lexer.nextToken();
         this.accept(Token.IN);
         this.accept(Token.LPAREN);
         this.names(clause.getStoreIn(), clause);
         this.accept(Token.RPAREN);
      }

   }

   protected SQLPartitionByHash partitionByHash() {
      this.acceptIdentifier("HASH");
      SQLPartitionByHash partitionByHash = new SQLPartitionByHash();
      if (this.lexer.token() == Token.KEY) {
         this.lexer.nextToken();
         partitionByHash.setKey(true);
      }

      this.accept(Token.LPAREN);
      this.exprList(partitionByHash.getColumns(), partitionByHash);
      this.accept(Token.RPAREN);
      this.parsePartitionByRest(partitionByHash);
      return partitionByHash;
   }

   public DmStorageClause parseStorage() {
      this.lexer.nextToken();
      this.accept(Token.LPAREN);
      DmStorageClause storage = new DmStorageClause();

      while(true) {
         if (this.lexer.token() == Token.COMMA) {
            this.accept(Token.COMMA);
         }

         if (this.lexer.identifierEquals("INITIAL")) {
            this.lexer.nextToken();
            storage.setInitial(this.expr());
         } else if (this.lexer.token() == Token.NEXT) {
            this.lexer.nextToken();
            storage.setNext(this.expr());
         } else if (this.lexer.token() == Token.MINEXTENTS) {
            this.lexer.nextToken();
            storage.setMinExtents(this.expr());
         } else if (this.lexer.token() == Token.ON) {
            this.lexer.nextToken();
            storage.setOn(this.expr());
         } else if (this.lexer.identifierEquals("FILLFACTOR")) {
            this.lexer.nextToken();
            storage.setFillfactor(this.expr());
         } else if (this.lexer.identifierEquals("BRANCH")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.LPAREN) {
               this.accept(Token.LPAREN);
               storage.setBranchcount(this.expr());
               this.accept(Token.COMMA);
               storage.setNobranchcount(this.expr());
               this.accept(Token.RPAREN);
            } else {
               storage.setBranchcount(this.expr());
            }
         } else if (this.lexer.identifierEquals("NOBRANCH")) {
            this.lexer.nextToken();
            storage.setNobranch(true);
         } else if (this.lexer.identifierEquals("CLUSTERBTR")) {
            this.lexer.nextToken();
            storage.setClusterbtr(true);
         } else if (this.lexer.token() == Token.WITH) {
            this.accept(Token.WITH);
            this.acceptIdentifier("COUNTER");
            storage.setWithCounter(true);
         } else if (this.lexer.identifierEquals("WITHOUT")) {
            this.acceptIdentifier("WITHOUT");
            this.acceptIdentifier("COUNTER");
            storage.setWithoutCounter(true);
         } else if (this.lexer.token() == Token.USING) {
            this.accept(Token.USING);
            this.acceptIdentifier("LONG");
            this.accept(Token.ROW);
            storage.setWithoutCounter(true);
         } else if (this.lexer.identifierEquals("STAT")) {
            this.acceptIdentifier("STAT");
            this.accept(Token.NONE);
            storage.setStatNone(true);
         } else {
            if (!this.lexer.identifierEquals("SECTION")) {
               this.accept(Token.RPAREN);
               return storage;
            }

            this.acceptIdentifier("SECTION");
            this.accept(Token.LPAREN);
            storage.setSectioncount(this.expr());
            this.accept(Token.RPAREN);
         }
      }
   }

   public SQLColumnDefinition parseColumnRest(SQLColumnDefinition column) {
      if (this.lexer.identifierEquals(Token.IDENTITY.name)) {
         this.lexer.nextToken();
         SQLColumnDefinition.Identity identity = new SQLColumnDefinition.Identity();
         if (this.lexer.token() == Token.LPAREN) {
            this.lexer.nextToken();
            SQLIntegerExpr seed = (SQLIntegerExpr)this.primary();
            this.accept(Token.COMMA);
            SQLIntegerExpr increment = (SQLIntegerExpr)this.primary();
            this.accept(Token.RPAREN);
            identity.setSeed((Integer)seed.getNumber());
            identity.setIncrement((Integer)increment.getNumber());
         }

         column.setIdentity(identity);
         return this.parseColumnRest(column);
      } else {
         if (this.lexer.identifierEquals("CLUSTER")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.PRIMARY) {
               this.lexer.nextToken();
               this.accept(Token.KEY);
               DmSQLColumnPrimaryKey dmSQLColumnPrimaryKey = new DmSQLColumnPrimaryKey();
               dmSQLColumnPrimaryKey.setCluster(true);
               dmSQLColumnPrimaryKey.setPrimary(true);
               column.addConstraint(dmSQLColumnPrimaryKey);
               return this.parseColumnRest(column);
            }

            if (this.lexer.token() == Token.UNIQUE) {
               this.lexer.nextToken();
               this.accept(Token.KEY);
               DmSQLColumnPrimaryKey dmSQLColumnPrimaryKey = new DmSQLColumnPrimaryKey();
               dmSQLColumnPrimaryKey.setCluster(true);
               dmSQLColumnPrimaryKey.setUnique(true);
               column.addConstraint(dmSQLColumnPrimaryKey);
               return this.parseColumnRest(column);
            }

            if (this.lexer.token() == Token.KEY) {
               this.accept(Token.KEY);
               DmSQLColumnPrimaryKey dmSQLColumnPrimaryKey = new DmSQLColumnPrimaryKey();
               dmSQLColumnPrimaryKey.setCluster(true);
               dmSQLColumnPrimaryKey.setKey(true);
               column.addConstraint(dmSQLColumnPrimaryKey);
               return this.parseColumnRest(column);
            }
         }

         if (this.lexer.token() == Token.NOT) {
            this.lexer.nextToken();
            if (this.lexer.identifierEquals("CLUSTER")) {
               this.lexer.nextToken();
               if (this.lexer.token() == Token.PRIMARY) {
                  this.lexer.nextToken();
                  this.accept(Token.KEY);
                  DmSQLColumnPrimaryKey dmSQLColumnPrimaryKey = new DmSQLColumnPrimaryKey();
                  dmSQLColumnPrimaryKey.setCluster(true);
                  dmSQLColumnPrimaryKey.setNot(true);
                  column.addConstraint(dmSQLColumnPrimaryKey);
                  return this.parseColumnRest(column);
               }
            }
         }

         if (this.lexer.identifierEquals(FnvHash.Constants.GENERATED)) {
            this.lexer.nextToken();
            this.acceptIdentifier("ALWAYS");
            this.accept(Token.AS);
            this.accept(Token.LPAREN);
            SQLExpr expr = this.expr();
            this.accept(Token.RPAREN);
            column.setGeneratedAlawsAs(expr);
         }

         if (this.lexer.token() == Token.AS) {
            this.accept(Token.AS);
            this.accept(Token.LPAREN);
            SQLExpr expr = this.expr();
            this.accept(Token.RPAREN);
            column.setGeneratedAlawsAs(expr);
         }

         if (this.lexer.identifierEquals(FnvHash.Constants.VIRTUAL)) {
            this.lexer.nextToken();
            column.setVirtual(true);
         }

         if (this.lexer.identifierEquals(FnvHash.Constants.VISIBLE)) {
            this.lexer.nextToken();
            column.setVisible(true);
         }

         if (this.lexer.token() == Token.STORAGE) {
            DmStorageClause storage = this.parseStorage();
            column.setStorage(storage);
         }

         return this.superParseColumnRest(column);
      }
   }

   public SQLColumnDefinition superParseColumnRest(SQLColumnDefinition column) {
      switch (this.lexer.token()) {
         case IDENTIFIER:
            long hash = this.lexer.hash_lower();
            if (hash == FnvHash.Constants.AUTO_INCREMENT) {
               this.lexer.nextToken();
               column.setAutoIncrement(true);
               if (this.lexer.token() == Token.BY) {
                  this.lexer.nextToken();
                  if (this.lexer.hash_lower() == FnvHash.Constants.GROUP) {
                     this.lexer.nextToken();
                     column.setSequenceType(AutoIncrementType.GROUP);
                     if (!this.lexer.identifierEquals(FnvHash.Constants.UNIT)) {
                        return this.parseColumnRest(column);
                     }

                     this.lexer.nextToken();
                     if (this.lexer.identifierEquals(FnvHash.Constants.COUNT)) {
                        this.lexer.nextToken();
                        SQLExpr unitCount = this.primary();
                        column.setUnitCount(unitCount);
                     }

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

                     if (this.lexer.hash_lower() == FnvHash.Constants.STEP) {
                        this.lexer.nextToken();
                        SQLExpr step = this.primary();
                        column.setStep(step);
                     }
                  } else {
                     if (this.lexer.hash_lower() == FnvHash.Constants.TIME) {
                        this.lexer.nextToken();
                        column.setSequenceType(AutoIncrementType.TIME);
                        return this.parseColumnRest(column);
                     }

                     if (this.lexer.hash_lower() == FnvHash.Constants.SIMPLE) {
                        this.lexer.nextToken();
                        if (this.lexer.hash_lower() == FnvHash.Constants.WITH) {
                           this.lexer.nextToken();
                           if (this.lexer.hash_lower() == FnvHash.Constants.CACHE) {
                              column.setSequenceType(AutoIncrementType.SIMPLE_CACHE);
                              this.lexer.nextToken();
                              return this.parseColumnRest(column);
                           }

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

                        column.setSequenceType(AutoIncrementType.SIMPLE);
                        return this.parseColumnRest(column);
                     }
                  }

                  return this.parseColumnRest(column);
               } else {
                  if (this.lexer.identifierEquals(FnvHash.Constants.UNIT)) {
                     this.lexer.nextToken();
                     if (this.lexer.identifierEquals(FnvHash.Constants.COUNT)) {
                        this.lexer.nextToken();
                        SQLExpr unitCount = this.primary();
                        column.setUnitCount(unitCount);
                     }

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

                     if (this.lexer.hash_lower() == FnvHash.Constants.STEP) {
                        this.lexer.nextToken();
                        SQLExpr unitIndex = this.primary();
                        column.setStep(unitIndex);
                     }
                  } else if (this.lexer.token() == Token.LPAREN) {
                     this.lexer.nextToken();
                     SQLColumnDefinition.Identity ident = new SQLColumnDefinition.Identity();
                     if (this.lexer.token() != Token.LITERAL_INT) {
                        throw new ParserException("TODO : " + this.lexer.info());
                     }

                     ident.setSeed(this.lexer.integerValue().intValue());
                     this.lexer.nextToken();
                     if (this.lexer.token() == Token.COMMA) {
                        this.lexer.nextToken();
                        if (this.lexer.token() != Token.LITERAL_INT) {
                           throw new ParserException("TODO : " + this.lexer.info());
                        }

                        ident.setIncrement(this.lexer.integerValue().intValue());
                        this.lexer.nextToken();
                     }

                     column.setIdentity(ident);
                     this.accept(Token.RPAREN);
                  }

                  return this.parseColumnRest(column);
               }
            }
         case VARIANT:
         case QUES:
         case LITERAL_ALIAS:
         case SYSDATE:
         case PRIOR:
         case COLON:
         case TABLE:
         case PLUS:
         case SUB:
         case CURSOR:
         case PCTFREE:
         case INITRANS:
         case MAXTRANS:
         case SEGMENT:
         case CREATION:
         case IMMEDIATE:
         case DEFERRED:
         case STORAGE:
         case NEXT:
         case MINEXTENTS:
         case MAXEXTENTS:
         case MAXSIZE:
         case PCTINCREASE:
         case FLASH_CACHE:
         case CELL_FLASH_CACHE:
         case NONE:
         case LOB:
         case STORE:
         case ROW:
         case CHUNK:
         case CACHE:
         case NOCACHE:
         case LOGGING:
         case NOCOMPRESS:
         case KEEP_DUPLICATES:
         case EXCEPTIONS:
         case PURGE:
         case OUTER:
         case DOCUMENT:
         case CONTENT:
         default:
            return column;
         case DEFAULT:
            this.lexer.nextToken();
            SQLExpr defaultExpr = null;
            if (this.lexer.token() == Token.LITERAL_CHARS && this.dbType == DbType.mysql) {
               defaultExpr = new SQLCharExpr(this.lexer.stringVal());
               this.lexer.nextToken();
            } else {
               defaultExpr = this.bitOr();
            }

            column.setDefaultExpr(defaultExpr);
            return this.parseColumnRest(column);
         case NOT:
            this.lexer.nextToken();
            if (this.lexer.token() == Token.NULL) {
               this.accept(Token.NULL);
               SQLNotNullConstraint notNull = new SQLNotNullConstraint();
               if (this.lexer.token() == Token.HINT) {
                  List<SQLCommentHint> hints = this.parseHints();
                  notNull.setHints(hints);
               }

               column.addConstraint(notNull);
            } else if (this.lexer.identifierEquals("VISIBLE")) {
               this.lexer.nextToken();
               SQLNotVisibleConstraint notVisibleConstraint = new SQLNotVisibleConstraint();
               this.parseEnable(notVisibleConstraint);
               column.addConstraint(notVisibleConstraint);
            }

            return this.parseColumnRest(column);
         case NULL:
            this.lexer.nextToken();
            column.getConstraints().add(new SQLNullConstraint());
            return this.parseColumnRest(column);
         case PRIMARY:
            this.lexer.nextToken();
            this.accept(Token.KEY);
            column.addConstraint(new SQLColumnPrimaryKey());
            return this.parseColumnRest(column);
         case UNIQUE:
            this.lexer.nextToken();
            if (this.lexer.token() == Token.KEY) {
               this.lexer.nextToken();
            }

            column.addConstraint(new SQLColumnUniqueKey());
            return this.parseColumnRest(column);
         case USING:
            DMIndexTablespacleConstraint tablespacleConstraint = new DMIndexTablespacleConstraint();
            this.parseIndexTablespaceConstraint(column, tablespacleConstraint);
            return this.parseColumnRest(column);
         case KEY:
            this.lexer.nextToken();
            column.addConstraint(new SQLColumnPrimaryKey());
            return this.parseColumnRest(column);
         case FOREIGN:
            this.accept(Token.FOREIGN);
            this.accept(Token.KEY);
         case REFERENCES:
            SQLColumnReference ref = this.parseReference();
            column.addConstraint(ref);
            return this.parseColumnRest(column);
         case CONSTRAINT:
            this.lexer.nextToken();
            SQLName name = this.name();
            if (this.lexer.token() == Token.USING) {
               DMIndexTablespacleConstraint tablespacle = new DMIndexTablespacleConstraint();
               tablespacle.setName(name);
               this.parseIndexTablespaceConstraint(column, tablespacle);
               return this.parseColumnRest(column);
            } else if (this.lexer.token() == Token.PRIMARY) {
               this.lexer.nextToken();
               this.accept(Token.KEY);
               SQLColumnPrimaryKey pk = new SQLColumnPrimaryKey();
               pk.setName(name);
               column.addConstraint(pk);
               return this.parseColumnRest(column);
            } else if (this.lexer.token() == Token.UNIQUE) {
               this.lexer.nextToken();
               SQLColumnUniqueKey uk = new SQLColumnUniqueKey();
               uk.setName(name);
               column.addConstraint(uk);
               return this.parseColumnRest(column);
            } else if (this.lexer.token() == Token.FOREIGN) {
               this.accept(Token.FOREIGN);
               this.accept(Token.KEY);
               SQLColumnReference ref2 = this.parseReference();
               ref2.setName(name);
               column.addConstraint(ref2);
               return this.parseColumnRest(column);
            } else if (this.lexer.token() == Token.REFERENCES) {
               SQLColumnReference ref3 = this.parseReference();
               ref3.setName(name);
               column.addConstraint(ref3);
               return this.parseColumnRest(column);
            } else if (this.lexer.token() == Token.NOT) {
               this.lexer.nextToken();
               if (this.lexer.token() == Token.NULL) {
                  this.accept(Token.NULL);
                  SQLNotNullConstraint notNull = new SQLNotNullConstraint();
                  notNull.setName(name);
                  column.addConstraint(notNull);
               } else if (this.lexer.identifierEquals("VISIBLE")) {
                  this.lexer.nextToken();
                  SQLNotVisibleConstraint notVisibleConstraint = new SQLNotVisibleConstraint();
                  this.parseEnable(notVisibleConstraint);
                  column.addConstraint(notVisibleConstraint);
               }

               return this.parseColumnRest(column);
            } else if (this.lexer.token() == Token.CHECK) {
               SQLColumnCheck check = this.parseColumnCheck();
               check.setName(name);
               check.setParent(column);
               column.addConstraint(check);
               return this.parseColumnRest(column);
            } else {
               if (this.lexer.token() == Token.DEFAULT) {
                  this.lexer.nextToken();
                  SQLExpr expr = this.expr();
                  column.setDefaultExpr(expr);
                  return this.parseColumnRest(column);
               }

               throw new ParserException("TODO : " + this.lexer.info());
            }
         case CHECK:
            SQLColumnCheck check = this.parseColumnCheck();
            column.addConstraint(check);
            return this.parseColumnRest(column);
         case COMMENT:
            this.lexer.nextToken();
            if (this.lexer.token() == Token.LITERAL_ALIAS) {
               String alias = this.lexer.stringVal();
               if (alias.length() > 2 && alias.charAt(0) == '"' && alias.charAt(alias.length() - 1) == '"') {
                  alias = alias.substring(1, alias.length() - 1);
               }

               column.setComment(alias);
               this.lexer.nextToken();
            } else if (this.lexer.token() == Token.LITERAL_CHARS) {
               String stringVal = this.lexer.stringVal();
               this.lexer.nextToken();
               if (this.dbType == DbType.odps) {
                  label182:
                  while(true) {
                     while(this.lexer.token() != Token.LITERAL_ALIAS) {
                        if (this.lexer.token() != Token.LITERAL_CHARS) {
                           break label182;
                        }

                        stringVal = stringVal + this.lexer.stringVal();
                        this.lexer.nextToken();
                     }

                     String tmp = this.lexer.stringVal();
                     if (tmp.length() > 2 && tmp.charAt(0) == '"' && tmp.charAt(tmp.length() - 1) == '"') {
                        tmp = tmp.substring(1, tmp.length() - 1);
                     }

                     stringVal = stringVal + tmp;
                     this.lexer.nextToken();
                  }
               }

               column.setComment(stringVal);
            } else {
               column.setComment(this.primary());
            }

            return this.parseColumnRest(column);
      }
   }

   public SQLColumnReference parseReference() {
      DmSQLColumnReference fk = new DmSQLColumnReference();
      this.lexer.nextToken();
      if (this.lexer.identifierEquals("PENDANT")) {
         fk.setPendant(true);
         this.lexer.nextToken();
      }

      fk.setTable(this.name());
      this.accept(Token.LPAREN);
      this.names(fk.getColumns(), fk);
      this.accept(Token.RPAREN);
      if (this.lexer.identifierEquals(FnvHash.Constants.MATCH)) {
         this.lexer.nextToken();
         if (!this.lexer.identifierEquals("FULL") && this.lexer.token() != Token.FULL) {
            if (this.lexer.identifierEquals(FnvHash.Constants.PARTIAL)) {
               fk.setReferenceMatch(SQLForeignKeyImpl.Match.PARTIAL);
               this.lexer.nextToken();
            } else {
               if (!this.lexer.identifierEquals(FnvHash.Constants.SIMPLE)) {
                  throw new ParserException("TODO : " + this.lexer.info());
               }

               fk.setReferenceMatch(SQLForeignKeyImpl.Match.SIMPLE);
               this.lexer.nextToken();
            }
         } else {
            fk.setReferenceMatch(SQLForeignKeyImpl.Match.FULL);
            this.lexer.nextToken();
         }
      }

      while(this.lexer.token() == Token.ON) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.DELETE) {
            this.lexer.nextToken();
            SQLForeignKeyImpl.Option option = this.parseReferenceOption();
            fk.setOnDelete(option);
         } else {
            if (this.lexer.token() != Token.UPDATE) {
               throw new ParserException("syntax error, expect DELETE or UPDATE, actual " + this.lexer.token() + " " + this.lexer.info());
            }

            this.lexer.nextToken();
            SQLForeignKeyImpl.Option option = this.parseReferenceOption();
            fk.setOnUpdate(option);
         }
      }

      if (this.lexer.token() == Token.WITH) {
         this.lexer.nextToken();
         this.accept(Token.INDEX);
         fk.setWithIndex(true);
      }

      return fk;
   }

   private void parseIndexTablespaceConstraint(SQLColumnDefinition column, DMIndexTablespacleConstraint tablespacleConstraint) {
      this.accept(Token.USING);
      this.accept(Token.INDEX);
      this.accept(Token.TABLESPACE);
      tablespacleConstraint.setTablespacename(this.expr());
      column.addConstraint(tablespacleConstraint);
      this.parseEnable(tablespacleConstraint);
   }

   private void parseEnable(SQLConstraintImpl sqlConstraint) {
      if (this.lexer.token() == Token.DISABLE) {
         this.lexer.nextToken();
         sqlConstraint.setEnable(false);
      } else if (this.lexer.token() == Token.ENABLE) {
         this.lexer.nextToken();
         sqlConstraint.setEnable(true);
      }

   }

   public SQLColumnDefinition parseColumn(SQLObject parent) {
      SQLColumnDefinition column = this.createColumnDefinition();
      column.setName(this.name());
      Token token = this.lexer.token();
      if (token != Token.SET && token != Token.DROP && token != Token.PRIMARY && token != Token.RPAREN && token != Token.COMMA && !this.lexer.identifierEquals(FnvHash.Constants.GENERATED) && !this.lexer.identifierEquals(FnvHash.Constants.RENAME) && this.lexer.token() != Token.AS) {
         column.setDataType(this.parseDataType());
      }

      return this.parseColumnRest(column);
   }

   public SQLConstraint parseConstaint() {
      SQLName name = null;
      if (this.lexer.token() == Token.CONSTRAINT) {
         this.lexer.nextToken();
         name = this.name();
         this.acceptIf(Token.TO);
      }

      SQLConstraint constraint;
      switch (this.lexer.token()) {
         case IDENTIFIER:
            if (this.lexer.identifierEquals("CLUSTER")) {
               this.acceptIdentifier("CLUSTER");
               if (this.lexer.token() == Token.PRIMARY) {
                  constraint = this.parsePrimaryKey();
                  ((DmSQLPrimaryKey)constraint).setClustered(true);
                  break;
               } else if (this.lexer.token() == Token.UNIQUE || this.lexer.token() == Token.KEY) {
                  constraint = this.parseUnique();
                  ((DmSQLUniqueKey)constraint).setClustered(true);
                  break;
               }
            }
         default:
            constraint = new DmConstraint();
            break;
         case DEFAULT:
            constraint = this.parseDefault();
            break;
         case NOT:
            this.accept(Token.NOT);
            this.acceptIdentifier("CLUSTER");
            constraint = this.parsePrimaryKey();
            ((DmSQLPrimaryKey)constraint).setNot(true);
            ((DmSQLPrimaryKey)constraint).setClustered(true);
            break;
         case PRIMARY:
            constraint = this.parsePrimaryKey();
            break;
         case UNIQUE:
            constraint = this.parseUnique();
            break;
         case KEY:
            constraint = this.parseUnique();
            break;
         case FOREIGN:
            constraint = this.parseForeignKey();
            break;
         case CHECK:
            constraint = this.parseCheck();
      }

      constraint.setName(name);
      if (constraint instanceof SQLConstraintImpl) {
         SQLConstraintImpl temp = (SQLConstraintImpl)constraint;
         if (this.lexer.token() == Token.DISABLE) {
            this.lexer.nextToken();
            temp.setEnable(false);
         }

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

      return constraint;
   }

   public SQLUnique parseUnique() {
      DmSQLUniqueKey unique = new DmSQLUniqueKey();
      if (this.lexer.token() == Token.UNIQUE) {
         this.accept(Token.UNIQUE);
         unique.setUnique(true);
      }

      if (this.lexer.token() == Token.KEY) {
         this.accept(Token.KEY);
         unique.setKey(true);
      }

      this.accept(Token.LPAREN);
      this.orderBy(unique.getColumns(), unique);
      this.accept(Token.RPAREN);
      if (this.lexer.token() == Token.USING) {
         this.accept(Token.USING);
         this.accept(Token.INDEX);
         this.accept(Token.TABLESPACE);
         unique.setTablespacename(this.expr());
      }

      if (this.lexer.token() == Token.DISABLE) {
         this.lexer.nextToken();
         unique.setEnable(false);
      } else if (this.lexer.token() == Token.ENABLE) {
         this.lexer.nextToken();
         unique.setEnable(true);
      }

      return unique;
   }

   public SQLPrimaryKey parsePrimaryKey() {
      this.accept(Token.PRIMARY);
      this.accept(Token.KEY);
      DmSQLPrimaryKey pk = new DmSQLPrimaryKey();
      this.accept(Token.LPAREN);
      this.orderBy(pk.getColumns(), pk);
      this.accept(Token.RPAREN);
      if (this.lexer.token() == Token.USING) {
         this.accept(Token.USING);
         this.accept(Token.INDEX);
         this.accept(Token.TABLESPACE);
         pk.setTablespacename(this.expr());
      } else if (this.lexer.token() == Token.NULL) {
         this.accept(Token.NULL);
         pk.setNull(true);
      }

      if (this.lexer.token() == Token.DISABLE) {
         this.lexer.nextToken();
         pk.setEnable(false);
      } else if (this.lexer.token() == Token.ENABLE) {
         this.lexer.nextToken();
         pk.setEnable(true);
      }

      return pk;
   }

   public SQLForeignKeyConstraint parseForeignKey() {
      this.accept(Token.FOREIGN);
      this.accept(Token.KEY);
      DmForeignKeyImpl fk = new DmForeignKeyImpl();
      this.accept(Token.LPAREN);
      this.names(fk.getReferencingColumns(), fk);
      this.accept(Token.RPAREN);
      if (this.lexer.token() == Token.FOREIGN) {
         this.accept(Token.FOREIGN);
         this.accept(Token.KEY);
      }

      this.accept(Token.REFERENCES);
      if (this.lexer.identifierEquals("PENDANT")) {
         fk.setPendant(true);
         this.lexer.nextToken();
      }

      fk.setReferencedTableName(this.name());
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         this.names(fk.getReferencedColumns(), fk);
         this.accept(Token.RPAREN);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.MATCH)) {
         this.lexer.nextToken();
         if (!this.lexer.identifierEquals("FULL") && this.lexer.token() != Token.FULL) {
            if (this.lexer.identifierEquals(FnvHash.Constants.PARTIAL)) {
               fk.setReferenceMatch(SQLForeignKeyImpl.Match.PARTIAL);
               this.lexer.nextToken();
            } else {
               if (!this.lexer.identifierEquals(FnvHash.Constants.SIMPLE)) {
                  throw new ParserException("TODO : " + this.lexer.info());
               }

               fk.setReferenceMatch(SQLForeignKeyImpl.Match.SIMPLE);
               this.lexer.nextToken();
            }
         } else {
            fk.setReferenceMatch(SQLForeignKeyImpl.Match.FULL);
            this.lexer.nextToken();
         }
      }

      while(this.lexer.token() == Token.ON) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.DELETE) {
            this.lexer.nextToken();
            SQLForeignKeyImpl.Option option = this.parseReferenceOption();
            fk.setOnDelete(option);
         } else {
            if (this.lexer.token() != Token.UPDATE) {
               throw new ParserException("syntax error, expect DELETE or UPDATE, actual " + this.lexer.token() + " " + this.lexer.info());
            }

            this.lexer.nextToken();
            SQLForeignKeyImpl.Option option = this.parseReferenceOption();
            fk.setOnUpdate(option);
         }
      }

      if (this.lexer.token() == Token.WITH) {
         this.lexer.nextToken();
         this.accept(Token.INDEX);
         fk.setWithIndex(true);
      }

      if (this.lexer.token() == Token.DISABLE) {
         Lexer.SavePoint mark = this.lexer.mark();
         this.lexer.nextToken();
         if (this.lexer.identifierEquals(FnvHash.Constants.NOVALIDATE)) {
            this.lexer.nextToken();
            fk.setDisableNovalidate(true);
         } else {
            this.lexer.reset(mark);
         }
      }

      return fk;
   }

   static {
      String[] strings = new String[]{"AVG", "CORR", "COVAR_POP", "COVAR_SAMP", "COUNT", "CUME_DIST", "DENSE_RANK", "FIRST", "FIRST_VALUE", "LAG", "LAST", "LAST_VALUE", "LISTAGG", "LEAD", "MAX", "MIN", "NTILE", "PERCENT_RANK", "PERCENTILE_CONT", "PERCENTILE_DISC", "RANK", "RATIO_TO_REPORT", "REGR_SLOPE", "REGR_INTERCEPT", "REGR_COUNT", "REGR_R2", "REGR_AVGX", "REGR_AVGY", "REGR_SXX", "REGR_SYY", "REGR_SXY", "ROW_NUMBER", "STDDEV", "STDDEV_POP", "STDDEV_SAMP", "SUM", "VAR_POP", "VAR_SAMP", "VARIANCE", "WM_CONCAT"};
      AGGREGATE_FUNCTIONS_CODES = FnvHash.fnv1a_64_lower(strings, true);
      AGGREGATE_FUNCTIONS = new String[AGGREGATE_FUNCTIONS_CODES.length];

      for(String str : strings) {
         long hash = FnvHash.fnv1a_64_lower(str);
         int index = Arrays.binarySearch(AGGREGATE_FUNCTIONS_CODES, hash);
         AGGREGATE_FUNCTIONS[index] = str;
      }

   }
}
