Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query correctly handles different language cultures #567

Merged
merged 6 commits into from
Jul 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos.Json
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;

Expand Down Expand Up @@ -911,7 +912,7 @@ public JsonTextTokenType CurrentTokenType
public double GetNumberValue()
{
string stringDouble = this.encoding.GetString(this.bufferedToken.GetBuffer(), 0, this.tokenLength);
return double.Parse(stringDouble);
return double.Parse(stringDouble, CultureInfo.InvariantCulture);
}

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion Microsoft.Azure.Cosmos/src/Json/JsonTextUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Microsoft.Azure.Cosmos.Json
{
using System;
using System.Globalization;
using System.Text;

/// <summary>
Expand All @@ -23,7 +24,7 @@ public static double GetNumberValue(ArraySegment<byte> bufferedToken)
int offset = bufferedToken.Offset;
int count = bufferedToken.Count;
string stringDouble = Encoding.UTF8.GetString(rawBufferedTokenArray, offset, count);
return double.Parse(stringDouble);
return double.Parse(stringDouble, CultureInfo.InvariantCulture);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Cosmos.Json
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

Expand Down Expand Up @@ -179,7 +180,7 @@ public override void WriteIntValue(long value)
{
this.JsonObjectState.RegisterToken(JsonTokenType.Number);
this.PrefixMemberSeparator();
this.streamWriter.Write(value.ToString());
this.streamWriter.Write(value.ToString(CultureInfo.InvariantCulture));
}

/// <summary>
Expand Down Expand Up @@ -226,7 +227,7 @@ public override void WriteNumberValue(double value)
// If you require more precision, specify format with the "G17" format specification, which always returns 17 digits of precision,
// or "R", which returns 15 digits if the number can be represented with that precision or 17 digits if the number can only be represented with maximum precision.
// In some cases, Double values formatted with the "R" standard numeric format string do not successfully round-trip if compiled using the /platform:x64 or /platform:anycpu switches and run on 64-bit systems. To work around this problem, you can format Double values by using the "G17" standard numeric format string.
this.streamWriter.Write(value.ToString("R"));
this.streamWriter.Write(value.ToString("R", CultureInfo.InvariantCulture));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Microsoft.Azure.Cosmos.Json.NewtonsoftInterop
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using Newtonsoft.Json;

internal sealed class JsonNewtonsoftReader : Microsoft.Azure.Cosmos.Json.JsonReader
Expand Down Expand Up @@ -40,14 +42,14 @@ public override double GetNumberValue()
object value = this.reader.Value;
if (value is double)
{
numberString = ((double)value).ToString("R");
numberString = ((double)value).ToString("R", CultureInfo.InvariantCulture);
}
else
{
numberString = value.ToString();
}

return double.Parse(numberString);
return double.Parse(numberString, CultureInfo.InvariantCulture);
}

public override string GetStringValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Cosmos.Json.NewtonsoftInterop
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
Expand All @@ -16,16 +17,16 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Json;
using Microsoft.Azure.Cosmos.Linq;
using Microsoft.Azure.Cosmos.Query;
using Microsoft.Azure.Cosmos.Routing;
using Microsoft.Azure.Cosmos.Linq;
using Microsoft.Azure.Cosmos.Utils;
using Microsoft.Azure.Documents;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using JsonReader = Json.JsonReader;
using JsonWriter = Json.JsonWriter;
using Newtonsoft.Json.Linq;

