diff --git a/src/Microsoft.OData.Core/UriParser/ExpressionLexer.cs b/src/Microsoft.OData.Core/UriParser/ExpressionLexer.cs
index 3f3a9f7067..738e855733 100644
--- a/src/Microsoft.OData.Core/UriParser/ExpressionLexer.cs
+++ b/src/Microsoft.OData.Core/UriParser/ExpressionLexer.cs
@@ -83,6 +83,11 @@ internal class ExpressionLexer
/// Whether the lexer is being used to parse function parameters. If true, will allow/recognize parameter aliases and typed nulls.
private readonly bool parsingFunctionParameters;
+ ///
+ /// Whether or not to interpret the semantics of a token that appears to be an identifier (i.e. consider if it is a keyword instead of an identifier)
+ ///
+ private readonly bool applySemanticsToIdentifiers;
+
/// Lexer ignores whitespace
private bool ignoreWhitespace;
@@ -108,6 +113,19 @@ internal ExpressionLexer(string expression, bool moveToFirstToken, bool useSemic
/// If true, the lexer will tokenize based on semicolons as well.
/// Whether the lexer is being used to parse function parameters. If true, will allow/recognize parameter aliases and typed nulls.
internal ExpressionLexer(string expression, bool moveToFirstToken, bool useSemicolonDelimiter, bool parsingFunctionParameters)
+ : this(expression, moveToFirstToken, useSemicolonDelimiter, parsingFunctionParameters, true)
+ {
+ }
+
+ /// Initializes a new .
+ /// Expression to parse.
+ /// If true, this constructor will call NextToken() to move to the first token.
+ /// If true, the lexer will tokenize based on semicolons as well.
+ /// Whether the lexer is being used to parse function parameters. If true, will allow/recognize parameter aliases and typed nulls.
+ ///
+ /// Whether or not to interpret the semantics of a token that appears to be an identifier (i.e. consider if it is a keyword instead of an identifier)
+ ///
+ internal ExpressionLexer(string expression, bool moveToFirstToken, bool useSemicolonDelimiter, bool parsingFunctionParameters, bool applySemanticsToIdentifiers)
{
Debug.Assert(expression != null, "expression != null");
@@ -116,6 +134,7 @@ internal ExpressionLexer(string expression, bool moveToFirstToken, bool useSemic
this.TextLen = this.Text.Length;
this.useSemicolonDelimiter = useSemicolonDelimiter;
this.parsingFunctionParameters = parsingFunctionParameters;
+ this.applySemanticsToIdentifiers = applySemanticsToIdentifiers;
this.parsingDoubleQuotedString = false;
this.SetTextPos(0);
@@ -810,7 +829,7 @@ private void HandleTypePrefixedLiterals()
this.HandleQuotedValues();
}
- else
+ else if (this.applySemanticsToIdentifiers)
{
// Handle keywords.
// Get built in type literal prefix.
diff --git a/src/Microsoft.OData.Core/UriParser/Parsers/SelectExpandParser.cs b/src/Microsoft.OData.Core/UriParser/Parsers/SelectExpandParser.cs
index 976b734484..5f755a2437 100644
--- a/src/Microsoft.OData.Core/UriParser/Parsers/SelectExpandParser.cs
+++ b/src/Microsoft.OData.Core/UriParser/Parsers/SelectExpandParser.cs
@@ -81,7 +81,7 @@ public SelectExpandParser(
// Sets up our lexer. We don't turn useSemicolonDelimiter on since the parsing code for expand options,
// which is the only thing that needs it, is in a different class that uses it's own lexer.
- this.lexer = clauseToParse != null ? new ExpressionLexer(clauseToParse, false /*moveToFirstToken*/, false /*useSemicolonDelimiter*/) : null;
+ this.lexer = clauseToParse != null ? new ExpressionLexer(clauseToParse, false, false, false, false) : null;
this.enableCaseInsensitiveBuiltinIdentifier = enableCaseInsensitiveBuiltinIdentifier;
diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/UriParser/ODataUriParserTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/UriParser/ODataUriParserTests.cs
index e0c9f122cf..0f5772c8cc 100644
--- a/test/FunctionalTests/Microsoft.OData.Core.Tests/UriParser/ODataUriParserTests.cs
+++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/UriParser/ODataUriParserTests.cs
@@ -26,6 +26,410 @@ public class ODataUriParserTests
private readonly Uri ServiceRoot = new Uri("http://host");
private readonly Uri FullUri = new Uri("http://host/People");
+ ///
+ /// Selects a property named "true"
+ ///
+ [Fact]
+ public void SelectTrue()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ foo.AddStructuralProperty("true", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$select=true", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var pathSelectItem = selectedItems[0] as PathSelectItem;
+ var segments = pathSelectItem.SelectedPath.Segments.ToArray();
+ Assert.Single(segments);
+ Assert.Equal("true", segments[0].Identifier);
+ Assert.Equal("Edm.String", segments[0].EdmType.FullTypeName());
+ }
+
+ ///
+ /// Selects a property named "false"
+ ///
+ [Fact]
+ public void SelectFalse()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ foo.AddStructuralProperty("false", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$select=false", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var pathSelectItem = selectedItems[0] as PathSelectItem;
+ var segments = pathSelectItem.SelectedPath.Segments.ToArray();
+ Assert.Single(segments);
+ Assert.Equal("false", segments[0].Identifier);
+ Assert.Equal("Edm.String", segments[0].EdmType.FullTypeName());
+ }
+
+ ///
+ /// Selects a property named "INF"
+ ///
+ [Fact]
+ public void SelectInfinity()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ foo.AddStructuralProperty("INF", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$select=INF", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var pathSelectItem = selectedItems[0] as PathSelectItem;
+ var segments = pathSelectItem.SelectedPath.Segments.ToArray();
+ Assert.Single(segments);
+ Assert.Equal("INF", segments[0].Identifier);
+ Assert.Equal("Edm.String", segments[0].EdmType.FullTypeName());
+ }
+
+ ///
+ /// Selects a property named "NaN"
+ ///
+ [Fact]
+ public void SelectNotANumber()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ foo.AddStructuralProperty("NaN", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$select=NaN", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var pathSelectItem = selectedItems[0] as PathSelectItem;
+ var segments = pathSelectItem.SelectedPath.Segments.ToArray();
+ Assert.Single(segments);
+ Assert.Equal("NaN", segments[0].Identifier);
+ Assert.Equal("Edm.String", segments[0].EdmType.FullTypeName());
+ }
+
+ ///
+ /// Selects a property named "null"
+ ///
+ [Fact]
+ public void SelectNull()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ foo.AddStructuralProperty("null", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$select=null", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var pathSelectItem = selectedItems[0] as PathSelectItem;
+ var segments = pathSelectItem.SelectedPath.Segments.ToArray();
+ Assert.Single(segments);
+ Assert.Equal("null", segments[0].Identifier);
+ Assert.Equal("Edm.String", segments[0].EdmType.FullTypeName());
+ }
+
+ ///
+ /// Selects a property named "null" when such a property doesn't exist on the entity
+ ///
+ [Fact]
+ public void SelectNullNotFound()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ foo.AddStructuralProperty("true", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$select=null", UriKind.Relative));
+ var odataException = Assert.Throws(() => parser.ParseUri());
+
+ Assert.Equal("Could not find a property named 'null' on type 'foobar.foo'.", odataException.Message);
+ }
+
+ ///
+ /// Expands a property named "true"
+ ///
+ [Fact]
+ public void ExpandTrue()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "true",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar ,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=true", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var expandedNavigationSelectItem = selectedItems[0] as ExpandedNavigationSelectItem;
+ Assert.Equal("true", expandedNavigationSelectItem.NavigationSource.Name);
+ Assert.Equal("foobar.bar", expandedNavigationSelectItem.NavigationSource.EntityType().FullTypeName());
+ Assert.Empty(expandedNavigationSelectItem.SelectAndExpand.SelectedItems);
+ }
+
+ ///
+ /// Expands a property named "false"
+ ///
+ [Fact]
+ public void ExpandFalse()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "false",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=false", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var expandedNavigationSelectItem = selectedItems[0] as ExpandedNavigationSelectItem;
+ Assert.Equal("false", expandedNavigationSelectItem.NavigationSource.Name);
+ Assert.Equal("foobar.bar", expandedNavigationSelectItem.NavigationSource.EntityType().FullTypeName());
+ Assert.Empty(expandedNavigationSelectItem.SelectAndExpand.SelectedItems);
+ }
+
+ ///
+ /// Expands a property named "INF"
+ ///
+ [Fact]
+ public void ExpandInfinity()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "INF",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=INF", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var expandedNavigationSelectItem = selectedItems[0] as ExpandedNavigationSelectItem;
+ Assert.Equal("INF", expandedNavigationSelectItem.NavigationSource.Name);
+ Assert.Equal("foobar.bar", expandedNavigationSelectItem.NavigationSource.EntityType().FullTypeName());
+ Assert.Empty(expandedNavigationSelectItem.SelectAndExpand.SelectedItems);
+ }
+
+ ///
+ /// Expands a property named "NaN"
+ ///
+ [Fact]
+ public void ExpandNotANumber()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "NaN",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=NaN", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var expandedNavigationSelectItem = selectedItems[0] as ExpandedNavigationSelectItem;
+ Assert.Equal("NaN", expandedNavigationSelectItem.NavigationSource.Name);
+ Assert.Equal("foobar.bar", expandedNavigationSelectItem.NavigationSource.EntityType().FullTypeName());
+ Assert.Empty(expandedNavigationSelectItem.SelectAndExpand.SelectedItems);
+ }
+
+ ///
+ /// Expands a property named "null"
+ ///
+ [Fact]
+ public void ExpandNull()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "null",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=null", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var selectedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var expandedNavigationSelectItem = selectedItems[0] as ExpandedNavigationSelectItem;
+ Assert.Equal("null", expandedNavigationSelectItem.NavigationSource.Name);
+ Assert.Equal("foobar.bar", expandedNavigationSelectItem.NavigationSource.EntityType().FullTypeName());
+ Assert.Empty(expandedNavigationSelectItem.SelectAndExpand.SelectedItems);
+ }
+
+ ///
+ /// Expands a property named "null" when such a property doesn't exist on the entity
+ ///
+ [Fact]
+ public void ExpandNullNotFound()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "true",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=null", UriKind.Relative));
+ var odataException = Assert.Throws(() => parser.ParseUri());
+
+ Assert.Equal("Could not find a property named 'null' on type 'foobar.foo'.", odataException.Message);
+ }
+
+ ///
+ /// Expands a property named "true" and selects its property named "false"
+ ///
+ [Fact]
+ public void ExpandTrueSelectFalse()
+ {
+ var model = new EdmModel();
+ var foo = new EdmEntityType("foobar", "foo");
+ foo.AddKeys(foo.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ var bar = new EdmEntityType("foobar", "bar");
+ bar.AddKeys(bar.AddStructuralProperty("id", EdmCoreModel.Instance.GetInt32(false)));
+ bar.AddStructuralProperty("false", EdmCoreModel.Instance.GetString(true));
+ model.AddElement(bar);
+ foo.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo()
+ {
+ Name = "true",
+ ContainsTarget = true,
+ TargetMultiplicity = EdmMultiplicity.ZeroOrOne,
+ Target = bar,
+ });
+ model.AddElement(foo);
+ var container = new EdmEntityContainer("foobar", "FooService");
+ container.AddEntitySet("foos", foo);
+ model.AddElement(container);
+
+ var parser = new ODataUriParser(model, new Uri("/foos?$expand=true($select=false)", UriKind.Relative));
+ var odataUri = parser.ParseUri();
+
+ var expandedItems = odataUri.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(expandedItems);
+ var expandedNavigationSelectItem = expandedItems[0] as ExpandedNavigationSelectItem;
+ Assert.Equal("true", expandedNavigationSelectItem.NavigationSource.Name);
+ Assert.Equal("foobar.bar", expandedNavigationSelectItem.NavigationSource.EntityType().FullTypeName());
+
+ var selectedItems = expandedNavigationSelectItem.SelectAndExpand.SelectedItems.ToArray();
+ Assert.Single(selectedItems);
+ var pathSelectItem = selectedItems[0] as PathSelectItem;
+ var segments = pathSelectItem.SelectedPath.Segments.ToArray();
+ Assert.Single(segments);
+ Assert.Equal("false", segments[0].Identifier);
+ Assert.Equal("Edm.String", segments[0].EdmType.FullTypeName());
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]