content: /*
   * Copyright 1999-2012 Alibaba Group.
   *  
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *  
   *      http://www.apache.org/licenses/LICENSE-2.0
   *  
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * (created at 2011-4-12)
   */
  package com.alibaba.cobar.parser.recognizer.mysql.syntax;
  
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.EOF;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.IDENTIFIER;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_AND;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_AS;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_ASC;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_BY;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_COLLATE;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_DESC;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_DISTINCT;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_FROM;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_IN;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_INTEGER;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_LIKE;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_NOT;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_SELECT;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_SEPARATOR;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_THEN;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_USING;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_WHEN;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.KW_WITH;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.LITERAL_CHARS;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.OP_ASSIGN;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.PUNC_COMMA;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.PUNC_DOT;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.PUNC_LEFT_PAREN;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.PUNC_RIGHT_PAREN;
  import static com.alibaba.cobar.parser.recognizer.mysql.MySQLToken.USR_VAR;
  
  import java.sql.SQLSyntaxErrorException;
  import java.util.ArrayList;
  import java.util.LinkedList;
  import java.util.List;
  
  import com.alibaba.cobar.parser.ast.expression.Expression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.ArithmeticAddExpression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.ArithmeticDivideExpression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.ArithmeticIntegerDivideExpression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.ArithmeticModExpression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.ArithmeticMultiplyExpression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.ArithmeticSubtractExpression;
  import com.alibaba.cobar.parser.ast.expression.arithmeic.MinusExpression;
  import com.alibaba.cobar.parser.ast.expression.bit.BitAndExpression;
  import com.alibaba.cobar.parser.ast.expression.bit.BitInvertExpression;
  import com.alibaba.cobar.parser.ast.expression.bit.BitOrExpression;
  import com.alibaba.cobar.parser.ast.expression.bit.BitShiftExpression;
  import com.alibaba.cobar.parser.ast.expression.bit.BitXORExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.BetweenAndExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionEqualsExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionGreaterThanExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionGreaterThanOrEqualsExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionIsExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionLessOrGreaterThanExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionLessThanExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionLessThanOrEqualsExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionNotEqualsExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.ComparisionNullSafeEqualsExpression;
  import com.alibaba.cobar.parser.ast.expression.comparison.InExpression;
  import com.alibaba.cobar.parser.ast.expression.logical.LogicalAndExpression;
  import com.alibaba.cobar.parser.ast.expression.logical.LogicalNotExpression;
  import com.alibaba.cobar.parser.ast.expression.logical.LogicalOrExpression;
  import com.alibaba.cobar.parser.ast.expression.logical.LogicalXORExpression;
  import com.alibaba.cobar.parser.ast.expression.logical.NegativeValueExpression;
  import com.alibaba.cobar.parser.ast.expression.misc.AssignmentExpression;
  import com.alibaba.cobar.parser.ast.expression.misc.InExpressionList;
  import com.alibaba.cobar.parser.ast.expression.misc.QueryExpression;
  import com.alibaba.cobar.parser.ast.expression.misc.SubqueryAllExpression;
  import com.alibaba.cobar.parser.ast.expression.misc.SubqueryAnyExpression;
  import com.alibaba.cobar.parser.ast.expression.misc.UserExpression;
  import com.alibaba.cobar.parser.ast.expression.primary.CaseWhenOperatorExpression;
  import com.alibaba.cobar.parser.ast.expression.primary.DefaultValue;
  import com.alibaba.cobar.parser.ast.expression.primary.ExistsPrimary;
  import com.alibaba.cobar.parser.ast.expression.primary.Identifier;
  import com.alibaba.cobar.parser.ast.expression.primary.MatchExpression;
  import com.alibaba.cobar.parser.ast.expression.primary.MatchExpression.Modifier;
  import com.alibaba.cobar.parser.ast.expression.primary.RowExpression;
  import com.alibaba.cobar.parser.ast.expression.primary.UsrDefVarPrimary;
  import com.alibaba.cobar.parser.ast.expression.primary.Wildcard;
  import com.alibaba.cobar.parser.ast.expression.primary.function.FunctionExpression;
  import com.alibaba.cobar.parser.ast.expression.primary.function.cast.Cast;
  import com.alibaba.cobar.parser.ast.expression.primary.function.cast.Convert;
  import com.alibaba.cobar.parser.ast.expression.primary.function.comparison.Interval;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Curdate;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Curtime;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Extract;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.GetFormat;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Now;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Timestampadd;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.Timestampdiff;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.UtcDate;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.UtcTime;
  import com.alibaba.cobar.parser.ast.expression.primary.function.datetime.UtcTimestamp;
  import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Avg;
  import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Count;
  import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.GroupConcat;
  import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Max;
  import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Min;
  import com.alibaba.cobar.parser.ast.expression.primary.function.groupby.Sum;
  import com.alibaba.cobar.parser.ast.expression.primary.function.info.CurrentUser;
  import com.alibaba.cobar.parser.ast.expression.primary.function.string.Char;
  import com.alibaba.cobar.parser.ast.expression.primary.function.string.Locate;
  import com.alibaba.cobar.parser.ast.expression.primary.function.string.Substring;
  import com.alibaba.cobar.parser.ast.expression.primary.function.string.Trim;
  import com.alibaba.cobar.parser.ast.expression.primary.function.string.Trim.Direction;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.IntervalPrimary;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralBitField;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralBoolean;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralHexadecimal;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralNull;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralNumber;
  import com.alibaba.cobar.parser.ast.expression.primary.literal.LiteralString;
  import com.alibaba.cobar.parser.ast.expression.string.LikeExpression;
  import com.alibaba.cobar.parser.ast.expression.string.RegexpExpression;
  import com.alibaba.cobar.parser.ast.expression.string.SoundsLikeExpression;
  import com.alibaba.cobar.parser.ast.expression.type.CastBinaryExpression;
  import com.alibaba.cobar.parser.ast.expression.type.CollateExpression;
  import com.alibaba.cobar.parser.recognizer.mysql.MySQLFunctionManager;
  import com.alibaba.cobar.parser.recognizer.mysql.MySQLToken;
  import com.alibaba.cobar.parser.recognizer.mysql.lexer.MySQLLexer;
  import com.alibaba.cobar.parser.util.Pair;
  
  /**
   * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a>
   */
  public class MySQLExprParser extends MySQLParser {
      public MySQLExprParser(MySQLLexer lexer) {
          this(lexer, MySQLFunctionManager.INSTANCE_MYSQL_DEFAULT, true, DEFAULT_CHARSET);
      }
  
      public MySQLExprParser(MySQLLexer lexer, String charset) {
          this(lexer, MySQLFunctionManager.INSTANCE_MYSQL_DEFAULT, true, charset);
      }
  
      public MySQLExprParser(MySQLLexer lexer, MySQLFunctionManager functionManager, boolean cacheEvalRst, String charset) {
          super(lexer, cacheEvalRst);
          this.functionManager = functionManager;
          this.charset = charset == null ? DEFAULT_CHARSET : charset;
      }
  
      private String charset;
      private final MySQLFunctionManager functionManager;
      private MySQLDMLSelectParser selectParser;
  
      public void setSelectParser(MySQLDMLSelectParser selectParser) {
          this.selectParser = selectParser;
      }
  
      /**
       * first token of this expression has been scanned, not yet consumed
       */
      public Expression expression() throws SQLSyntaxErrorException {
          MySQLToken token = lexer.token();
          if (token == null) {
              token = lexer.nextToken();
          }
          if (token == EOF) {
              err("unexpected EOF");
          }
          Expression left = logicalOrExpression();
          if (lexer.token() == OP_ASSIGN) {
              lexer.nextToken();
              Expression right = expression();
              return new AssignmentExpression(left, right).setCacheEvalRst(cacheEvalRst);
          }
          return left;
      }
  
      /**
       * <code>higherPRJExpr ( ( '||' | 'OR' ) higherPRJExpr )*</code>
       * 
       * @throws SQLSyntaxErrorException
       */
      private Expression logicalOrExpression() throws SQLSyntaxErrorException {
          LogicalOrExpression or = null;
          for (Expression expr = logicalXORExpression();;) {
              switch (lexer.token()) {
              case OP_LOGICAL_OR:
              case KW_OR:
                  lexer.nextToken();
                  if (or == null) {
                      or = new LogicalOrExpression();
                      or.setCacheEvalRst(cacheEvalRst);
                      or.appendOperand(expr);
                      expr = or;
                  }
                  Expression newExpr = logicalXORExpression();
                  or.appendOperand(newExpr);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>higherPRJExpr ( 'XOR' higherPRJExpr )*</code>
       * 
       * @throws SQLSyntaxErrorException
       */
      private Expression logicalXORExpression() throws SQLSyntaxErrorException {
          for (Expression expr = logicalAndExpression();;) {
              switch (lexer.token()) {
              case KW_XOR:
                  lexer.nextToken();
                  Expression newExpr = logicalAndExpression();
                  expr = new LogicalXORExpression(expr, newExpr).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>higherPRJExpr ( ('AND'|'&&') higherPRJExpr )*</code>
       * 
       * @throws SQLSyntaxErrorException
       */
      private Expression logicalAndExpression() throws SQLSyntaxErrorException {
          LogicalAndExpression and = null;
          for (Expression expr = logicalNotExpression();;) {
              switch (lexer.token()) {
              case OP_LOGICAL_AND:
              case KW_AND:
                  lexer.nextToken();
                  if (and == null) {
                      and = new LogicalAndExpression();
                      and.setCacheEvalRst(cacheEvalRst);
                      and.appendOperand(expr);
                      expr = and;
                  }
                  Expression newExpr = logicalNotExpression();
                  and.appendOperand(newExpr);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>'NOT'* higherPRJExpr</code>
       * 
       * @throws SQLSyntaxErrorException
       */
      private Expression logicalNotExpression() throws SQLSyntaxErrorException {
          int not = 0;
          for (; lexer.token() == KW_NOT; ++not) {
              lexer.nextToken();
          }
          Expression expr = comparisionExpression();
          for (; not > 0; --not) {
              expr = new LogicalNotExpression(expr).setCacheEvalRst(cacheEvalRst);
          }
          return expr;
      }
  
      /**
       * <code>BETWEEN ... AND</code> has lower precedence than other comparison
       * operator
       */
      private Expression comparisionExpression() throws SQLSyntaxErrorException {
          Expression temp;
          for (Expression fst = bitOrExpression(null, null);;) {
              switch (lexer.token()) {
              case KW_NOT:
                  lexer.nextToken();
                  switch (lexer.token()) {
                  case KW_BETWEEN:
                      lexer.nextToken();
                      Expression snd = comparisionExpression();
                      match(KW_AND);
                      Expression trd = comparisionExpression();
                      return new BetweenAndExpression(true, fst, snd, trd).setCacheEvalRst(cacheEvalRst);
                  case KW_RLIKE:
                  case KW_REGEXP:
                      lexer.nextToken();
                      temp = bitOrExpression(null, null);
                      fst = new RegexpExpression(true, fst, temp).setCacheEvalRst(cacheEvalRst);
                      continue;
                  case KW_LIKE:
                      lexer.nextToken();
                      temp = bitOrExpression(null, null);
                      Expression escape = null;
                      if (equalsIdentifier("ESCAPE") >= 0) {
                          lexer.nextToken();
                          escape = bitOrExpression(null, null);
                      }
                      fst = new LikeExpression(true, fst, temp, escape).setCacheEvalRst(cacheEvalRst);
                      continue;
                  case KW_IN:
                      if (lexer.nextToken() != PUNC_LEFT_PAREN) {
                          lexer.addCacheToke(KW_IN);
                          return fst;
                      }
                      Expression in = rightOprandOfIn();
                      fst = new InExpression(true, fst, in).setCacheEvalRst(cacheEvalRst);
                      continue;
                  default:
                      throw err("unexpect token after NOT: " + lexer.token());
                  }
              case KW_BETWEEN:
                  lexer.nextToken();
                  Expression snd = comparisionExpression();
                  match(KW_AND);
                  Expression trd = comparisionExpression();
                  return new BetweenAndExpression(false, fst, snd, trd).setCacheEvalRst(cacheEvalRst);
              case KW_RLIKE:
              case KW_REGEXP:
                  lexer.nextToken();
                  temp = bitOrExpression(null, null);
                  fst = new RegexpExpression(false, fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case KW_LIKE:
                  lexer.nextToken();
                  temp = bitOrExpression(null, null);
                  Expression escape = null;
                  if (equalsIdentifier("ESCAPE") >= 0) {
                      lexer.nextToken();
                      escape = bitOrExpression(null, null);
                  }
                  fst = new LikeExpression(false, fst, temp, escape).setCacheEvalRst(cacheEvalRst);
                  continue;
              case KW_IN:
                  if (lexer.nextToken() != PUNC_LEFT_PAREN) {
                      lexer.addCacheToke(KW_IN);
                      return fst;
                  }
                  temp = rightOprandOfIn();
                  fst = new InExpression(false, fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case KW_IS:
                  switch (lexer.nextToken()) {
                  case KW_NOT:
                      switch (lexer.nextToken()) {
                      case LITERAL_NULL:
                          lexer.nextToken();
                          fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_NOT_NULL).setCacheEvalRst(cacheEvalRst);
                          continue;
                      case LITERAL_BOOL_FALSE:
                          lexer.nextToken();
                          fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_NOT_FALSE).setCacheEvalRst(cacheEvalRst);
                          continue;
                      case LITERAL_BOOL_TRUE:
                          lexer.nextToken();
                          fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_NOT_TRUE).setCacheEvalRst(cacheEvalRst);
                          continue;
                      default:
                          matchIdentifier("UNKNOWN");
                          fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_NOT_UNKNOWN).setCacheEvalRst(cacheEvalRst);
                          continue;
                      }
                  case LITERAL_NULL:
                      lexer.nextToken();
                      fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_NULL).setCacheEvalRst(cacheEvalRst);
                      continue;
                  case LITERAL_BOOL_FALSE:
                      lexer.nextToken();
                      fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_FALSE).setCacheEvalRst(cacheEvalRst);
                      continue;
                  case LITERAL_BOOL_TRUE:
                      lexer.nextToken();
                      fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_TRUE).setCacheEvalRst(cacheEvalRst);
                      continue;
                  default:
                      matchIdentifier("UNKNOWN");
                      fst = new ComparisionIsExpression(fst, ComparisionIsExpression.IS_UNKNOWN).setCacheEvalRst(cacheEvalRst);
                      continue;
                  }
              case OP_EQUALS:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionEqualsExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_NULL_SAFE_EQUALS:
                  lexer.nextToken();
                  temp = bitOrExpression(null, null);
                  fst = new ComparisionNullSafeEqualsExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_GREATER_OR_EQUALS:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionGreaterThanOrEqualsExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_GREATER_THAN:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionGreaterThanExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_LESS_OR_EQUALS:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionLessThanOrEqualsExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_LESS_THAN:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionLessThanExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_LESS_OR_GREATER:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionLessOrGreaterThanExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              case OP_NOT_EQUALS:
                  lexer.nextToken();
                  temp = anyAllExpression();
                  fst = new ComparisionNotEqualsExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                  continue;
              default:
                  if (equalsIdentifier("SOUNDS") >= 0) {
                      lexer.nextToken();
                      match(KW_LIKE);
                      temp = bitOrExpression(null, null);
                      fst = new SoundsLikeExpression(fst, temp).setCacheEvalRst(cacheEvalRst);
                      continue;
                  }
                  return fst;
              }
          }
      }
  
      /**
       * @return {@link QueryExpression} or {@link InExpressionList}
       */
      private Expression rightOprandOfIn() throws SQLSyntaxErrorException {
          match(PUNC_LEFT_PAREN);
          if (KW_SELECT == lexer.token()) {
              QueryExpression subq = subQuery();
              match(PUNC_RIGHT_PAREN);
              return subq;
          }
          return new InExpressionList(expressionList(new LinkedList<Expression>())).setCacheEvalRst(cacheEvalRst);
      }
  
      private Expression anyAllExpression() throws SQLSyntaxErrorException {
          QueryExpression subquery = null;
          switch (lexer.token()) {
          case KW_ALL:
              lexer.nextToken();
              match(PUNC_LEFT_PAREN);
              subquery = subQuery();
              match(PUNC_RIGHT_PAREN);
              return new SubqueryAllExpression(subquery).setCacheEvalRst(cacheEvalRst);
          default:
              int matchIndex = equalsIdentifier("SOME", "ANY");
              if (matchIndex < 0) {
                  return bitOrExpression(null, null);
              }
              String consumed = lexer.stringValue();
              String consumedUp = lexer.stringValueUppercase();
              if (lexer.nextToken() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  subquery = subQuery();
                  match(PUNC_RIGHT_PAREN);
                  return new SubqueryAnyExpression(subquery).setCacheEvalRst(cacheEvalRst);
              }
              return bitOrExpression(consumed, consumedUp);
          }
      }
  
      /**
       * @param consumed not null means that a token that has been pre-consumed
       *            stands for next token
       */
      private Expression bitOrExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          for (Expression expr = bitAndExpression(consumed, consumedUp);;) {
              switch (lexer.token()) {
              case OP_VERTICAL_BAR:
                  lexer.nextToken();
                  Expression newExpr = bitAndExpression(null, null);
                  expr = new BitOrExpression(expr, newExpr).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      private Expression bitAndExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          for (Expression expr = bitShiftExpression(consumed, consumedUp);;) {
              switch (lexer.token()) {
              case OP_AMPERSAND:
                  lexer.nextToken();
                  Expression newExpr = bitShiftExpression(null, null);
                  expr = new BitAndExpression(expr, newExpr).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>higherExpr ( ('&lt;&lt;'|'&gt;&gt;') higherExpr)+</code>
       */
      private Expression bitShiftExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          Expression temp;
          for (Expression expr = arithmeticTermOperatorExpression(consumed, consumedUp);;) {
              switch (lexer.token()) {
              case OP_LEFT_SHIFT:
                  lexer.nextToken();
                  temp = arithmeticTermOperatorExpression(null, null);
                  expr = new BitShiftExpression(false, expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              case OP_RIGHT_SHIFT:
                  lexer.nextToken();
                  temp = arithmeticTermOperatorExpression(null, null);
                  expr = new BitShiftExpression(true, expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>higherExpr ( ('+'|'-') higherExpr)+</code>
       */
      private Expression arithmeticTermOperatorExpression(String consumed, String consumedUp)
              throws SQLSyntaxErrorException {
          Expression temp;
          for (Expression expr = arithmeticFactorOperatorExpression(consumed, consumedUp);;) {
              switch (lexer.token()) {
              case OP_PLUS:
                  lexer.nextToken();
                  temp = arithmeticFactorOperatorExpression(null, null);
                  expr = new ArithmeticAddExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              case OP_MINUS:
                  lexer.nextToken();
                  temp = arithmeticFactorOperatorExpression(null, null);
                  expr = new ArithmeticSubtractExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>higherExpr ( ('*'|'/'|'%'|'DIV'|'MOD') higherExpr)+</code>
       */
      private Expression arithmeticFactorOperatorExpression(String consumed, String consumedUp)
              throws SQLSyntaxErrorException {
          Expression temp;
          for (Expression expr = bitXORExpression(consumed, consumedUp);;) {
              switch (lexer.token()) {
              case OP_ASTERISK:
                  lexer.nextToken();
                  temp = bitXORExpression(null, null);
                  expr = new ArithmeticMultiplyExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              case OP_SLASH:
                  lexer.nextToken();
                  temp = bitXORExpression(null, null);
                  expr = new ArithmeticDivideExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              case KW_DIV:
                  lexer.nextToken();
                  temp = bitXORExpression(null, null);
                  expr = new ArithmeticIntegerDivideExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              case OP_PERCENT:
              case KW_MOD:
                  lexer.nextToken();
                  temp = bitXORExpression(null, null);
                  expr = new ArithmeticModExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>higherExpr ('^' higherExpr)+</code>
       */
      private Expression bitXORExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          Expression temp;
          for (Expression expr = unaryOpExpression(consumed, consumedUp);;) {
              switch (lexer.token()) {
              case OP_CARET:
                  lexer.nextToken();
                  temp = unaryOpExpression(null, null);
                  expr = new BitXORExpression(expr, temp).setCacheEvalRst(cacheEvalRst);
                  break;
              default:
                  return expr;
              }
          }
      }
  
      /**
       * <code>('+'|'-'|'~'|'!'|'BINARY')* higherExpr</code><br/>
       * '!' has higher precedence
       */
      private Expression unaryOpExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          if (consumed == null) {
              Expression expr;
              switch (lexer.token()) {
              case OP_EXCLAMATION:
                  lexer.nextToken();
                  expr = unaryOpExpression(null, null);
                  return new NegativeValueExpression(expr).setCacheEvalRst(cacheEvalRst);
              case OP_PLUS:
                  lexer.nextToken();
                  return unaryOpExpression(null, null);
              case OP_MINUS:
                  lexer.nextToken();
                  expr = unaryOpExpression(null, null);
                  return new MinusExpression(expr).setCacheEvalRst(cacheEvalRst);
              case OP_TILDE:
                  lexer.nextToken();
                  expr = unaryOpExpression(null, null);
                  return new BitInvertExpression(expr).setCacheEvalRst(cacheEvalRst);
              case KW_BINARY:
                  lexer.nextToken();
                  expr = unaryOpExpression(null, null);
                  return new CastBinaryExpression(expr).setCacheEvalRst(cacheEvalRst);
              }
          }
          return collateExpression(consumed, consumedUp);
      }
  
      private Expression collateExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          for (Expression expr = userExpression(consumed, consumedUp);;) {
              if (lexer.token() == KW_COLLATE) {
                  lexer.nextToken();
                  String collateName = lexer.stringValue();
                  match(IDENTIFIER);
                  expr = new CollateExpression(expr, collateName).setCacheEvalRst(cacheEvalRst);
                  continue;
              }
              return expr;
          }
      }
  
      private Expression userExpression(String consumed, String consumedUp) throws SQLSyntaxErrorException {
          Expression first = primaryExpression(consumed, consumedUp);
          if (lexer.token() == USR_VAR) {
              if (first instanceof LiteralString) {
                  StringBuilder str = new StringBuilder().append('\'')
                                                         .append(((LiteralString) first).getString())
                                                         .append('\'')
                                                         .append(lexer.stringValue());
                  lexer.nextToken();
                  return new UserExpression(str.toString()).setCacheEvalRst(cacheEvalRst);
              } else if (first instanceof Identifier) {
                  StringBuilder str = new StringBuilder().append(((Identifier) first).getIdText()).append(
                          lexer.stringValue());
                  lexer.nextToken();
                  return new UserExpression(str.toString()).setCacheEvalRst(cacheEvalRst);
              }
          }
          return first;
      }
  
      private Expression primaryExpression(final String consumed, String consumedUp) throws SQLSyntaxErrorException {
          if (consumed != null) {
              return startedFromIdentifier(consumed, consumedUp);
          }
          String tempStr;
          String tempStrUp;
          StringBuilder tempSb;
          Number tempNum;
          Expression tempExpr;
          Expression tempExpr2;
          List<Expression> tempExprList;
          switch (lexer.token()) {
          case PLACE_HOLDER:
              tempStr = lexer.stringValue();
              tempStrUp = lexer.stringValueUppercase();
              lexer.nextToken();
              return createPlaceHolder(tempStr, tempStrUp);
          case LITERAL_BIT:
              tempStr = lexer.stringValue();
              lexer.nextToken();
              return new LiteralBitField(null, tempStr).setCacheEvalRst(cacheEvalRst);
          case LITERAL_HEX:
              LiteralHexadecimal hex = new LiteralHexadecimal(
                      null,
                      lexer.getSQL(),
                      lexer.getOffsetCache(),
                      lexer.getSizeCache(),
                      charset);
              lexer.nextToken();
              return hex.setCacheEvalRst(cacheEvalRst);
          case LITERAL_BOOL_FALSE:
              lexer.nextToken();
              return new LiteralBoolean(false).setCacheEvalRst(cacheEvalRst);
          case LITERAL_BOOL_TRUE:
              lexer.nextToken();
              return new LiteralBoolean(true).setCacheEvalRst(cacheEvalRst);
          case LITERAL_NULL:
              lexer.nextToken();
              return new LiteralNull().setCacheEvalRst(cacheEvalRst);
          case LITERAL_NCHARS:
              tempSb = new StringBuilder();
              do {
                  lexer.appendStringContent(tempSb);
              } while (lexer.nextToken() == LITERAL_CHARS);
              return new LiteralString(null, tempSb.toString(), true).setCacheEvalRst(cacheEvalRst);
          case LITERAL_CHARS:
              tempSb = new StringBuilder();
              do {
                  lexer.appendStringContent(tempSb);
              } while (lexer.nextToken() == LITERAL_CHARS);
              return new LiteralString(null, tempSb.toString(), false).setCacheEvalRst(cacheEvalRst);
          case LITERAL_NUM_PURE_DIGIT:
              tempNum = lexer.integerValue();
              lexer.nextToken();
              return new LiteralNumber(tempNum).setCacheEvalRst(cacheEvalRst);
          case LITERAL_NUM_MIX_DIGIT:
              tempNum = lexer.decimalValue();
              lexer.nextToken();
              return new LiteralNumber(tempNum).setCacheEvalRst(cacheEvalRst);
          case QUESTION_MARK:
              int index = lexer.paramIndex();
              lexer.nextToken();
              return createParam(index);
          case KW_CASE:
              lexer.nextToken();
              return caseWhenExpression();
          case KW_INTERVAL:
              lexer.nextToken();
              return intervalExpression();
          case KW_EXISTS:
              lexer.nextToken();
              match(PUNC_LEFT_PAREN);
              tempExpr = subQuery();
              match(PUNC_RIGHT_PAREN);
              return new ExistsPrimary((QueryExpression) tempExpr).setCacheEvalRst(cacheEvalRst);
          case USR_VAR:
              tempStr = lexer.stringValue();
              tempExpr = new UsrDefVarPrimary(tempStr).setCacheEvalRst(cacheEvalRst);
              if (lexer.nextToken() == OP_ASSIGN) {
                  lexer.nextToken();
                  tempExpr2 = expression();
                  return new AssignmentExpression(tempExpr, tempExpr2);
              }
              return tempExpr;
          case SYS_VAR:
              return systemVariale();
          case KW_MATCH:
              lexer.nextToken();
              return matchExpression();
          case PUNC_LEFT_PAREN:
              lexer.nextToken();
              if (lexer.token() == KW_SELECT) {
                  tempExpr = subQuery();
                  match(PUNC_RIGHT_PAREN);
                  return tempExpr;
              }
              tempExpr = expression();
              switch (lexer.token()) {
              case PUNC_RIGHT_PAREN:
                  lexer.nextToken();
                  return tempExpr;
              case PUNC_COMMA:
                  lexer.nextToken();
                  tempExprList = new LinkedList<Expression>();
                  tempExprList.add(tempExpr);
                  tempExprList = expressionList(tempExprList);
                  return new RowExpression(tempExprList).setCacheEvalRst(cacheEvalRst);
              default:
                  throw err("unexpected token: " + lexer.token());
              }
          case KW_UTC_DATE:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new UtcDate(null).setCacheEvalRst(cacheEvalRst);
          case KW_UTC_TIME:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new UtcTime(null).setCacheEvalRst(cacheEvalRst);
          case KW_UTC_TIMESTAMP:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new UtcTimestamp(null).setCacheEvalRst(cacheEvalRst);
          case KW_CURRENT_DATE:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new Curdate().setCacheEvalRst(cacheEvalRst);
          case KW_CURRENT_TIME:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new Curtime().setCacheEvalRst(cacheEvalRst);
          case KW_CURRENT_TIMESTAMP:
          case KW_LOCALTIME:
          case KW_LOCALTIMESTAMP:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new Now().setCacheEvalRst(cacheEvalRst);
          case KW_CURRENT_USER:
              lexer.nextToken();
              if (lexer.token() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  match(PUNC_RIGHT_PAREN);
              }
              return new CurrentUser().setCacheEvalRst(cacheEvalRst);
          case KW_DEFAULT:
              if (lexer.nextToken() == PUNC_LEFT_PAREN) {
                  return ordinaryFunction(lexer.stringValue(), lexer.stringValueUppercase());
              }
              return new DefaultValue().setCacheEvalRst(cacheEvalRst);
          case KW_DATABASE:
          case KW_IF:
          case KW_INSERT:
          case KW_LEFT:
          case KW_REPEAT:
          case KW_REPLACE:
          case KW_RIGHT:
          case KW_SCHEMA:
          case KW_VALUES:
              tempStr = lexer.stringValue();
              tempStrUp = lexer.stringValueUppercase();
              String tempStrUp2 = MySQLToken.keyWordToString(lexer.token());
              if (!tempStrUp2.equals(tempStrUp)) {
                  tempStrUp = tempStr = tempStrUp2;
              }
              if (lexer.nextToken() == PUNC_LEFT_PAREN) {
                  return ordinaryFunction(tempStr, tempStrUp);
              }
              throw err("keyword not followed by '(' is not expression: " + tempStr);
          case KW_MOD:
              lexer.nextToken();
              match(PUNC_LEFT_PAREN);
              tempExpr = expression();
              match(PUNC_COMMA);
              tempExpr2 = expression();
              match(PUNC_RIGHT_PAREN);
              return new ArithmeticModExpression(tempExpr, tempExpr2).setCacheEvalRst(cacheEvalRst);
          case KW_CHAR:
              lexer.nextToken();
              match(PUNC_LEFT_PAREN);
              return functionChar();
          case KW_CONVERT:
              lexer.nextToken();
              match(PUNC_LEFT_PAREN);
              return functionConvert();
          case IDENTIFIER:
              tempStr = lexer.stringValue();
              tempStrUp = lexer.stringValueUppercase();
              lexer.nextToken();
              return startedFromIdentifier(tempStr, tempStrUp);
          case OP_ASTERISK:
              lexer.nextToken();
              return new Wildcard(null).setCacheEvalRst(cacheEvalRst);
          default:
              throw err("unrecognized token as first token of primary: " + lexer.token());
          }
      }
  
      /**
       * first '(' has been consumed
       */
      private Timestampdiff timestampdiff() throws SQLSyntaxErrorException {
          IntervalPrimary.Unit unit = intervalPrimaryUnit();
          match(PUNC_COMMA);
          Expression interval = expression();
          match(PUNC_COMMA);
          Expression expr = expression();
          match(PUNC_RIGHT_PAREN);
          List<Expression> argument = new ArrayList<Expression>(2);
          argument.add(interval);
          argument.add(expr);
          Timestampdiff func = new Timestampdiff(unit, argument);
          func.setCacheEvalRst(cacheEvalRst);
          return func;
      }
  
      /**
       * first '(' has been consumed
       */
      private Timestampadd timestampadd() throws SQLSyntaxErrorException {
          IntervalPrimary.Unit unit = intervalPrimaryUnit();
          match(PUNC_COMMA);
          Expression interval = expression();
          match(PUNC_COMMA);
          Expression expr = expression();
          match(PUNC_RIGHT_PAREN);
          List<Expression> argument = new ArrayList<Expression>(2);
          argument.add(interval);
          argument.add(expr);
          Timestampadd func = new Timestampadd(unit, argument);
          func.setCacheEvalRst(cacheEvalRst);
          return func;
      }
  
      /**
       * first '(' has been consumed
       */
      private Extract extract() throws SQLSyntaxErrorException {
          IntervalPrimary.Unit unit = intervalPrimaryUnit();
          match(KW_FROM);
          Expression date = expression();
          match(PUNC_RIGHT_PAREN);
          Extract extract = new Extract(unit, date);
          extract.setCacheEvalRst(cacheEvalRst);
          return extract;
      }
  
      /**
       * first '(' has been consumed
       */
      private Convert functionConvert() throws SQLSyntaxErrorException {
          Expression expr = expression();
          match(KW_USING);
          String tempStr = lexer.stringValue();
          match(IDENTIFIER);
          match(PUNC_RIGHT_PAREN);
          Convert cvt = new Convert(expr, tempStr);
          cvt.setCacheEvalRst(cacheEvalRst);
          return cvt;
      }
  
      /**
       * first '(' has been consumed
       */
      private Char functionChar() throws SQLSyntaxErrorException {
          Char chr;
          for (List<Expression> tempExprList = new LinkedList<Expression>();;) {
              Expression tempExpr = expression();
              tempExprList.add(tempExpr);
              switch (lexer.token()) {
              case PUNC_COMMA:
                  lexer.nextToken();
                  continue;
              case PUNC_RIGHT_PAREN:
                  lexer.nextToken();
                  chr = new Char(tempExprList, null);
                  chr.setCacheEvalRst(cacheEvalRst);
                  return chr;
              case KW_USING:
                  lexer.nextToken();
                  String tempStr = lexer.stringValue();
                  match(IDENTIFIER);
                  match(PUNC_RIGHT_PAREN);
                  chr = new Char(tempExprList, tempStr);
                  chr.setCacheEvalRst(cacheEvalRst);
                  return chr;
              default:
                  throw err("expect ',' or 'USING' or ')' but is " + lexer.token());
              }
          }
      }
  
      /**
       * last token consumed is {@link MySQLToken#IDENTIFIER}, MUST NOT be
       * <code>null</code>
       */
      private Expression startedFromIdentifier(final String consumed, String consumedUp) throws SQLSyntaxErrorException {
          Expression tempExpr;
          Expression tempExpr2;
          List<Expression> tempExprList;
          String tempStr;
          StringBuilder tempSb;
          boolean tempGroupDistinct;
          switch (lexer.token()) {
          case PUNC_DOT:
              for (tempExpr = new Identifier(null, consumed, consumedUp).setCacheEvalRst(cacheEvalRst); lexer.token() == PUNC_DOT;) {
                  switch (lexer.nextToken()) {
                  case IDENTIFIER:
                      tempExpr = new Identifier((Identifier) tempExpr, lexer.stringValue(), lexer.stringValueUppercase()).setCacheEvalRst(cacheEvalRst);
                      lexer.nextToken();
                      break;
                  case OP_ASTERISK:
                      lexer.nextToken();
                      return new Wildcard((Identifier) tempExpr).setCacheEvalRst(cacheEvalRst);
                  default:
                      throw err("expect IDENTIFIER or '*' after '.', but is " + lexer.token());
                  }
              }
              return tempExpr;
          case LITERAL_BIT:
              if (consumed.charAt(0) != '_') {
                  return new Identifier(null, consumed, consumedUp).setCacheEvalRst(cacheEvalRst);
              }
              tempStr = lexer.stringValue();
              lexer.nextToken();
              return new LiteralBitField(consumed, tempStr).setCacheEvalRst(cacheEvalRst);
          case LITERAL_HEX:
              if (consumed.charAt(0) != '_') {
                  return new Identifier(null, consumed, consumedUp).setCacheEvalRst(cacheEvalRst);
              }
              LiteralHexadecimal hex = new LiteralHexadecimal(
                      consumed,
                      lexer.getSQL(),
                      lexer.getOffsetCache(),
                      lexer.getSizeCache(),
                      charset);
              lexer.nextToken();
              return hex.setCacheEvalRst(cacheEvalRst);
          case LITERAL_CHARS:
              if (consumed.charAt(0) != '_') {
                  return new Identifier(null, consumed, consumedUp).setCacheEvalRst(cacheEvalRst);
              }
              tempSb = new StringBuilder();
              do {
                  lexer.appendStringContent(tempSb);
              } while (lexer.nextToken() == LITERAL_CHARS);
              return new LiteralString(consumed, tempSb.toString(), false).setCacheEvalRst(cacheEvalRst);
          case PUNC_LEFT_PAREN:
              consumedUp = Identifier.unescapeName(consumedUp);
              switch (functionManager.getParsingStrategy(consumedUp)) {
              case GET_FORMAT:
                  // GET_FORMAT({DATE|TIME|DATETIME},
                  // {'EUR'|'USA'|'JIS'|'ISO'|'INTERNAL'})
                  lexer.nextToken();
                  int gfi = matchIdentifier("DATE", "TIME", "DATETIME", "TIMESTAMP");
                  match(PUNC_COMMA);
                  Expression getFormatArg = expression();
                  match(PUNC_RIGHT_PAREN);
                  switch (gfi) {
                  case 0:
                      return new GetFormat(GetFormat.FormatType.DATE, getFormatArg);
                  case 1:
                      return new GetFormat(GetFormat.FormatType.TIME, getFormatArg);
                  case 2:
                  case 3:
                      return new GetFormat(GetFormat.FormatType.DATETIME, getFormatArg);
                  }
                  throw err("unexpected format type for GET_FORMAT()");
              case CAST:
                  lexer.nextToken();
                  tempExpr = expression();
                  match(KW_AS);
                  Pair<String, Pair<Expression, Expression>> type = type4specialFunc();
                  match(PUNC_RIGHT_PAREN);
                  Pair<Expression, Expression> info = type.getValue();
                  if (info != null) {
                      return new Cast(tempExpr, type.getKey(), info.getKey(), info.getValue()).setCacheEvalRst(cacheEvalRst);
                  } else {
                      return new Cast(tempExpr, type.getKey(), null, null).setCacheEvalRst(cacheEvalRst);
                  }
              case POSITION:
                  lexer.nextToken();
                  tempExprList = new ArrayList<Expression>(2);
                  tempExprList.add(expression());
                  match(KW_IN);
                  tempExprList.add(expression());
                  match(PUNC_RIGHT_PAREN);
                  return new Locate(tempExprList).setCacheEvalRst(cacheEvalRst);
              case SUBSTRING:
                  lexer.nextToken();
                  tempExprList = new ArrayList<Expression>(3);
                  tempExprList.add(expression());
                  match(PUNC_COMMA, KW_FROM);
                  tempExprList.add(expression());
                  switch (lexer.token()) {
                  case PUNC_COMMA:
                  case KW_FOR:
                      lexer.nextToken();
                      tempExprList.add(expression());
                  default:
                      match(PUNC_RIGHT_PAREN);
                  }
                  return new Substring(tempExprList).setCacheEvalRst(cacheEvalRst);
              case ROW:
                  lexer.nextToken();
                  tempExprList = expressionList(new LinkedList<Expression>());
                  return new RowExpression(tempExprList).setCacheEvalRst(cacheEvalRst);
              case TRIM:
                  Direction direction;
                  switch (lexer.nextToken()) {
                  case KW_BOTH:
                      lexer.nextToken();
                      direction = Direction.BOTH;
                      break;
                  case KW_LEADING:
                      lexer.nextToken();
                      direction = Direction.LEADING;
                      break;
                  case KW_TRAILING:
                      lexer.nextToken();
                      direction = Direction.TRAILING;
                      break;
                  default:
                      direction = Direction.DEFAULT;
                  }
                  if (direction == Direction.DEFAULT) {
                      tempExpr = expression();
                      if (lexer.token() == KW_FROM) {
                          lexer.nextToken();
                          tempExpr2 = expression();
                          match(PUNC_RIGHT_PAREN);
                          return new Trim(direction, tempExpr, tempExpr2).setCacheEvalRst(cacheEvalRst);
                      }
                      match(PUNC_RIGHT_PAREN);
                      return new Trim(direction, null, tempExpr).setCacheEvalRst(cacheEvalRst);
                  }
                  if (lexer.token() == KW_FROM) {
                      lexer.nextToken();
                      tempExpr = expression();
                      match(PUNC_RIGHT_PAREN);
                      return new Trim(direction, null, tempExpr).setCacheEvalRst(cacheEvalRst);
                  }
                  tempExpr = expression();
                  match(KW_FROM);
                  tempExpr2 = expression();
                  match(PUNC_RIGHT_PAREN);
                  return new Trim(direction, tempExpr, tempExpr2).setCacheEvalRst(cacheEvalRst);
              case AVG:
                  if (lexer.nextToken() == KW_DISTINCT) {
                      tempGroupDistinct = true;
                      lexer.nextToken();
                  } else {
                      tempGroupDistinct = false;
                  }
                  tempExpr = expression();
                  match(PUNC_RIGHT_PAREN);
                  return new Avg(tempExpr, tempGroupDistinct).setCacheEvalRst(cacheEvalRst);
              case MAX:
                  if (lexer.nextToken() == KW_DISTINCT) {
                      tempGroupDistinct = true;
                      lexer.nextToken();
                  } else {
                      tempGroupDistinct = false;
                  }
                  tempExpr = expression();
                  match(PUNC_RIGHT_PAREN);
                  return new Max(tempExpr, tempGroupDistinct).setCacheEvalRst(cacheEvalRst);
              case MIN:
                  if (lexer.nextToken() == KW_DISTINCT) {
                      tempGroupDistinct = true;
                      lexer.nextToken();
                  } else {
                      tempGroupDistinct = false;
                  }
                  tempExpr = expression();
                  match(PUNC_RIGHT_PAREN);
                  return new Min(tempExpr, tempGroupDistinct).setCacheEvalRst(cacheEvalRst);
              case SUM:
                  if (lexer.nextToken() == KW_DISTINCT) {
                      tempGroupDistinct = true;
                      lexer.nextToken();
                  } else {
                      tempGroupDistinct = false;
                  }
                  tempExpr = expression();
                  match(PUNC_RIGHT_PAREN);
                  return new Sum(tempExpr, tempGroupDistinct).setCacheEvalRst(cacheEvalRst);
              case COUNT:
                  if (lexer.nextToken() == KW_DISTINCT) {
                      lexer.nextToken();
                      tempExprList = expressionList(new LinkedList<Expression>());
                      return new Count(tempExprList).setCacheEvalRst(cacheEvalRst);
                  }
                  tempExpr = expression();
                  match(PUNC_RIGHT_PAREN);
                  return new Count(tempExpr).setCacheEvalRst(cacheEvalRst);
              case GROUP_CONCAT:
                  if (lexer.nextToken() == KW_DISTINCT) {
                      lexer.nextToken();
                      tempGroupDistinct = true;
                  } else {
                      tempGroupDistinct = false;
                  }
                  for (tempExprList = new LinkedList<Expression>();;) {
                      tempExpr = expression();
                      tempExprList.add(tempExpr);
                      if (lexer.token() == PUNC_COMMA) {
                          lexer.nextToken();
                      } else {
                          break;
                      }
                  }
                  boolean isDesc = false;
                  List<Expression> appendedColumnNames = null;
                  tempExpr = null; // order by
                  tempStr = null; // literalChars
                  switch (lexer.token()) {
                  case KW_ORDER:
                      lexer.nextToken();
                      match(KW_BY);
                      tempExpr = expression();
                      if (lexer.token() == KW_ASC) {
                          lexer.nextToken();
                      } else if (lexer.token() == KW_DESC) {
                          isDesc = true;
                          lexer.nextToken();
                      }
                      for (appendedColumnNames = new LinkedList<Expression>(); lexer.token() == PUNC_COMMA;) {
                          lexer.nextToken();
                          appendedColumnNames.add(expression());
                      }
                      if (lexer.token() != KW_SEPARATOR) {
                          break;
                      }
                  case KW_SEPARATOR:
                      lexer.nextToken();
                      tempSb = new StringBuilder();
                      lexer.appendStringContent(tempSb);
                      tempStr = LiteralString.getUnescapedString(tempSb.toString());
                      match(LITERAL_CHARS);
                      break;
                  }
                  match(PUNC_RIGHT_PAREN);
                  return new GroupConcat(tempGroupDistinct, tempExprList, tempExpr, isDesc, appendedColumnNames, tempStr).setCacheEvalRst(cacheEvalRst);
              case CHAR:
                  lexer.nextToken();
                  return functionChar();
              case CONVERT:
                  lexer.nextToken();
                  return functionConvert();
              case EXTRACT:
                  lexer.nextToken();
                  return extract();
              case TIMESTAMPDIFF:
                  lexer.nextToken();
                  return timestampdiff();
              case TIMESTAMPADD:
                  lexer.nextToken();
                  return timestampadd();
              case _ORDINARY:
                  return ordinaryFunction(consumed, consumedUp);
              case _DEFAULT:
                  return new Identifier(null, consumed, consumedUp).setCacheEvalRst(cacheEvalRst);
              default:
                  throw err("unexpected function parsing strategy for id of " + consumed);
              }
          default:
              return new Identifier(null, consumed, consumedUp).setCacheEvalRst(cacheEvalRst);
          }
      }
  
      /**
       * @return never null
       */
      private Pair<String, Pair<Expression, Expression>> type4specialFunc() throws SQLSyntaxErrorException {
          Expression exp1 = null;
          Expression exp2 = null;
          // DATE
          // DATETIME
          // SIGNED [INTEGER]
          // TIME
          String typeName;
          switch (lexer.token()) {
          case KW_BINARY:
          case KW_CHAR:
              typeName = MySQLToken.keyWordToString(lexer.token());
              if (lexer.nextToken() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  exp1 = expression();
                  match(PUNC_RIGHT_PAREN);
              }
              return constructTypePair(typeName, exp1, exp2);
          case KW_DECIMAL:
              typeName = MySQLToken.keyWordToString(lexer.token());
              if (lexer.nextToken() == PUNC_LEFT_PAREN) {
                  lexer.nextToken();
                  exp1 = expression();
                  if (lexer.token() == PUNC_COMMA) {
                      lexer.nextToken();
                      exp2 = expression();
                  }
                  match(PUNC_RIGHT_PAREN);
              }
              return constructTypePair(typeName, exp1, exp2);
          case KW_UNSIGNED:
              typeName = MySQLToken.keyWordToString(lexer.token());
              if (lexer.nextToken() == KW_INTEGER) {
                  lexer.nextToken();
              }
              return constructTypePair(typeName, null, null);
          case IDENTIFIER:
              typeName = lexer.stringValueUppercase();
              lexer.nextToken();
              if ("SIGNED".equals(typeName)) {
                  if (lexer.token() == KW_INTEGER) {
                      lexer.nextToken();
                  }
              } else if (!"DATE".equals(typeName) && !"DATETIME".equals(typeName) && !"TIME".equals(typeName)) {
                  throw err("invalide type name: " + typeName);
              }
              return constructTypePair(typeName, null, null);
          default:
              throw err("invalide type name: " + lexer.stringValueUppercase());
          }
      }
  
      private static Pair<String, Pair<Expression, Expression>> constructTypePair(String typeName, Expression exp1,
                                                                                  Expression exp2) {
          return new Pair<String, Pair<Expression, Expression>>(typeName, new Pair<Expression, Expression>(exp1, exp2));
      }
  
      /**
       * id has been consumed. id must be a function name. current token must be
       * {@link MySQLToken#PUNC_LEFT_PAREN}
       * 
       * @param idUpper must be name of a function
       * @return never null
       */
      private FunctionExpression ordinaryFunction(String id, String idUpper) throws SQLSyntaxErrorException {
          idUpper = Identifier.unescapeName(idUpper);
          match(PUNC_LEFT_PAREN);
          FunctionExpression funcExpr;
          if (lexer.token() == PUNC_RIGHT_PAREN) {
              lexer.nextToken();
              funcExpr = functionManager.createFunctionExpression(idUpper, null);
          } else {
              List<Expression> args = expressionList(new LinkedList<Expression>());
              funcExpr = functionManager.createFunctionExpression(idUpper, args);
          }
          if (funcExpr == null) {
              throw new SQLSyntaxErrorException(id + "() is not a function");
          }
          funcExpr.setCacheEvalRst(cacheEvalRst);
          return funcExpr;
      }
  
      /**
       * first <code>MATCH</code> has been consumed
       */
      private Expression matchExpression() throws SQLSyntaxErrorException {
          match(PUNC_LEFT_PAREN);
          List<Expression> colList = expressionList(new LinkedList<Expression>());
          matchIdentifier("AGAINST");
          match(PUNC_LEFT_PAREN);
          Expression pattern = expression();
          Modifier modifier = Modifier._DEFAULT;
          switch (lexer.token()) {
          case KW_WITH:
              lexer.nextToken();
              match(IDENTIFIER);
              match(IDENTIFIER);
              modifier = Modifier.WITH_QUERY_EXPANSION;
              break;
          case KW_IN:
              switch (lexer.nextToken()) {
              case KW_NATURAL:
                  lexer.nextToken();
                  matchIdentifier("LANGUAGE");
                  matchIdentifier("MODE");
                  if (lexer.token() == KW_WITH) {
                      lexer.nextToken();
                      lexer.nextToken();
                      lexer.nextToken();
                      modifier = Modifier.IN_NATURAL_LANGUAGE_MODE_WITH_QUERY_EXPANSION;
                  } else {
                      modifier = Modifier.IN_NATURAL_LANGUAGE_MODE;
                  }
                  break;
              default:
                  matchIdentifier("BOOLEAN");
                  matchIdentifier("MODE");
                  modifier = Modifier.IN_BOOLEAN_MODE;
                  break;
              }
          }
          match(PUNC_RIGHT_PAREN);
          return new MatchExpression(colList, pattern, modifier).setCacheEvalRst(cacheEvalRst);
      }
  
      /**
       * first <code>INTERVAL</code> has been consumed
       */
      private Expression intervalExpression() throws SQLSyntaxErrorException {
          Expression fstExpr;
          List<Expression> argList = null;
          if (lexer.token() == PUNC_LEFT_PAREN) {
              if (lexer.nextToken() == KW_SELECT) {
                  fstExpr = subQuery();
                  match(PUNC_RIGHT_PAREN);
              } else {
                  fstExpr = expression();
                  if (lexer.token() == PUNC_COMMA) {
                      lexer.nextToken();
                      argList = new LinkedList<Expression>();
                      argList.add(fstExpr);
                      argList = expressionList(argList);
                  } else {
                      match(PUNC_RIGHT_PAREN);
                  }
              }
          } else {
              fstExpr = expression();
          }
          if (argList != null) {
              return new Interval(argList).setCacheEvalRst(cacheEvalRst);
          }
  
          return new IntervalPrimary(fstExpr, intervalPrimaryUnit()).setCacheEvalRst(cacheEvalRst);
      }
  
      private IntervalPrimary.Unit intervalPrimaryUnit() throws SQLSyntaxErrorException {
          switch (lexer.token()) {
          case KW_SECOND_MICROSECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.SECOND_MICROSECOND;
          case KW_MINUTE_MICROSECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.MINUTE_MICROSECOND;
          case KW_MINUTE_SECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.MINUTE_SECOND;
          case KW_HOUR_MICROSECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.HOUR_MICROSECOND;
          case KW_HOUR_SECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.HOUR_SECOND;
          case KW_HOUR_MINUTE:
              lexer.nextToken();
              return IntervalPrimary.Unit.HOUR_MINUTE;
          case KW_DAY_MICROSECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.DAY_MICROSECOND;
          case KW_DAY_SECOND:
              lexer.nextToken();
              return IntervalPrimary.Unit.DAY_SECOND;
          case KW_DAY_MINUTE:
              lexer.nextToken();
              return IntervalPrimary.Unit.DAY_MINUTE;
          case KW_DAY_HOUR:
              lexer.nextToken();
              return IntervalPrimary.Unit.DAY_HOUR;
          case KW_YEAR_MONTH:
              lexer.nextToken();
              return IntervalPrimary.Unit.YEAR_MONTH;
          case IDENTIFIER:
              String unitText = lexer.stringValueUppercase();
              IntervalPrimary.Unit unit = IntervalPrimary.getIntervalUnit(unitText);
              if (unit != null) {
                  lexer.nextToken();
                  return unit;
              }
          default:
              throw err("literal INTERVAL should end with an UNIT");
          }
      }
  
      /**
       * first <code>CASE</code> has been consumed
       */
      private Expression caseWhenExpression() throws SQLSyntaxErrorException {
          Expression comparee = null;
          if (lexer.token() != KW_WHEN) {
              comparee = expression();
          }
          List<Pair<Expression, Expression>> list = new LinkedList<Pair<Expression, Expression>>();
          for (; lexer.token() == KW_WHEN;) {
              lexer.nextToken();
              Expression when = expression();
              match(KW_THEN);
              Expression then = expression();
              if (when == null || then == null)
                  throw err("when or then is null in CASE WHEN expression");
              list.add(new Pair<Expression, Expression>(when, then));
          }
          if (list.isEmpty()) {
              throw err("at least one WHEN ... THEN branch for CASE ... WHEN syntax");
          }
          Expression elseValue = null;
          switch (lexer.token()) {
          case KW_ELSE:
              lexer.nextToken();
              elseValue = expression();
          default:
              matchIdentifier("END");
          }
          return new CaseWhenOperatorExpression(comparee, list, elseValue).setCacheEvalRst(cacheEvalRst);
      }
  
      /**
       * first <code>'('</code> has been consumed. At least one element. Consume
       * last ')' after invocation <br/>
       * <code>'(' expr (',' expr)* ')'</code>
       */
      private List<Expression> expressionList(List<Expression> exprList) throws SQLSyntaxErrorException {
          for (;;) {
              Expression expr = expression();
              exprList.add(expr);
              switch (lexer.token()) {
              case PUNC_COMMA:
                  lexer.nextToken();
                  break;
              case PUNC_RIGHT_PAREN:
                  lexer.nextToken();
                  return exprList;
              default:
                  throw err("unexpected token: " + lexer.token());
              }
          }
      }
  
      /**
       * first token is {@link MySQLToken#KW_SELECT}
       */
      private QueryExpression subQuery() throws SQLSyntaxErrorException {
          if (selectParser == null) {
              selectParser = new MySQLDMLSelectParser(lexer, this);
          }
          return selectParser.select();
      }
  
  }
  