in MySQL.Data/src/X/Protocol/X/ExprParser.cs [837:1027]
Expr AtomicExpr()
{ // constant, identifier, variable, function call, etc
if (this.tokenPos >= this.tokens.Count)
{
throw new ArgumentException("No more tokens when expecting one at token pos " + this.tokenPos);
}
Token t = this.tokens[this.tokenPos];
this.tokenPos++; // consume
switch (t.type)
{
case TokenType.EROTEME:
case TokenType.COLON:
{
string placeholderName;
if (CurrentTokenTypeEquals(TokenType.LNUM_INT))
{
// int pos = Integer.valueOf(consumeToken(TokenType.LNUM_INT));
// return Expr.CreateBuilder().setType(Expr.Type.PLACEHOLDER).setPosition(pos).Build();
placeholderName = ConsumeToken(TokenType.LNUM_INT);
}
else if (CurrentTokenTypeEquals(TokenType.IDENT))
{
placeholderName = ConsumeToken(TokenType.IDENT);
}
else if (t.type == TokenType.EROTEME)
{
placeholderName = this.positionalPlaceholderCount.ToString();
}
else
{
throw new ArgumentException("Invalid placeholder name at token pos " + this.tokenPos);
}
placeholderName = placeholderName.ToLowerInvariant();
Expr placeholder = new Expr();
placeholder.Type = Expr.Types.Type.Placeholder;
if (this.placeholderNameToPosition.ContainsKey(placeholderName))
{
placeholder.Position = (uint)this.placeholderNameToPosition[placeholderName];
}
else
{
placeholder.Position = (uint)this.positionalPlaceholderCount;
this.placeholderNameToPosition.Add(placeholderName, this.positionalPlaceholderCount);
this.positionalPlaceholderCount++;
}
return placeholder;
}
case TokenType.LPAREN:
Expr e = GetExpr();
ConsumeToken(TokenType.RPAREN);
return e;
case TokenType.LSQBRACKET:
{
Mysqlx.Expr.Array builder = new Mysqlx.Expr.Array();
ParseCommaSeparatedList(() =>
{
return GetExpr();
}).ForEach(f => builder.Value.Add(f));
ConsumeToken(TokenType.RSQBRACKET);
return new Expr() { Type = Expr.Types.Type.Array, Array = builder };
}
case TokenType.LCURLY: // JSON object
{
Mysqlx.Expr.Object builder = new Mysqlx.Expr.Object();
if (CurrentTokenTypeEquals(TokenType.LSTRING))
{
ParseCommaSeparatedList(() =>
{
string key = ConsumeToken(TokenType.LSTRING);
ConsumeToken(TokenType.COLON);
Expr value = GetExpr();
Mysqlx.Expr.Object.Types.ObjectField objectField = new Mysqlx.Expr.Object.Types.ObjectField();
objectField.Key = key;
objectField.Value = value;
return objectField;
}).ForEach(f => builder.Fld.Add(f));
}
ConsumeToken(TokenType.RCURLY);
return new Expr() { Type = Expr.Types.Type.Object, Object = builder };
}
case TokenType.CAST:
{
ConsumeToken(TokenType.LPAREN);
Operator builder = new Operator();
builder.Name = TokenType.CAST.ToString().ToLowerInvariant();
builder.Param.Add(GetExpr());
ConsumeToken(TokenType.AS);
StringBuilder typeStr = new StringBuilder(this.tokens[this.tokenPos].value.ToUpperInvariant());
// ensure next token is a valid type argument to CAST
if (CurrentTokenTypeEquals(TokenType.DECIMAL))
{
this.tokenPos++;
if (CurrentTokenTypeEquals(TokenType.LPAREN))
{
typeStr.Append(ConsumeToken(TokenType.LPAREN));
typeStr.Append(ConsumeToken(TokenType.LNUM_INT));
if (CurrentTokenTypeEquals(TokenType.COMMA))
{
typeStr.Append(ConsumeToken(TokenType.COMMA));
typeStr.Append(ConsumeToken(TokenType.LNUM_INT));
}
typeStr.Append(ConsumeToken(TokenType.RPAREN));
}
}
else if (CurrentTokenTypeEquals(TokenType.CHAR) || CurrentTokenTypeEquals(TokenType.BINARY))
{
this.tokenPos++;
if (CurrentTokenTypeEquals(TokenType.LPAREN))
{
typeStr.Append(ConsumeToken(TokenType.LPAREN));
typeStr.Append(ConsumeToken(TokenType.LNUM_INT));
typeStr.Append(ConsumeToken(TokenType.RPAREN));
}
}
else if (CurrentTokenTypeEquals(TokenType.UNSIGNED) || CurrentTokenTypeEquals(TokenType.SIGNED))
{
this.tokenPos++;
if (CurrentTokenTypeEquals(TokenType.INTEGER))
{
// don't add optional INTEGER to type string argument
ConsumeToken(TokenType.INTEGER);
}
}
else if (CurrentTokenTypeEquals(TokenType.JSON) || CurrentTokenTypeEquals(TokenType.DATE) || CurrentTokenTypeEquals(TokenType.DATETIME) ||
CurrentTokenTypeEquals(TokenType.TIME))
{
this.tokenPos++;
}
else
{
throw new ArgumentException("Expected valid CAST type argument at " + this.tokenPos);
}
ConsumeToken(TokenType.RPAREN);
// TODO charset?
builder.Param.Add(ExprUtil.BuildLiteralScalar(Encoding.UTF8.GetBytes(typeStr.ToString())));
return new Expr() { Type = Expr.Types.Type.Operator, Operator = builder };
}
case TokenType.PLUS:
case TokenType.MINUS:
{
if (CurrentTokenTypeEquals(TokenType.LNUM_INT) || CurrentTokenTypeEquals(TokenType.LNUM_DOUBLE))
{
// unary operators are handled inline making positive or negative numeric literals
this.tokens[this.tokenPos].value = t.value + this.tokens[this.tokenPos].value;
return AtomicExpr();
}
}
return BuildUnaryOp(t.value, AtomicExpr());
case TokenType.NOT:
case TokenType.NEG:
case TokenType.BANG:
return BuildUnaryOp(t.value, AtomicExpr());
case TokenType.LSTRING:
return ExprUtil.BuildLiteralScalar(t.value);
case TokenType.NULL:
return ExprUtil.BuildLiteralNullScalar();
case TokenType.LNUM_INT:
return ExprUtil.BuildLiteralScalar(long.Parse(t.value));
case TokenType.LNUM_DOUBLE:
return ExprUtil.BuildLiteralScalar(double.Parse(t.value));
case TokenType.TRUE:
case TokenType.FALSE:
return ExprUtil.BuildLiteralScalar(t.type == TokenType.TRUE);
case TokenType.AT:
return DocumentField();
case TokenType.STAR:
// special "0-ary" consideration of "*" as an operator (for COUNT(*), etc)
return StarOperator();
case TokenType.IDENT:
this.tokenPos--; // stay on the identifier
// check for function call which may be: func(...) or schema.func(...)
if (NextTokenTypeEquals(TokenType.LPAREN)
|| (PosTokenTypeEquals(this.tokenPos + 1, TokenType.DOT) && PosTokenTypeEquals(this.tokenPos + 2, TokenType.IDENT) && PosTokenTypeEquals(
this.tokenPos + 3, TokenType.LPAREN)))
{
return ParseFunctionCall();
}
else
{
if (this.allowRelationalColumns)
{
return ParseColumnIdentifier();
}
else
{
return DocumentField();
}
}
}
throw new ArgumentException("Cannot find atomic expression at token pos: " + (this.tokenPos - 1));
}