Skip to content

Commit ef70951

Browse files
Fix $filter in (collectionofdynamicprimitivevalues) (#3190)
* Fix filter in a collection of primitive values. * update changes * update changes * update changes * update changes * update changes
1 parent ade4036 commit ef70951

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

src/Microsoft.OData.Core/UriParser/Binders/InBinder.cs

+76-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
namespace Microsoft.OData.UriParser
88
{
99
using System;
10+
using System.Buffers;
1011
using System.Diagnostics;
1112
using System.Globalization;
1213
using System.Linq;
1314
using System.Text;
14-
using Microsoft.OData.Edm;
1515
using Microsoft.OData.Core;
16+
using Microsoft.OData.Edm;
1617

1718
/// <summary>
1819
/// Class that knows how to bind the In operator.
@@ -129,7 +130,7 @@ private CollectionNode GetCollectionOperandFromToken(QueryToken queryToken, IEdm
129130
Debug.Assert(expectedType.IsCollection());
130131
string expectedTypeFullName = expectedType.Definition.AsElementType().FullTypeName();
131132

132-
if (expectedTypeFullName.Equals("Edm.String", StringComparison.Ordinal) || expectedTypeFullName.Equals("Edm.Untyped", StringComparison.Ordinal))
133+
if (expectedTypeFullName.Equals("Edm.String", StringComparison.Ordinal) || (expectedTypeFullName.Equals("Edm.Untyped", StringComparison.Ordinal) && IsCollectionEmptyOrWhiteSpace(bracketLiteralText)))
133134
{
134135
// For collection of strings, need to convert single-quoted string to double-quoted string,
135136
// and also, per ABNF, a single quote within a string literal is "encoded" as two consecutive single quotes in either
@@ -423,5 +424,78 @@ private static string NormalizeDateTimeCollectionItems(string bracketLiteralText
423424

424425
return "[" + String.Join(",", items) + "]";
425426
}
427+
428+
private static bool IsCollectionEmptyOrWhiteSpace(string bracketLiteralText)
429+
{
430+
string content = bracketLiteralText[1..^1].Trim();
431+
432+
if (string.IsNullOrWhiteSpace(content))
433+
{
434+
return true;
435+
}
436+
437+
bool isEmptyOrWhiteSpace = true;
438+
bool isCharInsideQuotes = false;
439+
char quoteChar = '\0';
440+
char[] buffer = ArrayPool<char>.Shared.Rent(content.Length);
441+
int bufferIndex = 0;
442+
443+
try
444+
{
445+
for (int i = 0; i < content.Length; i++)
446+
{
447+
char c = content[i];
448+
449+
if (isCharInsideQuotes)
450+
{
451+
if (c == quoteChar)
452+
{
453+
isCharInsideQuotes = false;
454+
}
455+
buffer[bufferIndex++] = c;
456+
}
457+
else
458+
{
459+
if (c == '"' || c == '\'')
460+
{
461+
isCharInsideQuotes = true;
462+
quoteChar = c;
463+
buffer[bufferIndex++] = c;
464+
}
465+
else if (c == ',')
466+
{
467+
string item = new string(buffer, 0, bufferIndex).Trim().Trim('\'', '"');
468+
469+
if (!string.IsNullOrWhiteSpace(item))
470+
{
471+
isEmptyOrWhiteSpace = false;
472+
break;
473+
}
474+
bufferIndex = 0;
475+
}
476+
else
477+
{
478+
buffer[bufferIndex++] = c;
479+
}
480+
}
481+
}
482+
483+
if (bufferIndex > 0)
484+
{
485+
string lastItem = new string(buffer, 0, bufferIndex).Trim().Trim('\'', '"');
486+
487+
if (!string.IsNullOrWhiteSpace(lastItem))
488+
{
489+
isEmptyOrWhiteSpace = false;
490+
}
491+
}
492+
}
493+
finally
494+
{
495+
ArrayPool<char>.Shared.Return(buffer);
496+
}
497+
498+
return isEmptyOrWhiteSpace;
499+
}
426500
}
427501
}

test/UnitTests/Microsoft.OData.Core.Tests/ScenarioTests/UriParser/FilterAndOrderByFunctionalTests.cs

+21
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,27 @@ public void FilterWithInOperationWithParensCollection()
21302130
Assert.Equal("(1,2,3)", Assert.IsType<CollectionConstantNode>(inNode.Right).LiteralText);
21312131
}
21322132

2133+
[Theory]
2134+
[InlineData("TestInt", "(1,2,3)")]
2135+
[InlineData("TestDouble", "(1.1,2.2,3.3)")]
2136+
[InlineData("TestBool", "(true,false)")]
2137+
[InlineData("TestString", "('a','b','c')")]
2138+
[InlineData("TestDecimal", "(1.1,2.2,3.3)")]
2139+
[InlineData("TestIntInSquareBrackets", "[1,2,3]")]
2140+
[InlineData("TestDoubleInSquareBrackets", "[1.1,2.2,3.3]")]
2141+
[InlineData("TestBoolInSquareBrackets", "[true,false]")]
2142+
[InlineData("TestStringInSquareBrackets", "['a','b','c']")]
2143+
[InlineData("TestDecimalInSquareBrackets", "[1.1,2.2,3.3]")]
2144+
public void FilterWithInOperationWithParensCollectionConsistingOfDynamicPrimitiveProperties(string propertyName, string values)
2145+
{
2146+
string filterExpression = $"{propertyName} in {values}";
2147+
FilterClause filter = ParseFilter(filterExpression, HardCodedTestModel.TestModel, HardCodedTestModel.GetOpenEmployeeType());
2148+
2149+
var inNode = Assert.IsType<InNode>(filter.Expression);
2150+
Assert.IsType<SingleValueOpenPropertyAccessNode>(inNode.Left);
2151+
Assert.Equal(values, Assert.IsType<CollectionConstantNode>(inNode.Right).LiteralText);
2152+
}
2153+
21332154
[Fact]
21342155
public void FilterWithInOperationWithParensCollectionAndLogicalNotOperator()
21352156
{

0 commit comments

Comments
 (0)