[TestClass]
public class CosmosItemTests : BaseCosmosClientHelper
Expand Down Expand Up @@ -743,45 +744,65 @@ public async Task ItemMultiplePartitionQuery()
[TestMethod]
public async Task ItemMultiplePartitionOrderByQueryStream()
{
IList<ToDoActivity> deleteList = await this.CreateRandomItems(300, randomPartitionKey: true);

QueryDefinition sql = new QueryDefinition("SELECT * FROM toDoActivity t ORDER BY t.taskNum ");
CultureInfo defaultCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;

QueryRequestOptions requestOptions = new QueryRequestOptions()
CultureInfo[] cultureInfoList = new CultureInfo[]
{
MaxBufferedItemCount = 10,
ResponseContinuationTokenLimitInKb = 500,
MaxConcurrency = 5,
MaxItemCount = 1,
defaultCultureInfo,
System.Globalization.CultureInfo.GetCultureInfo("fr-FR")
};

List<ToDoActivity> resultList = new List<ToDoActivity>();
double totalRequstCharge = 0;
FeedIterator feedIterator = this.Container.GetItemQueryStreamIterator(
sql,
requestOptions: requestOptions);
IList<ToDoActivity> deleteList = await this.CreateRandomItems(300, randomPartitionKey: true);

while (feedIterator.HasMoreResults)
try
{
ResponseMessage iter = await feedIterator.ReadNextAsync();
Assert.IsTrue(iter.IsSuccessStatusCode);
Assert.IsNull(iter.ErrorMessage);
totalRequstCharge += iter.Headers.RequestCharge;
foreach (CultureInfo cultureInfo in cultureInfoList)
{
System.Threading.Thread.CurrentThread.CurrentCulture = cultureInfo;

ToDoActivity[] activities = this.jsonSerializer.FromStream<CosmosFeedResponseUtil<ToDoActivity>>(iter.Content).Data.ToArray();
Assert.AreEqual(1, activities.Length);
ToDoActivity response = activities.First();
resultList.Add(response);
}
QueryDefinition sql = new QueryDefinition("SELECT * FROM toDoActivity t ORDER BY t.taskNum ");

Assert.AreEqual(deleteList.Count, resultList.Count);
Assert.IsTrue(totalRequstCharge > 0);
QueryRequestOptions requestOptions = new QueryRequestOptions()
{
MaxBufferedItemCount = 10,
ResponseContinuationTokenLimitInKb = 500,
MaxConcurrency = 5,
MaxItemCount = 1,
};

List<ToDoActivity> resultList = new List<ToDoActivity>();
double totalRequstCharge = 0;
FeedIterator feedIterator = this.Container.GetItemQueryStreamIterator(
sql,
requestOptions: requestOptions);

List<ToDoActivity> verifiedOrderBy = deleteList.OrderBy(x => x.taskNum).ToList();
for (int i = 0; i < verifiedOrderBy.Count(); i++)
while (feedIterator.HasMoreResults)
{
ResponseMessage iter = await feedIterator.ReadNextAsync();
Assert.IsTrue(iter.IsSuccessStatusCode);
Assert.IsNull(iter.ErrorMessage);
totalRequstCharge += iter.Headers.RequestCharge;

ToDoActivity[] activities = this.jsonSerializer.FromStream<CosmosFeedResponseUtil<ToDoActivity>>(iter.Content).Data.ToArray();
Assert.AreEqual(1, activities.Length);
ToDoActivity response = activities.First();
resultList.Add(response);
}

Assert.AreEqual(deleteList.Count, resultList.Count);
Assert.IsTrue(totalRequstCharge > 0);

List<ToDoActivity> verifiedOrderBy = deleteList.OrderBy(x => x.taskNum).ToList();
for (int i = 0; i < verifiedOrderBy.Count(); i++)
{
Assert.AreEqual(verifiedOrderBy[i].taskNum, resultList[i].taskNum);
Assert.AreEqual(verifiedOrderBy[i].id, resultList[i].id);
}
}
}
finally
{
Assert.AreEqual(verifiedOrderBy[i].taskNum, resultList[i].taskNum);
Assert.AreEqual(verifiedOrderBy[i].id, resultList[i].id);
System.Threading.Thread.CurrentThread.CurrentCulture = defaultCultureInfo;
}
}

