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