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

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLAnnIndex;
import com.alibaba.druid.sql.ast.SQLArrayDataType;
import com.alibaba.druid.sql.ast.SQLCurrentTimeExpr;
import com.alibaba.druid.sql.ast.SQLCurrentUserExpr;
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.SQLLimit;
import com.alibaba.druid.sql.ast.SQLName;
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.SQLPartitionValue;
import com.alibaba.druid.sql.ast.SQLSubPartition;
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.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLExtractExpr;
import com.alibaba.druid.sql.ast.expr.SQLHexExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntervalExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntervalUnit;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
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.SQLAssignItem;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLValuesQuery;
import com.alibaba.druid.sql.dialect.kingbase.ast.clause.KingbaseSQLOver;
import com.alibaba.druid.sql.dialect.kingbase.ast.expression.KingbaseCharExpr;
import com.alibaba.druid.sql.dialect.kingbase.ast.expression.KingbaseConstantExpr;
import com.alibaba.druid.sql.dialect.kingbase.ast.expression.KingbaseDateTimeExpr;
import com.alibaba.druid.sql.dialect.kingbase.ast.expression.KingbaseTypeCastExpr;
import com.alibaba.druid.sql.dialect.kingbase.parser.lexer.KingbaseLexer;
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.SQLSelectParser;
import com.alibaba.druid.sql.parser.Token;
import com.alibaba.druid.util.FnvHash;
import com.alibaba.druid.util.MySqlUtils;
import java.util.Arrays;
import java.util.List;

public class KingbaseExprParser extends SQLExprParser {
   public static final String[] AGGREGATE_FUNCTIONS;
   public static final long[] AGGREGATE_FUNCTIONS_CODES;

   public KingbaseExprParser(String sql, SQLParserFeature... features) {
      this((Lexer)(new KingbaseLexer(sql, false, features)), (DbType)DbType.kingbase);
   }

   public KingbaseExprParser(Lexer lexer, DbType dbType) {
      super(lexer, dbType);
      this.aggregateFunctions = AGGREGATE_FUNCTIONS;
      this.aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
   }