Expand Down Expand Up @@ -1277,7 +1298,7 @@ public async Task ReadNonPartitionItemAsync()
public async Task ItemLINQQueryTest()
{
//Creating items for query.
IList<ToDoActivity> itemList = await CreateRandomItems(pkCount: 2, perPKItemCount: 1, randomPartitionKey: true);
IList<ToDoActivity> itemList = await this.CreateRandomItems(pkCount: 2, perPKItemCount: 1, randomPartitionKey: true);

IOrderedQueryable<ToDoActivity> linqQueryable = this.Container.GetItemLinqQueryable<ToDoActivity>();
IQueryable<ToDoActivity> queriable = linqQueryable.Where(item => (item.taskNum < 100));
Expand All @@ -1293,7 +1314,7 @@ public async Task ItemLINQQueryTest()
resultsFetched += queryResponse.Count();

// For the items returned with NonePartitionKeyValue
var iter = queryResponse.GetEnumerator();
IEnumerator<ToDoActivity> iter = queryResponse.GetEnumerator();
while (iter.MoveNext())
{
ToDoActivity activity = iter.Current;
Expand All @@ -1305,7 +1326,7 @@ public async Task ItemLINQQueryTest()
//Checking for exception in case of ToFeedIterator() use on non cosmos linq IQueryable.
try
{
IQueryable<ToDoActivity> nonLinqQueryable = (new List<ToDoActivity> { CreateRandomToDoActivity() }).AsQueryable();
IQueryable<ToDoActivity> nonLinqQueryable = (new List<ToDoActivity> { this.CreateRandomToDoActivity() }).AsQueryable();
setIterator = nonLinqQueryable.ToFeedIterator();
Assert.Fail("It should throw ArgumentOutOfRangeException as ToFeedIterator() only applicable to cosmos LINQ query");
}
Expand Down Expand Up @@ -1355,7 +1376,7 @@ public async Task ItemLINQQueryTest()
public async Task ItemLINQQueryWithContinuationTokenTest()
{
//Creating items for query.
IList<ToDoActivity> itemList = await CreateRandomItems(pkCount: 10, perPKItemCount: 1, randomPartitionKey: true);
IList<ToDoActivity> itemList = await this.CreateRandomItems(pkCount: 10, perPKItemCount: 1, randomPartitionKey: true);

QueryRequestOptions queryRequestOptions = new QueryRequestOptions();
queryRequestOptions.MaxConcurrency = 1;
Expand Down Expand Up @@ -1429,7 +1450,7 @@ public async Task MigrateDataInNonPartitionContainer()
resultsFetched += queryResponse.Count();

// For the items returned with NonePartitionKeyValue
var iter = queryResponse.GetEnumerator();
IEnumerator<ToDoActivity> iter = queryResponse.GetEnumerator();
while (iter.MoveNext())
{
ToDoActivity activity = iter.Current;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.Azure.Cosmos.NetFramework.Tests.Json
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Azure.Cosmos.Json;
using System.IO;
using System.Globalization;

[TestClass]
public class JsonNavigatorTests
Expand Down Expand Up @@ -373,36 +374,56 @@ private void VerifyNavigator(string input, bool performExtraChecks = true)

private void VerifyNavigator(string input, Exception expectedException, bool performExtraChecks = true)
{
IJsonReader jsonReader = JsonReader.Create(Encoding.UTF8.GetBytes(input));
JsonTokenInfo[] tokensFromReader = JsonNavigatorTests.GetTokensWithReader(jsonReader);
CultureInfo defaultCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;

// Test text
IJsonNavigator textNavigator = JsonNavigator.Create(Encoding.UTF8.GetBytes(input));
IJsonNavigatorNode textRootNode = textNavigator.GetRootNode();
JsonTokenInfo[] tokensFromTextNavigator = JsonNavigatorTests.GetTokensFromNode(textRootNode, textNavigator, performExtraChecks);
CultureInfo[] cultureInfoList = new CultureInfo[]
{
defaultCultureInfo,
System.Globalization.CultureInfo.GetCultureInfo("fr-FR")
};

try
{
foreach (CultureInfo cultureInfo in cultureInfoList)
{
System.Threading.Thread.CurrentThread.CurrentCulture = cultureInfo;

IJsonReader jsonReader = JsonReader.Create(Encoding.UTF8.GetBytes(input));
JsonTokenInfo[] tokensFromReader = JsonNavigatorTests.GetTokensWithReader(jsonReader);

// Test text
IJsonNavigator textNavigator = JsonNavigator.Create(Encoding.UTF8.GetBytes(input));
IJsonNavigatorNode textRootNode = textNavigator.GetRootNode();
JsonTokenInfo[] tokensFromTextNavigator = JsonNavigatorTests.GetTokensFromNode(textRootNode, textNavigator, performExtraChecks);

Assert.IsTrue(tokensFromTextNavigator.SequenceEqual(tokensFromReader));
Assert.IsTrue(tokensFromTextNavigator.SequenceEqual(tokensFromReader));

// Test binary
byte[] binaryInput = JsonTestUtils.ConvertTextToBinary(input);
IJsonNavigator binaryNavigator = JsonNavigator.Create(binaryInput);
IJsonNavigatorNode binaryRootNode = binaryNavigator.GetRootNode();
JsonTokenInfo[] tokensFromBinaryNavigator = JsonNavigatorTests.GetTokensFromNode(binaryRootNode, binaryNavigator, performExtraChecks);
// Test binary
byte[] binaryInput = JsonTestUtils.ConvertTextToBinary(input);
IJsonNavigator binaryNavigator = JsonNavigator.Create(binaryInput);
IJsonNavigatorNode binaryRootNode = binaryNavigator.GetRootNode();
JsonTokenInfo[] tokensFromBinaryNavigator = JsonNavigatorTests.GetTokensFromNode(binaryRootNode, binaryNavigator, performExtraChecks);

Assert.IsTrue(tokensFromBinaryNavigator.SequenceEqual(tokensFromReader));
Assert.IsTrue(tokensFromBinaryNavigator.SequenceEqual(tokensFromReader));

// Test binary + user string encoding
JsonStringDictionary jsonStringDictionary = new JsonStringDictionary(capacity: 4096);
byte[] binaryWithUserStringEncodingInput = JsonTestUtils.ConvertTextToBinary(input, jsonStringDictionary);
if (jsonStringDictionary.TryGetStringAtIndex(index: 0, value: out string temp))
// Test binary + user string encoding
JsonStringDictionary jsonStringDictionary = new JsonStringDictionary(capacity: 4096);
byte[] binaryWithUserStringEncodingInput = JsonTestUtils.ConvertTextToBinary(input, jsonStringDictionary);
if (jsonStringDictionary.TryGetStringAtIndex(index: 0, value: out string temp))
{
Assert.IsFalse(binaryWithUserStringEncodingInput.SequenceEqual(binaryInput), "Binary should be different with user string encoding");
}
IJsonNavigator binaryNavigatorWithUserStringEncoding = JsonNavigator.Create(binaryInput, jsonStringDictionary);
IJsonNavigatorNode binaryRootNodeWithUserStringEncoding = binaryNavigatorWithUserStringEncoding.GetRootNode();
JsonTokenInfo[] tokensFromBinaryNavigatorWithUserStringEncoding = JsonNavigatorTests.GetTokensFromNode(binaryRootNode, binaryNavigator, performExtraChecks);

Assert.IsTrue(tokensFromBinaryNavigatorWithUserStringEncoding.SequenceEqual(tokensFromReader));
}
}
finally
{
Assert.IsFalse(binaryWithUserStringEncodingInput.SequenceEqual(binaryInput), "Binary should be different with user string encoding");
System.Threading.Thread.CurrentThread.CurrentCulture = defaultCultureInfo;
}
IJsonNavigator binaryNavigatorWithUserStringEncoding = JsonNavigator.Create(binaryInput, jsonStringDictionary);
IJsonNavigatorNode binaryRootNodeWithUserStringEncoding = binaryNavigatorWithUserStringEncoding.GetRootNode();
JsonTokenInfo[] tokensFromBinaryNavigatorWithUserStringEncoding = JsonNavigatorTests.GetTokensFromNode(binaryRootNode, binaryNavigator, performExtraChecks);

Assert.IsTrue(tokensFromBinaryNavigatorWithUserStringEncoding.SequenceEqual(tokensFromReader));
}

internal static JsonTokenInfo[] GetTokensWithReader(IJsonReader jsonReader)
Expand Down
Loading