Skip to content
This repository has been archived by the owner on Feb 29, 2020. It is now read-only.

Commit

Permalink
[client][managed][offline] Allow query on memer name datetime
Browse files Browse the repository at this point in the history
  • Loading branch information
hasankhan committed Nov 5, 2014
1 parent 4129fd9 commit 7d831cd
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
// ----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.WindowsAzure.MobileServices.Query
{
Expand All @@ -16,9 +11,9 @@ internal sealed class ODataExpressionLexer
private string text;
private int textLen;
private int textPos;
private char ch;
public char CurrentChar { get; private set; }

public QueryToken Token {get; private set; }
public QueryToken Token { get; private set; }

public ODataExpressionLexer(string expression)
{
Expand All @@ -31,14 +26,14 @@ public ODataExpressionLexer(string expression)

public QueryToken NextToken()
{
while (Char.IsWhiteSpace(this.ch))
while (Char.IsWhiteSpace(this.CurrentChar))
{
this.NextChar();
}

QueryTokenKind t = QueryTokenKind.Unknown;
int tokenPos = this.textPos;
switch (this.ch)
switch (this.CurrentChar)
{
case '(':
this.NextChar();
Expand All @@ -61,7 +56,7 @@ public QueryToken NextToken()
t = QueryTokenKind.Dot;
break;
case '\'':
char quote = this.ch;
char quote = this.CurrentChar;
do
{
this.AdvanceToNextOccuranceOf(quote);
Expand All @@ -71,29 +66,29 @@ public QueryToken NextToken()
}
this.NextChar();
}
while (this.ch == quote);
while (this.CurrentChar == quote);
t = QueryTokenKind.StringLiteral;
break;
default:
if (this.IsIdentifierStart(this.ch) || this.ch == '@' || this.ch == '_')
if (this.IsIdentifierStart(this.CurrentChar) || this.CurrentChar == '@' || this.CurrentChar == '_')
{
do
{
this.NextChar();
}
while (this.IsIdentifierPart(this.ch) || this.ch == '_');
while (this.IsIdentifierPart(this.CurrentChar) || this.CurrentChar == '_');
t = QueryTokenKind.Identifier;
break;
}
if (Char.IsDigit(this.ch))
if (Char.IsDigit(this.CurrentChar))
{
t = QueryTokenKind.IntegerLiteral;
do
{
this.NextChar();
}
while (Char.IsDigit(this.ch));
if (this.ch == '.')
while (Char.IsDigit(this.CurrentChar));
if (this.CurrentChar == '.')
{
t = QueryTokenKind.RealLiteral;
this.NextChar();
Expand All @@ -102,13 +97,13 @@ public QueryToken NextToken()
{
this.NextChar();
}
while (Char.IsDigit(this.ch));
while (Char.IsDigit(this.CurrentChar));
}
if (this.ch == 'E' || this.ch == 'e')
if (this.CurrentChar == 'E' || this.CurrentChar == 'e')
{
t = QueryTokenKind.RealLiteral;
this.NextChar();
if (this.ch == '+' || this.ch == '-')
if (this.CurrentChar == '+' || this.CurrentChar == '-')
{
this.NextChar();
}
Expand All @@ -117,9 +112,9 @@ public QueryToken NextToken()
{
this.NextChar();
}
while (Char.IsDigit(this.ch));
while (Char.IsDigit(this.CurrentChar));
}
if (this.ch == 'F' || this.ch == 'f' || this.ch == 'M' || this.ch == 'm' || this.ch == 'D' || this.ch == 'd')
if (this.CurrentChar == 'F' || this.CurrentChar == 'f' || this.CurrentChar == 'M' || this.CurrentChar == 'm' || this.CurrentChar == 'D' || this.CurrentChar == 'd')
{
t = QueryTokenKind.RealLiteral;
this.NextChar();
Expand All @@ -145,64 +140,81 @@ public QueryToken NextToken()

private void ValidateDigit()
{
if (!Char.IsDigit(this.ch)) {
if (!Char.IsDigit(this.CurrentChar))
{
this.ParseError(Resources.ODataExpressionParser_DigitExpected, this.textPos);
}
}

private void ReClassifyToken() {
if (Token.Kind == QueryTokenKind.Identifier) {
if (this.Token.Text == "or") {
private void ReClassifyToken()
{
if (Token.Kind == QueryTokenKind.Identifier)
{
if (this.Token.Text == "or")
{
this.Token.Kind = QueryTokenKind.Or;
}
else if (this.Token.Text == "add") {
else if (this.Token.Text == "add")
{
this.Token.Kind = QueryTokenKind.Add;
}
else if (this.Token.Text == "and") {
else if (this.Token.Text == "and")
{
this.Token.Kind = QueryTokenKind.And;
}
else if (this.Token.Text == "div") {
else if (this.Token.Text == "div")
{
this.Token.Kind = QueryTokenKind.Divide;
}
else if (this.Token.Text == "sub") {
else if (this.Token.Text == "sub")
{
this.Token.Kind = QueryTokenKind.Sub;
}
else if (this.Token.Text == "mul") {
else if (this.Token.Text == "mul")
{
this.Token.Kind = QueryTokenKind.Multiply;
}
else if (this.Token.Text == "mod") {
else if (this.Token.Text == "mod")
{
this.Token.Kind = QueryTokenKind.Modulo;
}
else if (this.Token.Text == "ne") {
else if (this.Token.Text == "ne")
{
this.Token.Kind = QueryTokenKind.NotEqual;
}
else if (this.Token.Text == "not") {
else if (this.Token.Text == "not")
{
this.Token.Kind = QueryTokenKind.Not;
}
else if (this.Token.Text == "le") {
else if (this.Token.Text == "le")
{
this.Token.Kind = QueryTokenKind.LessThanEqual;
}
else if (this.Token.Text == "lt") {
else if (this.Token.Text == "lt")
{
this.Token.Kind = QueryTokenKind.LessThan;
}
else if (this.Token.Text == "eq") {
else if (this.Token.Text == "eq")
{
this.Token.Kind = QueryTokenKind.Equal;
}
else if (this.Token.Text == "ge") {
else if (this.Token.Text == "ge")
{
this.Token.Kind = QueryTokenKind.GreaterThanEqual;
}
else if (this.Token.Text == "gt") {
else if (this.Token.Text == "gt")
{
this.Token.Kind = QueryTokenKind.GreaterThan;
}
}
}

private bool IsIdentifierStart (char ch)
private bool IsIdentifierStart(char ch)
{
return Char.IsLetter(ch);
}

private bool IsIdentifierPart (char ch)
private bool IsIdentifierPart(char ch)
{
bool result = this.IsIdentifierStart(ch) || Char.IsDigit(ch) || (ch == '_' || ch == '-');
return result;
Expand All @@ -211,7 +223,7 @@ private bool IsIdentifierPart (char ch)
private void AdvanceToNextOccuranceOf(char endingValue)
{
this.NextChar();
while (this.textPos < this.textLen && this.ch != endingValue)
while (this.textPos < this.textLen && this.CurrentChar != endingValue)
{
this.NextChar();
}
Expand All @@ -223,14 +235,14 @@ private void NextChar()
{
this.textPos++;
}
this.ch = (this.textPos < this.textLen) ? this.text[this.textPos] : '\0';
this.CurrentChar = (this.textPos < this.textLen) ? this.text[this.textPos] : '\0';
}

private void SetTextPos(int pos)
{
this.textPos = pos;
this.ch = (this.textPos < this.textLen) ? this.text[this.textPos] : '\0';
}
this.CurrentChar = (this.textPos < this.textLen) ? this.text[this.textPos] : '\0';
}

private void ParseError(string message, int errorPos)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -454,12 +454,18 @@ private QueryNode ParseIdentifier()
QueryNode value;
if (keywords.TryGetValue(this.lexer.Token.Text, out value))
{
if (value == null)
// type construction has the format of type'value' e.g. datetime'2001-04-01T00:00:00Z'
// therefore if the next character is a single quote then we try to
// interpret this as type construction else its a normal member access
if (value == null && this.lexer.CurrentChar == '\'')
{
return this.ParseTypeConstruction();
}
this.lexer.NextToken();
return value;
else if (value != null) // this is a constant
{
this.lexer.NextToken();
return value;
}
}

return this.ParseMemberAccess(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,69 @@ public void ParseFilter_Guid()

Assert.IsNotNull(queryNode);

BinaryOperatorNode comparisonNode = queryNode as BinaryOperatorNode;
var comparisonNode = queryNode as BinaryOperatorNode;
Assert.IsNotNull(comparisonNode);

MemberAccessNode left = comparisonNode.LeftOperand as MemberAccessNode;
var left = comparisonNode.LeftOperand as MemberAccessNode;
Assert.IsNotNull(left);

ConstantNode right = comparisonNode.RightOperand as ConstantNode;
var right = comparisonNode.RightOperand as ConstantNode;
Assert.IsNotNull(right);

Assert.AreEqual("Field", left.MemberName);
Assert.AreEqual(BinaryOperatorKind.Equal, comparisonNode.OperatorKind);
Assert.AreEqual(filterGuid, right.Value);
}

[TestMethod]
public void ParseFilter_TrueToken()
{
QueryNode queryNode = ODataExpressionParser.ParseFilter("(true eq null) and false");

Assert.IsNotNull(queryNode);

var comparisonNode = queryNode as BinaryOperatorNode;
Assert.IsNotNull(comparisonNode);

var left = comparisonNode.LeftOperand as BinaryOperatorNode;
Assert.IsNotNull(left);

var trueNode = left.LeftOperand as ConstantNode;
Assert.IsNotNull(trueNode);
Assert.AreEqual(true, trueNode.Value);

var nullNode = left.RightOperand as ConstantNode;
Assert.IsNotNull(nullNode);
Assert.AreEqual(null, nullNode.Value);

var falseNode = comparisonNode.RightOperand as ConstantNode;
Assert.IsNotNull(falseNode);

Assert.AreEqual(BinaryOperatorKind.And, comparisonNode.OperatorKind);
Assert.AreEqual(false, falseNode.Value);
}

[TestMethod]
public void ParseFilter_DateTimeMember()
{
QueryNode queryNode = ODataExpressionParser.ParseFilter("datetime eq 1");

Assert.IsNotNull(queryNode);

var comparisonNode = queryNode as BinaryOperatorNode;
Assert.IsNotNull(comparisonNode);

var left = comparisonNode.LeftOperand as MemberAccessNode;
Assert.IsNotNull(left);

var right = comparisonNode.RightOperand as ConstantNode;
Assert.IsNotNull(right);

Assert.AreEqual("datetime", left.MemberName);
Assert.AreEqual(BinaryOperatorKind.Equal, comparisonNode.OperatorKind);
Assert.AreEqual(1L, right.Value);
}

[TestMethod]
public void ParseFilter_Guid_InvalidGuidString()
{
Expand Down

0 comments on commit 7d831cd

Please sign in to comment.