   public SQLExpr primary() {
      Token tok = this.lexer.token();
      switch (tok) {
         case IDENTIFIER:
            long hash_lower = this.lexer.hash_lower();
            String strVal = this.lexer.stringVal();
            boolean quoteStart = strVal.length() > 0 && (strVal.charAt(0) == '`' || strVal.charAt(0) == '"');
            if (!quoteStart) {
               this.setAllowIdentifierMethod(true);
            }

            SQLCurrentTimeExpr currentTimeExpr = null;
            if (hash_lower == FnvHash.Constants.CURRENT_TIME && !quoteStart) {
               currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURRENT_TIME);
            } else if (hash_lower == FnvHash.Constants.CURRENT_TIMESTAMP && !quoteStart) {
               currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURRENT_TIMESTAMP);
            } else if (hash_lower == FnvHash.Constants.CURRENT_DATE && !quoteStart) {
               currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURRENT_DATE);
            } else if (hash_lower == FnvHash.Constants.CURDATE && !quoteStart) {
               currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.CURDATE);
            } else if (hash_lower == FnvHash.Constants.LOCALTIME && !quoteStart) {
               currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.LOCALTIME);
            } else if (hash_lower == FnvHash.Constants.LOCALTIMESTAMP && !quoteStart) {
               currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.LOCALTIMESTAMP);
            } else {
               if (hash_lower == FnvHash.Constants._LATIN1 && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     String collate = null;
                     if (this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
                        this.lexer.nextToken();
                        collate = this.lexer.stringVal();
                        if (this.lexer.token() == Token.LITERAL_CHARS) {
                           this.lexer.nextToken();
                        } else {
                           this.accept(Token.IDENTIFIER);
                        }
                     }

                     charExpr = new KingbaseCharExpr(str, "_latin1", collate);
                  } else {
                     charExpr = new KingbaseCharExpr(hexString, "_latin1");
                  }

                  return this.primaryRest(charExpr);
               }

               if ((hash_lower == FnvHash.Constants._UTF8 || hash_lower == FnvHash.Constants._UTF8MB4) && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     String collate = null;
                     if (this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
                        this.lexer.nextToken();
                        collate = this.lexer.stringVal();
                        if (this.lexer.token() == Token.LITERAL_CHARS) {
                           this.lexer.nextToken();
                        } else {
                           this.accept(Token.IDENTIFIER);
                        }
                     }

                     charExpr = new KingbaseCharExpr(str, "_utf8", collate);
                  } else {
                     String str = MySqlUtils.utf8(hexString);
                     charExpr = new SQLCharExpr(str);
                  }

                  return this.primaryRest(charExpr);
               }

               if ((hash_lower == FnvHash.Constants._UTF16 || hash_lower == FnvHash.Constants._UCS2) && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLCharExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     charExpr = new KingbaseCharExpr(str, "_utf16");
                  } else {
                     charExpr = new SQLCharExpr(MySqlUtils.utf16(hexString));
                  }

                  return this.primaryRest(charExpr);
               }

               if (hash_lower == FnvHash.Constants._UTF16LE && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLCharExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     charExpr = new KingbaseCharExpr(str, "_utf16le");
                  } else {
                     charExpr = new KingbaseCharExpr(hexString, "_utf16le");
                  }

                  return this.primaryRest(charExpr);
               }

               if (hash_lower == FnvHash.Constants._UTF32 && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLCharExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     charExpr = new KingbaseCharExpr(str, "_utf32");
                  } else {
                     charExpr = new SQLCharExpr(MySqlUtils.utf32(hexString));
                  }

                  return this.primaryRest(charExpr);
               }

               if (hash_lower == FnvHash.Constants._GBK && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLCharExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     charExpr = new KingbaseCharExpr(str, "_gbk");
                  } else {
                     charExpr = new SQLCharExpr(MySqlUtils.gbk(hexString));
                  }

                  return this.primaryRest(charExpr);
               }

               if (hash_lower == FnvHash.Constants._UJIS && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLCharExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     charExpr = new KingbaseCharExpr(str, "_ujis");
                  } else {
                     charExpr = new KingbaseCharExpr(hexString, "_ujis");
                  }

                  return this.primaryRest(charExpr);
               }

               if (hash_lower == FnvHash.Constants._BIG5 && !quoteStart) {
                  this.lexer.nextToken();
                  String hexString;
                  if (this.lexer.identifierEquals(FnvHash.Constants.X)) {
                     this.lexer.nextToken();
                     hexString = this.lexer.stringVal();
                     this.lexer.nextToken();
                  } else if (this.lexer.token() == Token.LITERAL_CHARS) {
                     hexString = null;
                  } else {
                     hexString = this.lexer.hexString();
                     this.lexer.nextToken();
                  }

                  SQLCharExpr charExpr;
                  if (hexString == null) {
                     String str = this.lexer.stringVal();
                     this.lexer.nextToken();
                     charExpr = new KingbaseCharExpr(str, "_big5");
                  } else {
                     charExpr = new SQLCharExpr(MySqlUtils.big5(hexString));
                  }

                  return this.primaryRest(charExpr);
               }

               if (hash_lower == FnvHash.Constants.CURRENT_USER && this.isEnabled(SQLParserFeature.EnableCurrentUserExpr)) {
                  this.lexer.nextToken();
                  return this.primaryRest(new SQLCurrentUserExpr());
               }

               if (hash_lower == -5808529385363204345L && this.lexer.charAt(this.lexer.pos()) == '\'') {
                  this.lexer.nextToken();
                  SQLHexExpr hex = new SQLHexExpr(this.lexer.stringVal());
                  this.lexer.nextToken();
                  return this.primaryRest(hex);
               }
            }

            if (currentTimeExpr != null) {
               String methodName = this.lexer.stringVal();
               this.lexer.nextToken();
               if (this.lexer.token() == Token.LPAREN) {
                  this.lexer.nextToken();
                  if (this.lexer.token() != Token.LPAREN) {
                     return this.primaryRest(this.methodRest(new SQLIdentifierExpr(methodName), false));
                  }

                  this.lexer.nextToken();
               }

               return this.primaryRest(currentTimeExpr);
            } else {
               return super.primary();
            }
         case VARIANT:
            SQLVariantRefExpr varRefExpr = new SQLVariantRefExpr(this.lexer.stringVal());
            this.lexer.nextToken();
            if (varRefExpr.getName().equalsIgnoreCase("@@global")) {
               this.accept(Token.DOT);
               varRefExpr = new SQLVariantRefExpr(this.lexer.stringVal(), true);
               this.lexer.nextToken();
            } else if (varRefExpr.getName().equals("@") && this.lexer.token() == Token.LITERAL_CHARS) {
               varRefExpr.setName("@'" + this.lexer.stringVal() + "'");
               this.lexer.nextToken();
            } else if (varRefExpr.getName().equals("@@") && this.lexer.token() == Token.LITERAL_CHARS) {
               varRefExpr.setName("@@'" + this.lexer.stringVal() + "'");
               this.lexer.nextToken();
            }

            return this.primaryRest(varRefExpr);
         case VALUES:
            this.lexer.nextToken();
            if (this.lexer.token() != Token.LPAREN) {
               SQLExpr expr = this.primary();
               SQLValuesQuery values = new SQLValuesQuery();
               values.addValue(new SQLListExpr(new SQLExpr[]{expr}));
               return new SQLQueryExpr(new SQLSelect(values));
            }

            return this.methodRest(new SQLIdentifierExpr("VALUES"), true);
         case BINARY:
            this.lexer.nextToken();
            if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.SEMI && this.lexer.token() != Token.EOF) {
               SQLUnaryExpr binaryExpr = new SQLUnaryExpr(SQLUnaryOperator.BINARY, this.primary());
               return this.primaryRest(binaryExpr);
            }

            return new SQLIdentifierExpr("BINARY");
         case TABLE:
            SQLQueryExpr queryExpr = new SQLQueryExpr(this.createSelectParser().select());
            return this.primaryRest(queryExpr);
         case TIMESTAMP:
         case TIMESTAMPTZ:
         case TIME:
         case DATE:
         case INTERVAL:
            return this.parseDateTimeExpr();
         case INFINITY:
         case NAN:
         case EPOCH:
         case TODAY:
         case TOMORROW:
         case YESTERDAY:
         case ALLBALLS:
         case ROWNUM:
            KingbaseConstantExpr expr = new KingbaseConstantExpr();
            expr.setValue(this.lexer.token().name);
            return expr;
         default:
            return super.primary();
      }
   }

   public SQLDataType parseDataType() {
      if (this.lexer.token() == Token.TYPE) {
         this.lexer.nextToken();
      }

      return super.parseDataType();
   }

   public final SQLExpr primaryRest(SQLExpr expr) {
      if (expr == null) {
         throw new IllegalArgumentException("expr");
      } else if (this.lexer.token() == Token.COLONCOLON) {
         this.lexer.nextToken();
         SQLDataType dataType = this.parseDataType();
         KingbaseTypeCastExpr castExpr = new KingbaseTypeCastExpr();
         castExpr.setExpr(expr);
         castExpr.setDataType(dataType);
         return this.primaryRest(castExpr);
      } else {
         if (this.lexer.token() == Token.LITERAL_CHARS) {
            if (expr instanceof SQLIdentifierExpr) {
               SQLIdentifierExpr identExpr = (SQLIdentifierExpr)expr;
               String ident = identExpr.getName();
               if (ident.equalsIgnoreCase("x")) {
                  char ch = this.lexer.charAt(this.lexer.pos());
                  if (ch == '\'') {
                     String charValue = this.lexer.stringVal();
                     this.lexer.nextToken();
                     expr = new SQLHexExpr(charValue);
                     return this.primaryRest(expr);
                  }
               } else if (ident.startsWith("_")) {
                  String charValue = this.lexer.stringVal();
                  this.lexer.nextToken();
                  KingbaseCharExpr mysqlCharExpr = new KingbaseCharExpr(charValue);
                  mysqlCharExpr.setCharset(identExpr.getName());
                  if (this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
                     this.lexer.nextToken();
                     String collate = this.lexer.stringVal();
                     mysqlCharExpr.setCollate(collate);
                     if (this.lexer.token() == Token.LITERAL_CHARS) {
                        this.lexer.nextToken();
                     } else {
                        this.accept(Token.IDENTIFIER);
                     }
                  }

                  return this.primaryRest(mysqlCharExpr);
               }
            } else if (expr instanceof SQLCharExpr) {
               String text2 = ((SQLCharExpr)expr).getText();

               do {
                  String chars = this.lexer.stringVal();
                  text2 = text2 + chars;
                  this.lexer.nextToken();
               } while(this.lexer.token() == Token.LITERAL_CHARS || this.lexer.token() == Token.LITERAL_ALIAS);

               expr = new SQLCharExpr(text2);
            } else if (expr instanceof SQLVariantRefExpr) {
               SQLMethodInvokeExpr concat = new SQLMethodInvokeExpr("CONCAT");
               concat.addArgument(expr);
               concat.addArgument(this.primary());
               return this.primaryRest(concat);
            }
         } else if (this.lexer.token() == Token.IDENTIFIER) {
            if (expr instanceof SQLHexExpr) {
               if ("USING".equalsIgnoreCase(this.lexer.stringVal())) {
                  this.lexer.nextToken();
                  if (this.lexer.token() != Token.IDENTIFIER) {
                     throw new ParserException("syntax error, illegal hex. " + this.lexer.info());
                  }

                  String charSet = this.lexer.stringVal();
                  this.lexer.nextToken();
                  expr.getAttributes().put("USING", charSet);
                  return this.primaryRest(expr);
               }
            } else {
               if (this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
                  this.lexer.nextToken();
                  if (this.lexer.token() == Token.EQ) {
                     this.lexer.nextToken();
                  }

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

                  String collate = this.lexer.stringVal();
                  this.lexer.nextToken();
                  SQLBinaryOpExpr binaryExpr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.COLLATE, new SQLIdentifierExpr(collate), DbType.mysql);
                  return this.primaryRest(binaryExpr);
               }

               if (expr instanceof SQLVariantRefExpr && this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
                  this.lexer.nextToken();
                  if (this.lexer.token() != Token.IDENTIFIER && this.lexer.token() != Token.LITERAL_CHARS) {
                     throw new ParserException("syntax error. " + this.lexer.info());
                  }

                  String collate = this.lexer.stringVal();
                  this.lexer.nextToken();
                  expr.putAttribute("COLLATE", collate);
                  return this.primaryRest(expr);
               }
            }
         } else {
            if (this.lexer.token() == Token.LBRACKET) {
               SQLArrayExpr array = new SQLArrayExpr();
               array.setExpr(expr);
               this.lexer.nextToken();
               this.exprList(array.getValues(), array);
               this.accept(Token.RBRACKET);
               return this.primaryRest(array);
            }

            if (this.lexer.token() == Token.DOT) {
               this.lexer.nextToken();
               if (expr instanceof SQLCharExpr) {
                  String text = ((SQLCharExpr)expr).getText();
                  expr = new SQLIdentifierExpr(text);
               }

               expr = this.dotRest(expr);
               return this.primaryRest(expr);
            }
         }

         if (this.lexer.token() == Token.ERROR) {
            throw new ParserException("syntax error. " + this.lexer.info());
         } else {
            return super.primaryRest(expr);
         }
      }
   }

   protected SQLExpr parsePosition() {
      SQLExpr expr = this.primary();
      expr = this.primaryRest(expr);
      expr = this.bitXorRest(expr);
      expr = this.additiveRest(expr);
      expr = this.shiftRest(expr);
      expr = this.bitAndRest(expr);
      expr = this.bitOrRest(expr);
      if (this.lexer.token() == Token.IN) {
         this.accept(Token.IN);
      } else {
         if (this.lexer.token() != Token.COMMA) {
            throw new ParserException("syntax error. " + this.lexer.info());
         }

         this.accept(Token.COMMA);
      }

      SQLExpr str = this.expr();
      this.accept(Token.RPAREN);
      SQLMethodInvokeExpr locate = new SQLMethodInvokeExpr("LOCATE");
      locate.addArgument(expr);
      locate.addArgument(str);
      return this.primaryRest(locate);
   }

   protected SQLExpr parseExtract() {
      if (this.lexer.token() != Token.IDENTIFIER) {
         throw new ParserException("syntax error. " + this.lexer.info());
      } else {
         String unitVal = this.lexer.stringVal();
         SQLIntervalUnit unit = SQLIntervalUnit.valueOf(unitVal.toUpperCase());
         this.lexer.nextToken();
         this.accept(Token.FROM);
         SQLExpr value = this.expr();
         SQLExtractExpr extract = new SQLExtractExpr();
         extract.setValue(value);
         extract.setUnit(unit);
         this.accept(Token.RPAREN);
         return this.primaryRest(extract);
      }
   }

   public SQLSelectParser createSelectParser() {
      return new KingbaseStatementSelectParser(this);
   }

   protected SQLExpr parseInterval() {
      this.accept(Token.INTERVAL);
      if (this.lexer.token() == Token.LPAREN) {
         this.lexer.nextToken();
         SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr("INTERVAL");
         if (this.lexer.token() != Token.RPAREN) {
            this.exprList(methodInvokeExpr.getArguments(), methodInvokeExpr);
         }

         this.accept(Token.RPAREN);
         if (methodInvokeExpr.getArguments().size() == 1 && this.lexer.token() == Token.IDENTIFIER) {
            SQLExpr value = (SQLExpr)methodInvokeExpr.getArguments().get(0);
            String unit = this.lexer.stringVal();
            this.lexer.nextToken();
            SQLIntervalExpr intervalExpr = new SQLIntervalExpr();
            intervalExpr.setValue(value);
            intervalExpr.setUnit(SQLIntervalUnit.valueOf(unit.toUpperCase()));
            return intervalExpr;
         } else {
            return this.primaryRest(methodInvokeExpr);
         }
      } else {
         SQLExpr value = this.expr();
         if (this.lexer.token() != Token.IDENTIFIER) {
            throw new ParserException("Syntax error. " + this.lexer.info());
         } else {
            SQLIntervalUnit intervalUnit = null;
            String unit = this.lexer.stringVal();
            long unitHash = this.lexer.hash_lower();
            this.lexer.nextToken();
            intervalUnit = SQLIntervalUnit.valueOf(unit.toUpperCase());
            if (this.lexer.token() == Token.TO) {
               this.lexer.nextToken();
               if (unitHash == FnvHash.Constants.YEAR) {
                  if (!this.lexer.identifierEquals(FnvHash.Constants.MONTH)) {
                     throw new ParserException("Syntax error. " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  intervalUnit = SQLIntervalUnit.YEAR_MONTH;
               } else if (unitHash == FnvHash.Constants.DAY) {
                  if (this.lexer.identifierEquals(FnvHash.Constants.HOUR)) {
                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.DAY_HOUR;
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.MINUTE)) {
                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.DAY_MINUTE;
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.SECOND)) {
                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.DAY_SECOND;
                  } else {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.MICROSECOND)) {
                        throw new ParserException("Syntax error. " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.DAY_MICROSECOND;
                  }
               } else if (unitHash == FnvHash.Constants.HOUR) {
                  if (this.lexer.identifierEquals(FnvHash.Constants.MINUTE)) {
                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.HOUR_MINUTE;
                  } else if (this.lexer.identifierEquals(FnvHash.Constants.SECOND)) {
                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.HOUR_SECOND;
                  } else {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.MICROSECOND)) {
                        throw new ParserException("Syntax error. " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.HOUR_MICROSECOND;
                  }
               } else if (unitHash == FnvHash.Constants.MINUTE) {
                  if (this.lexer.identifierEquals(FnvHash.Constants.SECOND)) {
                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.MINUTE_SECOND;
                  } else {
                     if (!this.lexer.identifierEquals(FnvHash.Constants.MICROSECOND)) {
                        throw new ParserException("Syntax error. " + this.lexer.info());
                     }

                     this.lexer.nextToken();
                     intervalUnit = SQLIntervalUnit.MINUTE_MICROSECOND;
                  }
               } else {
                  if (unitHash != FnvHash.Constants.SECOND) {
                     throw new ParserException("Syntax error. " + this.lexer.info());
                  }

                  if (!this.lexer.identifierEquals(FnvHash.Constants.MICROSECOND)) {
                     throw new ParserException("Syntax error. " + this.lexer.info());
                  }

                  this.lexer.nextToken();
                  intervalUnit = SQLIntervalUnit.SECOND_MICROSECOND;
               }
            }

            SQLIntervalExpr intervalExpr = new SQLIntervalExpr();
            intervalExpr.setValue(value);
            intervalExpr.setUnit(intervalUnit);
            return intervalExpr;
         }
      }
   }

   public SQLColumnDefinition parseColumn() {
      SQLColumnDefinition column = new SQLColumnDefinition();
      column.setDbType(this.dbType);
      SQLName name = this.name();
      column.setName(name);
      column.setDataType(this.parseDataType());
      if (column.getDataType() != null && column.getDataType().jdbcType() == 1 && this.lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
         this.lexer.nextToken();
         this.accept(Token.SET);
         if (this.lexer.token() != Token.IDENTIFIER && this.lexer.token() != Token.LITERAL_CHARS) {
            throw new ParserException(this.lexer.info());
         }

         column.setCharsetExpr(this.primary());
      }

      while(this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
         this.lexer.nextToken();
         SQLExpr collateExpr;
         if (this.lexer.token() == Token.IDENTIFIER) {
            collateExpr = new SQLIdentifierExpr(this.lexer.stringVal());
         } else {
            collateExpr = new SQLCharExpr(this.lexer.stringVal());
         }

         this.lexer.nextToken();
         column.setCollateExpr(collateExpr);
      }

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

      return this.parseColumnRest(column);
   }

   public SQLColumnDefinition parseColumnRest(SQLColumnDefinition column) {
      if (this.lexer.token() == Token.ON) {
         this.lexer.nextToken();
         this.accept(Token.UPDATE);
         SQLExpr expr = this.primary();
         column.setOnUpdate(expr);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.ENCODE)) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         column.setEncode(this.charExpr());
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.COMPRESSION)) {
         this.lexer.nextToken();
         this.accept(Token.EQ);
         column.setCompression(this.charExpr());
      }

      if (!this.lexer.identifierEquals(FnvHash.Constants.CHARACTER) && !this.lexer.identifierEquals(FnvHash.Constants.CHARSET)) {
         if (this.lexer.identifierEquals("disableindex")) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.TRUE) {
               this.lexer.nextToken();
               column.setDisableIndex(true);
            }

            return this.parseColumnRest(column);
         } else if (this.lexer.identifierEquals("jsonIndexAttrs")) {
            this.lexer.nextToken();
            column.setJsonIndexAttrsExpr(new SQLIdentifierExpr(this.lexer.stringVal()));
            this.lexer.nextToken();
            return this.parseColumnRest(column);
         } else if (this.lexer.identifierEquals("precision")) {
            this.lexer.nextToken();
            int precision = this.parseIntValue();
            this.acceptIdentifier("scale");
            int scale = this.parseIntValue();
            List<SQLExpr> arguments = column.getDataType().getArguments();
            arguments.add(new SQLIntegerExpr(precision));
            arguments.add(new SQLIntegerExpr(scale));
            return this.parseColumnRest(column);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
            this.lexer.nextToken();
            SQLExpr collateExpr;
            if (this.lexer.token() == Token.IDENTIFIER) {
               collateExpr = new SQLIdentifierExpr(this.lexer.stringVal());
            } else {
               collateExpr = new SQLCharExpr(this.lexer.stringVal());
            }

            this.lexer.nextToken();
            column.setCollateExpr(collateExpr);
            return this.parseColumnRest(column);
         } else {
            if (this.lexer.identifierEquals(FnvHash.Constants.PRECISION) && column.getDataType().nameHashCode64() == FnvHash.Constants.DOUBLE) {
               this.lexer.nextToken();
            }

            if (this.lexer.identifierEquals("COLUMN_FORMAT")) {
               this.lexer.nextToken();
               SQLExpr expr = this.expr();
               column.setFormat(expr);
            }

            if (this.lexer.identifierEquals(FnvHash.Constants.STORAGE)) {
               this.lexer.nextToken();
               SQLExpr expr = this.expr();
               column.setStorage(expr);
            }

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

            if (this.lexer.identifierEquals(FnvHash.Constants.STORED) || this.lexer.identifierEquals("PERSISTENT")) {
               this.lexer.nextToken();
               column.setStored(true);
            }

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

            if (this.lexer.identifierEquals(FnvHash.Constants.DELIMITER)) {
               this.lexer.nextToken();
               SQLExpr expr = this.expr();
               column.setDelimiter(expr);
               return this.parseColumnRest(column);
            } else if (this.lexer.identifierEquals("delimiter_tokenizer")) {
               this.lexer.nextToken();
               SQLExpr expr = this.expr();
               column.setDelimiterTokenizer(expr);
               return this.parseColumnRest(column);
            } else {
               if (this.lexer.identifierEquals("nlp_tokenizer")) {
                  this.lexer.nextToken();
                  SQLExpr expr = this.expr();
                  column.setNlpTokenizer(expr);
               }

               if (this.lexer.identifierEquals("value_type")) {
                  this.lexer.nextToken();
                  SQLExpr expr = this.expr();
                  column.setValueType(expr);
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.COLPROPERTIES)) {
                  this.lexer.nextToken();
                  this.parseAssignItem(column.getColProperties(), column);
               }

               if (this.lexer.identifierEquals(FnvHash.Constants.ANNINDEX)) {
                  this.lexer.nextToken();
                  this.accept(Token.LPAREN);
                  SQLAnnIndex annIndex = new SQLAnnIndex();

                  while(true) {
                     if (this.lexer.identifierEquals(FnvHash.Constants.TYPE)) {
                        this.lexer.nextToken();
                        this.accept(Token.EQ);
                        String type = this.lexer.stringVal();
                        annIndex.setIndexType(type);
                        this.accept(Token.LITERAL_CHARS);
                     } else if (this.lexer.identifierEquals(FnvHash.Constants.RTTYPE)) {
                        this.lexer.nextToken();
                        this.accept(Token.EQ);
                        String type = this.lexer.stringVal();
                        annIndex.setRtIndexType(type);
                        this.accept(Token.LITERAL_CHARS);
                     } else if (this.lexer.identifierEquals(FnvHash.Constants.DISTANCE)) {
                        this.lexer.nextToken();
                        this.accept(Token.EQ);
                        String type = this.lexer.stringVal();
                        annIndex.setDistance(type);
                        this.accept(Token.LITERAL_CHARS);
                     }

                     if (this.lexer.token() != Token.COMMA) {
                        this.accept(Token.RPAREN);
                        column.setAnnIndex(annIndex);
                        return this.parseColumnRest(column);
                     }

                     this.lexer.nextToken();
                  }
               } else {
                  super.parseColumnRest(column);
                  return column;
               }
            }
         }
      } else {
         if (this.lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
            this.lexer.nextToken();
            this.accept(Token.SET);
         } else {
            this.lexer.nextToken();
         }

         SQLExpr charSet;
         if (this.lexer.token() == Token.IDENTIFIER) {
            charSet = new SQLIdentifierExpr(this.lexer.stringVal());
         } else {
            charSet = new SQLCharExpr(this.lexer.stringVal());
         }

         this.lexer.nextToken();
         column.setCharsetExpr(charSet);
         return this.parseColumnRest(column);
      }
   }

   protected SQLDataType parseDataTypeRest(SQLDataType dataType) {
      super.parseDataTypeRest(dataType);

      while(true) {
         while(!this.lexer.identifierEquals(FnvHash.Constants.UNSIGNED)) {
            if (!this.lexer.identifierEquals(FnvHash.Constants.ZEROFILL)) {
               if (this.lexer.identifierEquals(FnvHash.Constants.ARRAY)) {
                  this.lexer.nextToken();
                  dataType = new SQLArrayDataType(dataType);
               }

               return dataType;
            }

            this.lexer.nextToken();
            ((SQLDataTypeImpl)dataType).setZerofill(true);
         }

         this.lexer.nextToken();
         ((SQLDataTypeImpl)dataType).setUnsigned(true);
      }
   }

   public SQLAssignItem parseAssignItem(boolean variant) {
      SQLAssignItem item = new SQLAssignItem();
      SQLExpr var = this.primary();
      String ident = null;
      long identHash = 0L;
      if (variant && var instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr identExpr = (SQLIdentifierExpr)var;
         ident = identExpr.getName();
         identHash = identExpr.hashCode64();
         if (identHash == FnvHash.Constants.GLOBAL) {
            ident = this.lexer.stringVal();
            this.lexer.nextToken();
            var = new SQLVariantRefExpr(ident, true);
         } else if (identHash == FnvHash.Constants.SESSION) {
            ident = this.lexer.stringVal();
            this.lexer.nextToken();
            var = new SQLVariantRefExpr(ident, false, true);
         } else {
            var = new SQLVariantRefExpr(ident);
         }
      }

      SQLAssignItem result;
      if (identHash == FnvHash.Constants.NAMES) {
         String charset = this.lexer.stringVal();
         SQLExpr varExpr = null;
         boolean chars = false;
         Token token = this.lexer.token();
         if (token == Token.IDENTIFIER) {
            this.lexer.nextToken();
         } else if (token == Token.DEFAULT) {
            charset = "DEFAULT";
            this.lexer.nextToken();
         } else if (token == Token.QUES) {
            varExpr = new SQLVariantRefExpr("?");
            this.lexer.nextToken();
         } else {
            chars = true;
            this.accept(Token.LITERAL_CHARS);
         }

         if (this.lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
            KingbaseCharExpr charsetExpr = new KingbaseCharExpr(charset);
            this.lexer.nextToken();
            String collate = this.lexer.stringVal();
            this.lexer.nextToken();
            charsetExpr.setCollate(collate);
            item.setValue(charsetExpr);
         } else if (varExpr != null) {
            item.setValue(varExpr);
         } else {
            item.setValue((SQLExpr)(chars ? new SQLCharExpr(charset) : new SQLIdentifierExpr(charset)));
         }

         item.setTarget(var);
         result = item;
      } else {
         if (identHash == FnvHash.Constants.CHARACTER) {
            var = new SQLVariantRefExpr("CHARACTER SET");
            this.accept(Token.SET);
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }
         } else if (identHash == FnvHash.Constants.CHARSET) {
            var = new SQLVariantRefExpr("CHARACTER SET");
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }
         } else if (identHash == FnvHash.Constants.TRANSACTION) {
            var = new SQLVariantRefExpr("TRANSACTION");
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }
         } else if (this.lexer.token() == Token.COLONEQ) {
            this.lexer.nextToken();
         } else {
            this.accept(Token.EQ);
         }

         if (this.lexer.token() == Token.ON) {
            this.lexer.nextToken();
            item.setValue(new SQLIdentifierExpr("ON"));
         } else {
            item.setValue(this.expr());
         }

         item.setTarget(var);
         result = item;
      }

      return result;
   }

   protected SQLAggregateExpr parseAggregateExprRest(SQLAggregateExpr aggregateExpr) {
      if (this.lexer.token() == Token.ORDER) {
         SQLOrderBy orderBy = this.parseOrderBy();
         aggregateExpr.setOrderBy(orderBy);
         aggregateExpr.putAttribute("ORDER BY", orderBy);
      }

      if (this.lexer.identifierEquals(FnvHash.Constants.SEPARATOR)) {
         this.lexer.nextToken();
         SQLExpr seperator = this.primary();
         seperator.setParent(aggregateExpr);
         aggregateExpr.putAttribute("SEPARATOR", seperator);
      }

      return aggregateExpr;
   }

   public SQLSubPartition parseSubPartition() {
      SQLSubPartition subPartition = new SQLSubPartition();
      subPartition.setName(this.name());

      while(true) {
         boolean storage = false;
         if (this.lexer.identifierEquals(FnvHash.Constants.DATA)) {
            this.lexer.nextToken();
            this.acceptIdentifier("DIRECTORY");
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            subPartition.setDataDirectory(this.expr());
         } else if (this.lexer.token() == Token.TABLESPACE) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            SQLName tableSpace = this.name();
            subPartition.setTablespace(tableSpace);
         } else if (this.lexer.token() == Token.INDEX) {
            this.lexer.nextToken();
            this.acceptIdentifier("DIRECTORY");
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            subPartition.setIndexDirectory(this.expr());
         } else if (this.lexer.identifierEquals(FnvHash.Constants.MAX_ROWS)) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            SQLExpr maxRows = this.primary();
            subPartition.setMaxRows(maxRows);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.MIN_ROWS)) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            SQLExpr minRows = this.primary();
            subPartition.setMinRows(minRows);
         } else if (!this.lexer.identifierEquals(FnvHash.Constants.ENGINE) && !(storage = this.lexer.token() == Token.STORAGE || this.lexer.identifierEquals(FnvHash.Constants.STORAGE))) {
            if (this.lexer.token() != Token.COMMENT) {
               return subPartition;
            }

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

            SQLExpr comment = this.primary();
            subPartition.setComment(comment);
         } else {
            if (storage) {
               this.lexer.nextToken();
            }

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

            SQLName engine = this.name();
            subPartition.setEngine(engine);
         }
      }
   }

   public SQLPartition parsePartition() {
      if (!this.lexer.identifierEquals(FnvHash.Constants.DBPARTITION) && !this.lexer.identifierEquals(FnvHash.Constants.TBPARTITION) && !this.lexer.identifierEquals(FnvHash.Constants.SUBPARTITION)) {
         this.accept(Token.PARTITION);
      } else {
         this.lexer.nextToken();
      }

      SQLPartition partitionDef = new SQLPartition();
      partitionDef.setName(this.name());
      SQLPartitionValue values = this.parsePartitionValues();
      if (values != null) {
         partitionDef.setValues(values);
      }

      while(true) {
         boolean storage = false;
         if (this.lexer.identifierEquals(FnvHash.Constants.DATA)) {
            this.lexer.nextToken();
            this.acceptIdentifier("DIRECTORY");
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            partitionDef.setDataDirectory(this.expr());
         } else if (this.lexer.token() == Token.TABLESPACE) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            SQLName tableSpace = this.name();
            partitionDef.setTablespace(tableSpace);
         } else if (this.lexer.token() == Token.INDEX) {
            this.lexer.nextToken();
            this.acceptIdentifier("DIRECTORY");
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            partitionDef.setIndexDirectory(this.expr());
         } else if (this.lexer.identifierEquals(FnvHash.Constants.MAX_ROWS)) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            SQLExpr maxRows = this.primary();
            partitionDef.setMaxRows(maxRows);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.MIN_ROWS)) {
            this.lexer.nextToken();
            if (this.lexer.token() == Token.EQ) {
               this.lexer.nextToken();
            }

            SQLExpr minRows = this.primary();
            partitionDef.setMaxRows(minRows);
         } else if (!this.lexer.identifierEquals(FnvHash.Constants.ENGINE) && !(storage = this.lexer.token() == Token.STORAGE || this.lexer.identifierEquals(FnvHash.Constants.STORAGE))) {
            if (this.lexer.token() != Token.COMMENT) {
               if (this.lexer.token() == Token.LPAREN) {
                  this.lexer.nextToken();

                  while(true) {
                     this.acceptIdentifier("SUBPARTITION");
                     SQLSubPartition subPartition = this.parseSubPartition();
                     partitionDef.addSubPartition(subPartition);
                     if (this.lexer.token() != Token.COMMA) {
                        this.accept(Token.RPAREN);
                        break;
                     }

                     this.lexer.nextToken();
                  }
               }

               return partitionDef;
            }

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

            SQLExpr comment = this.primary();
            partitionDef.setComment(comment);
         } else {
            if (storage) {
               this.lexer.nextToken();
            }

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

            SQLName engine = this.name();
            partitionDef.setEngine(engine);
         }
      }
   }

   protected SQLExpr parseAliasExpr(String alias) {
      if (this.isEnabled(SQLParserFeature.KeepNameQuotes)) {
         return new SQLIdentifierExpr(alias);
      } else {
         Lexer newLexer = new Lexer(alias);
         newLexer.nextTokenValue();
         return new SQLCharExpr(newLexer.stringVal());
      }
   }

   public SQLExpr exprRest(SQLExpr expr) {
      if (((KingbaseLexer)this.lexer).tablesource && this.lexer.token() == Token.STAR) {
         expr.putAttribute("inherit", true);
         return expr;
      } else if (this.lexer.token() != Token.INTERVAL && this.lexer.token() != Token.DATE && this.lexer.token() != Token.TIMESTAMP && this.lexer.token() != Token.TIMESTAMPTZ && this.lexer.token() != Token.TIME) {
         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());
         }

         if (this.lexer.token() == Token.BANG) {
            this.lexer.nextToken();
            SQLUnaryExpr uexpr = new SQLUnaryExpr();
            if (this.lexer.token() != Token.BANG) {
               uexpr.setOperator(SQLUnaryOperator.FactorialSuffix);
            } else {
               this.lexer.nextToken();
               uexpr.setOperator(SQLUnaryOperator.FactorialPrefix);
            }

            uexpr.setExpr(expr);
            return uexpr;
         } else if (this.lexer.token() == Token.BANGBANG) {
            this.lexer.nextToken();
            SQLUnaryExpr uexpr = new SQLUnaryExpr();
            uexpr.setOperator(SQLUnaryOperator.FactorialPrefix);
            uexpr.setExpr(expr);
            return uexpr;
         } else if (this.lexer.token() == Token.TILDE) {
            this.lexer.nextToken();
            if (expr != null) {
               SQLBinaryOpExpr bexpr = new SQLBinaryOpExpr();
               bexpr.setLeft(expr);
               bexpr.setOperator(SQLBinaryOperator.POSIX_Regular_Match);
               expr = this.expr();
               bexpr.setRight(expr);
               return bexpr;
            } else {
               SQLUnaryExpr uexpr = new SQLUnaryExpr();
               uexpr.setOperator(SQLUnaryOperator.Compl);
               uexpr.setExpr(expr);
               return uexpr;
            }
         } else if (this.lexer.token() == Token.MONKEYS_AT) {
            if (expr != null) {
               return expr;
            } else {
               this.lexer.nextToken();
               SQLUnaryExpr uexpr = new SQLUnaryExpr();
               uexpr.setOperator(SQLUnaryOperator.Abs);
               uexpr.setExpr(expr);
               return uexpr;
            }
         } else if (this.lexer.token() == Token.CARET && expr != null) {
            this.lexer.nextToken();
            SQLBinaryOpExpr bexpr = new SQLBinaryOpExpr();
            bexpr.setOperator(SQLBinaryOperator.BitwiseXor);
            bexpr.setLeft(expr);
            expr = this.expr();
            bexpr.setRight(expr);
            return bexpr;
         } else {
            switch (this.lexer.token()) {
               case INFINITY:
               case NAN:
               case EPOCH:
               case TODAY:
               case TOMORROW:
               case YESTERDAY:
               case ALLBALLS:
               case ROWNUM:
                  KingbaseConstantExpr e = new KingbaseConstantExpr();
                  e.setValue(this.lexer.token().name);
                  return e;
               default:
                  return expr;
            }
         }
      } else {
         return this.parseDateTimeExpr();
      }
   }

   public void over(SQLOver over) {
      this.lexer.nextToken();
      KingbaseSQLOver kbOver = (KingbaseSQLOver)over;
      if (this.lexer.token() == Token.IDENTIFIER && !this.lexer.identifierEquals(FnvHash.Constants.ROWS) && !this.lexer.identifierEquals(FnvHash.Constants.RANGE) && !this.lexer.identifierEquals(FnvHash.Constants.GROUPS)) {
         SQLExpr name = this.expr();
         kbOver.setExistingName(name);
      }

      if (this.lexer.token() == Token.PARTITION || this.lexer.identifierEquals("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);
            if (over.getPartitionBy().size() == 1 && this.lexer.token() == Token.COMMA) {
               this.lexer.nextToken();
               this.exprList(over.getPartitionBy(), over);
            }
         } else {
            this.exprList(over.getPartitionBy(), over);
         }
      }

      over.setOrderBy(this.parseOrderBy());
      over.setDistributeBy(this.parseDistributeBy());
      over.setSortBy(this.parseSortBy());
      if (this.lexer.token() == Token.OF) {
         this.lexer.nextToken();
         SQLName of = this.name();
         over.setOf(of);
      }

      SQLOver.WindowingType windowingType = null;
      if (!this.lexer.identifierEquals(FnvHash.Constants.ROWS) && this.lexer.token() != Token.ROWS) {
         if (this.lexer.identifierEquals(FnvHash.Constants.RANGE)) {
            windowingType = SQLOver.WindowingType.RANGE;
         } else if (this.lexer.identifierEquals(FnvHash.Constants.GROUPS)) {
            windowingType = SQLOver.WindowingType.GROUPS;
         }
      } else {
         windowingType = SQLOver.WindowingType.ROWS;
      }

      if (windowingType != null) {
         over.setWindowingType(windowingType);
         this.lexer.nextToken();
         if (this.lexer.token() == Token.BETWEEN) {
            this.lexer.nextToken();
            if (this.lexer.token() != Token.LITERAL_INT && this.lexer.token() != Token.LITERAL_FLOAT && this.lexer.token() != Token.LITERAL_CHARS) {
               if (this.lexer.token() == Token.IDENTIFIER) {
                  long hash = this.lexer.hash_lower();
                  if (hash != FnvHash.Constants.PRECEDING && hash != FnvHash.Constants.FOLLOWING && hash != FnvHash.Constants.CURRENT && hash != FnvHash.Constants.UNBOUNDED) {
                     SQLExpr betweenBegin = this.primary();
                     over.setWindowingBetweenBegin(betweenBegin);
                  }
               }
            } else {
               SQLExpr betweenBegin = this.additive();
               over.setWindowingBetweenBegin(betweenBegin);
            }

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

            this.accept(Token.AND);
            if (this.lexer.token() != Token.LITERAL_INT && this.lexer.token() != Token.LITERAL_FLOAT && this.lexer.token() != Token.LITERAL_CHARS) {
               if (this.lexer.token() == Token.IDENTIFIER) {
                  long hash = this.lexer.hash_lower();
                  if (hash != FnvHash.Constants.PRECEDING && hash != FnvHash.Constants.FOLLOWING && hash != FnvHash.Constants.CURRENT && hash != FnvHash.Constants.UNBOUNDED) {
                     SQLExpr betweenBegin = this.additive();
                     over.setWindowingBetweenEnd(betweenBegin);
                  }
               }
            } else {
               SQLExpr betweenEnd = this.additive();
               over.setWindowingBetweenEnd(betweenEnd);
            }

            SQLOver.WindowingBound endBound = this.parseWindowingBound();
            if (endBound != null) {
               over.setWindowingBetweenEndBound(endBound);
            }
         } else {
            if (this.lexer.token() != Token.LITERAL_INT && this.lexer.token() != Token.LITERAL_FLOAT && this.lexer.token() != Token.LITERAL_CHARS) {
               if (this.lexer.token() == Token.IDENTIFIER) {
                  long hash = this.lexer.hash_lower();
                  if (hash != FnvHash.Constants.PRECEDING && hash != FnvHash.Constants.FOLLOWING && hash != FnvHash.Constants.CURRENT && hash != FnvHash.Constants.UNBOUNDED) {
                     SQLExpr betweenBegin = this.additive();
                     over.setWindowingBetweenBegin(betweenBegin);
                  }
               }
            } else {
               SQLExpr betweenBegin = this.additive();
               over.setWindowingBetweenBegin(betweenBegin);
            }

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

      if (this.lexer.token() == Token.EXCLUDE) {
         this.lexer.nextToken();
         if (this.lexer.identifierEquals(FnvHash.Constants.CURRENT)) {
            this.lexer.nextToken();
            kbOver.setExcludeBound(KingbaseSQLOver.WindowingExclude.CURRENT_ROW);
         } else if (this.lexer.token() == Token.GROUP) {
            kbOver.setExcludeBound(KingbaseSQLOver.WindowingExclude.GROUP);
         } else if (this.lexer.identifierEquals(FnvHash.Constants.TIES)) {
            kbOver.setExcludeBound(KingbaseSQLOver.WindowingExclude.TIES);
         } else {
            this.lexer.nextToken();
            kbOver.setExcludeBound(KingbaseSQLOver.WindowingExclude.NO_OTHERS);
         }

         this.lexer.nextToken();
      }

      this.accept(Token.RPAREN);
   }

   public SQLExpr expr() {
      if (this.lexer.token() != Token.INTERVAL && this.lexer.token() != Token.DATE && this.lexer.token() != Token.TIMESTAMP && this.lexer.token() != Token.TIMESTAMPTZ && this.lexer.token() != Token.TIME) {
         if (this.lexer.token() == Token.LITERAL_BCHARS) {
            this.lexer.nextToken();
            KingbaseCharExpr expr = new KingbaseCharExpr();
            expr.setType("B");
            expr.setText(this.lexer.stringVal());
            this.lexer.nextToken();
            return expr;
         } else if (this.lexer.token() == Token.BARSLASH) {
            this.lexer.nextToken();
            SQLUnaryExpr expr = new SQLUnaryExpr();
            expr.setOperator(SQLUnaryOperator.Sqrt);
            SQLExpr opExpr = this.expr();
            expr.setExpr(opExpr);
            return expr;
         } else if (this.lexer.token() == Token.BARBARSLASH) {
            this.lexer.nextToken();
            SQLUnaryExpr expr = new SQLUnaryExpr();
            expr.setOperator(SQLUnaryOperator.Cubet);
            SQLExpr opExpr = this.expr();
            expr.setExpr(opExpr);
            return expr;
         } else if (this.lexer.token() == Token.MONKEYS_AT) {
            this.lexer.nextToken();
            SQLUnaryExpr expr = new SQLUnaryExpr();
            expr.setOperator(SQLUnaryOperator.Abs);
            SQLExpr opExpr = this.expr();
            expr.setExpr(opExpr);
            return expr;
         } else if (this.lexer.token() == Token.BANG) {
            this.lexer.nextToken();
            SQLUnaryExpr expr = new SQLUnaryExpr();
            expr.setOperator(SQLUnaryOperator.FactorialSuffix);
            SQLExpr opExpr = this.expr();
            expr.setExpr(opExpr);
            return expr;
         } else if (this.lexer.token() == Token.BANGBANG) {
            this.lexer.nextToken();
            SQLUnaryExpr expr = new SQLUnaryExpr();
            expr.setOperator(SQLUnaryOperator.FactorialPrefix);
            SQLExpr opExpr = this.expr();
            expr.setExpr(opExpr);
            return expr;
         } else {
            return super.expr();
         }
      } else {
         return this.parseDateTimeExpr();
      }
   }

   public SQLExpr parseDateTimeExpr() {
      Token token = this.lexer.token();
      KingbaseDateTimeExpr expr = new KingbaseDateTimeExpr();
      if (token == Token.TIMESTAMP) {
         expr.setTimeType(KingbaseDateTimeExpr.DateTimeType.TIMESTAMP);
      } else if (token == Token.TIMESTAMPTZ) {
         expr.setTimeType(KingbaseDateTimeExpr.DateTimeType.TIMESTAMPTZ);
      } else if (token == Token.DATE) {
         expr.setTimeType(KingbaseDateTimeExpr.DateTimeType.DATE);
      } else if (token == Token.TIME) {
         expr.setTimeType(KingbaseDateTimeExpr.DateTimeType.TIME);
      } else if (token == Token.INTERVAL) {
         expr.setTimeType(KingbaseDateTimeExpr.DateTimeType.INTERVAL);
      }

      this.lexer.nextToken();
      token = this.lexer.token();
      if (token == Token.LPAREN) {
         this.lexer.nextToken();
         SQLExpr precision = this.expr();
         expr.setPrecision(precision);
         this.accept(Token.RPAREN);
         token = this.lexer.token();
      }

      if (token == Token.WITH) {
         this.lexer.nextToken();
         if (this.lexer.token() == Token.LOCAL) {
            expr.setLocal(true);
            this.lexer.nextToken();
         }

         this.lexer.nextToken();
         this.lexer.nextToken();
         expr.setWithTimezone(true);
         token = this.lexer.token();
      } else if (token == Token.WITHOUT) {
         this.lexer.nextToken();
         this.lexer.nextToken();
         this.lexer.nextToken();
         expr.setWithoutTimezone(true);
         token = this.lexer.token();
      }

      if (token == Token.LITERAL_CHARS) {
         KingbaseCharExpr value = new KingbaseCharExpr(this.lexer.stringVal());
         expr.setValue(value);
         this.lexer.nextToken();
         token = this.lexer.token();
      }

      if (expr.getTimeType() == KingbaseDateTimeExpr.DateTimeType.INTERVAL && (token == Token.YEAR || token == Token.MONTH || token == Token.DAY || token == Token.HOUR || token == Token.MINUTE || token == Token.SECOND || token == Token.YEAR_MONTH || token == Token.DAY_HOUR || token == Token.DAY_MINUTE || token == Token.DAY_SECOND || token == Token.HOUR_MINUTE || token == Token.HOUR_SECOND || token == Token.MINUTE_SECOND)) {
         expr.setIntervalBegin(new SQLIdentifierExpr(token.name));
         this.lexer.nextToken();
         token = this.lexer.token();
         if (token == Token.TO) {
            this.lexer.nextToken();
            token = this.lexer.token();
            expr.setIntervalEnd(new SQLIdentifierExpr(token.name));
            this.lexer.nextToken();
         }
      }

      return 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.identifierEquals(FnvHash.Constants.FILTER)) {
         this.filter(aggregateExpr);
      }

      if (this.lexer.token() == Token.OVER) {
         this.over(aggregateExpr);
      }

      return aggregateExpr;
   }

   protected void over(SQLAggregateExpr aggregateExpr) {
      this.lexer.nextToken();
      if (this.lexer.token() != Token.LPAREN) {
         SQLName overRef = this.name();
         aggregateExpr.setOverRef(overRef);
      } else {
         KingbaseSQLOver over = new KingbaseSQLOver();
         this.over((SQLOver)over);
         aggregateExpr.setOver(over);
      }
   }

   public SQLLimit parseLimit() {
      if (this.lexer.token() != Token.LIMIT) {
         return null;
      } else {
         SQLLimit limit = new SQLLimit();
         limit.setType(SQLLimit.Type.LIMIT);
         this.lexer.nextTokenValue();
         SQLExpr temp;
         if (this.lexer.token() == Token.LITERAL_INT) {
            temp = new SQLIntegerExpr(this.lexer.integerValue());
            this.lexer.nextTokenComma();
            if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.EOF && this.lexer.token() != Token.IDENTIFIER) {
               temp = this.primaryRest(temp);
               temp = this.exprRest(temp);
            }
         } else if (this.lexer.token() == Token.ALL) {
            temp = new SQLIdentifierExpr(this.lexer.token().name);
            this.lexer.nextTokenComma();
            if (this.lexer.token() != Token.COMMA && this.lexer.token() != Token.EOF && this.lexer.token() != Token.IDENTIFIER) {
               temp = this.primaryRest(temp);
               temp = this.exprRest(temp);
            }
         } else {
            temp = this.expr();
         }

         if (this.lexer.token() == Token.COMMA) {
            limit.setOffset(temp);
            this.lexer.nextTokenValue();
            SQLExpr rowCount;
            if (this.lexer.token() == Token.LITERAL_INT) {
               rowCount = new SQLIntegerExpr(this.lexer.integerValue());
               this.lexer.nextToken();
               if (this.lexer.token() != Token.EOF && this.lexer.token() != Token.IDENTIFIER) {
                  rowCount = this.primaryRest(rowCount);
                  rowCount = this.exprRest(rowCount);
               }
            } else {
               rowCount = this.expr();
            }

            limit.setRowCount(rowCount);
         } else if (this.lexer.token() == Token.OFFSET) {
            limit.setType(SQLLimit.Type.LIMIT_OFFSET);
            limit.setRowCount(temp);
            this.lexer.nextToken();
            limit.setOffset(this.expr());
         } else {
            limit.setRowCount(temp);
         }

         return limit;
      }
   }

   public SQLExpr bitXorRest(SQLExpr expr) {
      Token token = this.lexer.token();
      if (token != Token.POUND && (token != Token.VARIANT || !this.lexer.stringVal().equals("#"))) {
         return expr;
      } else {
         this.lexer.nextToken();
         SQLBinaryOperator op = SQLBinaryOperator.BitwiseXor_KINGBASE;
         SQLExpr rightExp = this.primary();
         expr = new SQLBinaryOpExpr(expr, op, rightExp, this.dbType);
         expr = this.bitXorRest(expr);
         return expr;
      }
   }

   public SQLName name() {
      try {
         return super.name();
      } catch (ParserException e) {
         switch (this.lexer.token()) {
            case TIMESTAMP:
            case TIMESTAMPTZ:
            case TIME:
            case DATE:
            case YEAR:
            case MONTH:
            case DAY:
            case HOUR:
            case MINUTE:
            case SECOND:
            case YEAR_MONTH:
            case DAY_MINUTE:
            case DAY_SECOND:
            case HOUR_MINUTE:
            case HOUR_SECOND:
            case MINUTE_SECOND:
               String identName = this.lexer.stringVal();
               SQLIdentifierExpr identifierExpr = new SQLIdentifierExpr(identName);
               this.lexer.nextToken();
               return identifierExpr;
            case INTERVAL:
            case INFINITY:
            case NAN:
            case EPOCH:
            case TODAY:
            case TOMORROW:
            case YESTERDAY:
            case ALLBALLS:
            case ROWNUM:
            default:
               throw e;
         }
      }
   }

   static {
      String[] strings = new String[]{"AVG", "CORR", "COVAR_POP", "COVAR_SAMP", "COUNT", "CUME_DIST", "DENSE_RANK", "LISTAGG", "MAX", "MIN", "RANK", "PERCENT_RANK", "REGR_SLOPE", "REGR_INTERCEPT", "REGR_COUNT", "REGR_R2", "REGR_AVGX", "REGR_AVGY", "REGR_SXX", "REGR_SYY", "REGR_SXY", "STDDEV", "STDDEV_POP", "STDDEV_SAMP", "SUM", "VAR_POP", "VAR_SAMP", "VARIANCE", "WM_CONCAT", "BIT_AND", "BIT_OR", "BOOL_ADN", "BOOL_OR", "EVERY", "JSON_AGG", "JSON_OBJECT_AGG", "JSONB_OBJECT_AGG", "STRING_AGG", "XMLAGG", "PERCENTILE_CONT", "PERCENTILE_DISC", "ROW_NUMBER", "RANK", "NTILE", "LAG", "LEAD", "FIRST_VALUE", "LAST_VALUE", "NTH_VALUE", "RATIO_TO_REPORT"};
      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;
      }

   }
}
