diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs index d8a885a07b..a7f7f17934 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs @@ -755,22 +755,9 @@ private static Documents.PartitionKeyDefinition GetPartitionKeyDefinition(InputP { if (!inputParameters.EnableOptimisticDirectExecution) return null; - // case 1: Is query going to a single partition - bool hasPartitionKey = inputParameters.PartitionKey.HasValue - && inputParameters.PartitionKey != PartitionKey.Null - && inputParameters.PartitionKey != PartitionKey.None; - - // case 2: does query execution plan have a single query range - bool hasQueryRanges = partitionedQueryExecutionInfo != null - && partitionedQueryExecutionInfo.QueryRanges.Count == 1 - && partitionedQueryExecutionInfo.QueryRanges[0].IsSingleValue; - - if (!hasPartitionKey && !hasQueryRanges) return null; - - //TODO: does collection have only one physical partition - - List targetRanges = new List(); + Debug.Assert(containerQueryProperties.ResourceId != null, "CosmosQueryExecutionContextFactory Assert!", "Container ResourceId cannot be null!"); + List targetRanges; if (partitionedQueryExecutionInfo != null) { targetRanges = await CosmosQueryExecutionContextFactory.GetTargetPartitionKeyRangesAsync( @@ -785,8 +772,10 @@ private static Documents.PartitionKeyDefinition GetPartitionKeyDefinition(InputP else { Documents.PartitionKeyDefinition partitionKeyDefinition = GetPartitionKeyDefinition(inputParameters, containerQueryProperties); - if (partitionKeyDefinition != null && containerQueryProperties.ResourceId != null && inputParameters.PartitionKey != null) + if (inputParameters.PartitionKey != null) { + Debug.Assert(partitionKeyDefinition != null, "CosmosQueryExecutionContextFactory Assert!", "PartitionKeyDefinition cannot be null if partitionKey is defined"); + targetRanges = await cosmosQueryContext.QueryClient.GetTargetPartitionKeyRangesByEpkStringAsync( cosmosQueryContext.ResourceLink, containerQueryProperties.ResourceId, @@ -794,6 +783,15 @@ private static Documents.PartitionKeyDefinition GetPartitionKeyDefinition(InputP forceRefresh: false, trace); } + else + { + targetRanges = await cosmosQueryContext.QueryClient.GetTargetPartitionKeyRangesAsync( + cosmosQueryContext.ResourceLink, + containerQueryProperties.ResourceId, + new List> { FeedRangeEpk.FullRange.Range }, + forceRefresh: false, + trace); + } } if (targetRanges.Count == 1) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/OptimisticDirectExecutionQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/OptimisticDirectExecutionQueryTests.cs index 9d89f6362a..751c21b64b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/OptimisticDirectExecutionQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/OptimisticDirectExecutionQueryTests.cs @@ -14,195 +14,131 @@ [TestCategory("Query")] public sealed class OptimisticDirectExecutionQueryTests : QueryTestsBase { + private static class PageSizeOptions + { + public static readonly int[] NonGroupByPageSizeOptions = { -1, 1, 2, 10, 100 }; + public static readonly int[] GroupByPageSizeOptions = { -1 }; + } + [TestMethod] - public async Task TestOptimisticDirectExecutionQueries() + public async Task TestPassingOptimisticDirectExecutionQueries() { int numberOfDocuments = 8; string partitionKey = "key"; string numberField = "numberField"; string nullField = "nullField"; - List documents = new List(numberOfDocuments); - for (int i = 0; i < numberOfDocuments; ++i) + List documents = CreateDocuments(numberOfDocuments, partitionKey, numberField, nullField); + + List singlePartitionContainerTestCases = new List() { - Document doc = new Document(); - doc.SetPropertyValue(partitionKey, "/value"); - doc.SetPropertyValue(numberField, i % 8); - doc.SetPropertyValue(nullField, null); - documents.Add(doc.ToString()); - } + // Tests for bool enableOptimisticDirectExecution + CreateInput( query: $"SELECT TOP 5 VALUE r.numberField FROM r ORDER BY r.{partitionKey}", expectedResult: new List { 0, 1, 2, 3, 4 }, partitionKey: "/value", partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT TOP 5 VALUE r.numberField FROM r ORDER BY r.{partitionKey}", expectedResult: new List { 0, 1, 2, 3, 4 }, partitionKey: "/value", partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: false, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.Passthrough), + + // Simple query + CreateInput( query: $"SELECT VALUE r.numberField FROM r", expectedResult: new List { 0, 1, 2, 3, 4, 5, 6, 7 }, partitionKey: "/value", partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT VALUE r.numberField FROM r", expectedResult: new List { 0, 1, 2, 3, 4, 5, 6, 7 }, partitionKey: null, partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + + // DISTINCT with ORDER BY + CreateInput( query: $"SELECT DISTINCT VALUE r.{numberField} FROM r ORDER BY r.{numberField} DESC", expectedResult: new List { 7, 6, 5, 4, 3, 2, 1, 0 }, partitionKey: "/value", partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT DISTINCT VALUE r.{numberField} FROM r ORDER BY r.{numberField} DESC", expectedResult: new List { 7, 6, 5, 4, 3, 2, 1, 0 }, partitionKey: null, partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + + // TOP with GROUP BY + CreateInput( query: $"SELECT TOP 5 VALUE r.{numberField} FROM r GROUP BY r.{numberField}", expectedResult: new List { 0, 1, 2, 3, 4 }, partitionKey: "/value", partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.GroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT TOP 5 VALUE r.{numberField} FROM r GROUP BY r.{numberField}", expectedResult: new List { 0, 1, 2, 3, 4 }, partitionKey: null, partition: CollectionTypes.SinglePartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.GroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + + // OFFSET LIMIT with WHERE and BETWEEN + CreateInput( query: $"SELECT VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {numberOfDocuments} OFFSET 1 LIMIT 1", expectedResult: new List { 1 }, partitionKey: "/value", partition: CollectionTypes.SinglePartition, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, enableOptimisticDirectExecution: true, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {numberOfDocuments} OFFSET 1 LIMIT 1", expectedResult: new List { 1 }, partitionKey: null, partition: CollectionTypes.SinglePartition, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, enableOptimisticDirectExecution: true, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution) + }; - SinglePartitionWithContinuationsArgs args = new SinglePartitionWithContinuationsArgs + List multiPartitionContainerTestCases = new List() { - NumberOfDocuments = numberOfDocuments, - PartitionKey = partitionKey, - NumberField = numberField, - NullField = nullField, + // Simple query + CreateInput( query: $"SELECT VALUE r.numberField FROM r", expectedResult: new List { 0, 1, 2, 3, 4, 5, 6, 7 }, partitionKey: "/value", partition: CollectionTypes.MultiPartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT VALUE r.numberField FROM r", expectedResult: new List { 0, 1, 2, 3, 4, 5, 6, 7 }, partitionKey: null, partition: CollectionTypes.MultiPartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.Passthrough), + + // DISTINCT with ORDER BY + CreateInput( query: $"SELECT DISTINCT VALUE r.{numberField} FROM r ORDER BY r.{numberField} DESC", expectedResult: new List { 7, 6, 5, 4, 3, 2, 1, 0 }, partitionKey: "/value", partition: CollectionTypes.MultiPartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT DISTINCT VALUE r.{numberField} FROM r ORDER BY r.{numberField} DESC", expectedResult: new List { 7, 6, 5, 4, 3, 2, 1, 0 }, partitionKey: null, partition: CollectionTypes.MultiPartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.Specialized), + + // OFFSET LIMIT with WHERE and BETWEEN + CreateInput( query: $"SELECT VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {numberOfDocuments} OFFSET 1 LIMIT 1", expectedResult: new List { 1 }, partitionKey: "/value", partition: CollectionTypes.MultiPartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.OptimisticDirectExecution), + CreateInput( query: $"SELECT VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {numberOfDocuments} OFFSET 1 LIMIT 1", expectedResult: new List { 1 }, partitionKey: null, partition: CollectionTypes.MultiPartition, enableOptimisticDirectExecution: true, pageSizeOptions: PageSizeOptions.NonGroupByPageSizeOptions, expectedPipelineType: TestInjections.PipelineType.Specialized) }; - await this.CreateIngestQueryDeleteAsync( + await this.CreateIngestQueryDeleteAsync( ConnectionModes.Direct | ConnectionModes.Gateway, - CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + CollectionTypes.SinglePartition, documents, - RunTests, - args, + (container, documents) => RunPassingTests(singlePartitionContainerTestCases, container), "/" + partitionKey); - } - private static async Task RunTests(Container container, IReadOnlyList documents, SinglePartitionWithContinuationsArgs args) - { - await TestPositiveOptimisticDirectExecutionOutput(container, args); - await TestNegativeOptimisticDirectExecutionOutput(container); + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.MultiPartition, + documents, + (container, documents) => RunPassingTests(multiPartitionContainerTestCases, container), + "/" + partitionKey); } - private static async Task TestPositiveOptimisticDirectExecutionOutput( - Container container, - SinglePartitionWithContinuationsArgs args) + [TestMethod] + public async Task TestFailingOptimisticDirectExecutionOutput() { - int documentCount = args.NumberOfDocuments; - string partitionKey = args.PartitionKey; - string numberField = args.NumberField; - string nullField = args.NullField; - - QueryRequestOptions feedOptions = new QueryRequestOptions - { - MaxItemCount = -1, - EnableOptimisticDirectExecution = true, - TestSettings = new TestInjections(simulate429s: false, simulateEmptyPages: false, new TestInjections.ResponseStats()) - }; - - // check if pipeline returns empty continuation token - FeedResponse responseWithEmptyContinuationExpected = await container.GetItemQueryIterator( - $"SELECT TOP 0 * FROM r", - requestOptions: feedOptions).ReadNextAsync(); - - Assert.AreEqual(null, responseWithEmptyContinuationExpected.ContinuationToken); - - List queries = new List - { - $"SELECT TOP 5 VALUE r.numberField FROM r ORDER BY r.{partitionKey}", - $"SELECT VALUE r.numberField FROM r", - $"SELECT VALUE r.numberField FROM r", - $"SELECT TOP 4 VALUE r.numberField FROM r ORDER BY r.{numberField}", - $"SELECT TOP 3 VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {documentCount} ORDER BY r.{numberField} DESC", - $"SELECT VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {documentCount} OFFSET 1 LIMIT 1", - $"SELECT DISTINCT VALUE r.{numberField} FROM r ORDER BY r.{numberField}", - $"SELECT TOP 3 VALUE r.numberField FROM r WHERE r.{numberField} BETWEEN 0 AND {documentCount} ORDER BY r.{numberField} DESC", - $"SELECT TOP 4 VALUE r.numberField FROM r ORDER BY r.{numberField}", - $"SELECT VALUE r.numberField FROM r", - }; - - List> results = new List> - { - new List { 0, 1, 2, 3, 4 }, - new List { 0, 1, 2, 3, 4, 5, 6, 7 }, - new List { 0, 1, 2, 3, 4, 5, 6, 7 }, - new List { 0, 1, 2, 3}, - new List { 7, 6, 5}, - new List { 1}, - new List { 0, 1, 2, 3, 4, 5, 6, 7}, - new List { 7, 6, 5}, - new List { 0, 1, 2, 3}, - new List { 0, 1, 2, 3, 4, 5, 6, 7 }, - }; - - List partitionKeys = new List - { - "/value", - null, - "/value", - "/value", - "/value", - null, - null, - "/value", - "/value", - "/value", - }; + int numberOfDocuments = 8; + string partitionKey = "key"; + string numberField = "numberField"; + string nullField = "nullField"; - List expectedPipelineType = new List - { - TestInjections.PipelineType.OptimisticDirectExecution, - TestInjections.PipelineType.Passthrough, - TestInjections.PipelineType.OptimisticDirectExecution, - TestInjections.PipelineType.OptimisticDirectExecution, - TestInjections.PipelineType.OptimisticDirectExecution, - TestInjections.PipelineType.Specialized, - TestInjections.PipelineType.Specialized, - TestInjections.PipelineType.Specialized, - TestInjections.PipelineType.Specialized, - TestInjections.PipelineType.Passthrough, - }; + List documents = CreateDocuments(numberOfDocuments, partitionKey, numberField, nullField); - List enabledOptimisticDirectExecution = new List + // check if bad continuation queries and syntax error queries are handled by pipeline + IDictionary invalidQueries = new Dictionary { - true, - true, - true, - true, - true, - true, - true, - false, - false, - false, + { "SELECT * FROM t", Guid.NewGuid().ToString() }, + { "SELECT TOP 10 * FOM r", null }, + { "this is not a valid query", null }, }; + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + documents, + (container, documents) => RunFailingTests(container, invalidQueries), + "/" + partitionKey); + } - List queryAndResults = new List(); - - for (int i = 0; i < queries.Count(); i++) - { - QueryResultsAndPipelineType queryAndResult = new QueryResultsAndPipelineType() - { - Query = queries[i], - Result = results[i], - PartitionKey = partitionKeys[i], - ExpectedPipelineType = expectedPipelineType[i], - EnableOptimisticDirectExecution = enabledOptimisticDirectExecution[i], - }; - - queryAndResults.Add(queryAndResult); - } - - int[] pageSizeOptions = new[] { -1, 1, 2, 10, 100 }; - - for (int i = 0; i < pageSizeOptions.Length; i++) + private static async Task RunPassingTests(IEnumerable testCases, Container container) + { + foreach (DirectExecutionTestCase testCase in testCases) { - for(int j = 0; j < queryAndResults.Count(); j++) + foreach (int pageSize in testCase.PageSizeOptions) { - feedOptions = new QueryRequestOptions + QueryRequestOptions feedOptions = new QueryRequestOptions { - MaxItemCount = pageSizeOptions[i], - PartitionKey = queryAndResults[j].PartitionKey == null - ? null - : new Cosmos.PartitionKey(queryAndResults[j].PartitionKey), - EnableOptimisticDirectExecution = queryAndResults[j].EnableOptimisticDirectExecution, + MaxItemCount = pageSize, + PartitionKey = testCase.PartitionKey == null + ? null + : new Cosmos.PartitionKey(testCase.PartitionKey), + EnableOptimisticDirectExecution = testCase.EnableOptimisticDirectExecution, TestSettings = new TestInjections(simulate429s: false, simulateEmptyPages: false, new TestInjections.ResponseStats()) }; List items = await RunQueryAsync( container, - queryAndResults[j].Query, + testCase.Query, feedOptions); long[] actual = items.Cast().Select(x => Number64.ToLong(x.Value)).ToArray(); - Assert.IsTrue(queryAndResults[j].Result.SequenceEqual(actual)); - - if (queryAndResults[j].EnableOptimisticDirectExecution) - { - Assert.AreEqual(queryAndResults[j].ExpectedPipelineType, feedOptions.TestSettings.Stats.PipelineType.Value); - } - else - { // test if pipeline is called if TestInjection.EnableOptimisticDirectExecution is false - Assert.AreNotEqual(TestInjections.PipelineType.OptimisticDirectExecution, feedOptions.TestSettings.Stats.PipelineType.Value); - } + Assert.IsTrue(testCase.ExpectedResult.SequenceEqual(actual)); + Assert.AreEqual(testCase.ExpectedPipelineType, feedOptions.TestSettings.Stats.PipelineType.Value); } } } - private static async Task TestNegativeOptimisticDirectExecutionOutput( - Container container) + private static async Task RunFailingTests(Container container, IDictionary invalidQueries) { QueryRequestOptions feedOptions = new QueryRequestOptions { @@ -211,21 +147,13 @@ private static async Task TestNegativeOptimisticDirectExecutionOutput( TestSettings = new TestInjections(simulate429s: false, simulateEmptyPages: false, new TestInjections.ResponseStats()) }; - // check if bad continuation queries and syntax error queries are handled by pipeline - IDictionary invalidQueries = new Dictionary - { - { "SELECT * FROM t", Guid.NewGuid().ToString() }, - { "SELECT TOP 10 * FOM r", null }, - { "this is not a valid query", null }, - }; - - foreach (KeyValuePair entry in invalidQueries) + foreach (KeyValuePair queryAndResult in invalidQueries) { try { await container.GetItemQueryIterator( - queryDefinition: new QueryDefinition(entry.Key), - continuationToken: entry.Value, + queryDefinition: new QueryDefinition(queryAndResult.Key), + continuationToken: queryAndResult.Value, requestOptions: feedOptions).ReadNextAsync(); Assert.Fail("Expect exception"); @@ -241,21 +169,60 @@ await container.GetItemQueryIterator( } } - private struct SinglePartitionWithContinuationsArgs + private static List CreateDocuments(int documentCount, string partitionKey, string numberField, string nullField) + { + List documents = new List(documentCount); + for (int i = 0; i < documentCount; ++i) + { + Document doc = new Document(); + doc.SetPropertyValue(partitionKey, "/value"); + doc.SetPropertyValue(numberField, i % documentCount); + doc.SetPropertyValue(nullField, null); + documents.Add(doc.ToString()); + } + + return documents; + } + + private static DirectExecutionTestCase CreateInput( + string query, + List expectedResult, + string partitionKey, + CollectionTypes partition, + bool enableOptimisticDirectExecution, + int[] pageSizeOptions, + TestInjections.PipelineType expectedPipelineType) { - public int NumberOfDocuments; - public string PartitionKey; - public string NumberField; - public string NullField; + return new DirectExecutionTestCase(query, expectedResult, partitionKey, partition, enableOptimisticDirectExecution, pageSizeOptions, expectedPipelineType); } - private struct QueryResultsAndPipelineType + internal readonly struct DirectExecutionTestCase { - public string Query { get; set; } - public List Result { get; set; } - public string PartitionKey { get; set; } - public TestInjections.PipelineType ExpectedPipelineType { get; set; } - public bool EnableOptimisticDirectExecution { get; set; } + public string Query { get; } + public List ExpectedResult { get; } + public string PartitionKey { get; } + public CollectionTypes Partition { get; } + public bool EnableOptimisticDirectExecution { get; } + public int[] PageSizeOptions { get; } + public TestInjections.PipelineType ExpectedPipelineType { get; } + + public DirectExecutionTestCase( + string query, + List expectedResult, + string partitionKey, + CollectionTypes partition, + bool enableOptimisticDirectExecution, + int[] pageSizeOptions, + TestInjections.PipelineType expectedPipelineType) + { + this.Query = query; + this.ExpectedResult = expectedResult; + this.PartitionKey = partitionKey; + this.Partition = partition; + this.EnableOptimisticDirectExecution = enableOptimisticDirectExecution; + this.PageSizeOptions = pageSizeOptions; + this.ExpectedPipelineType = expectedPipelineType; + } } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.NegativeOptimisticDirectExecutionOutput.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.NegativeOptimisticDirectExecutionOutput.xml index 7a8259eefc..4685001217 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.NegativeOptimisticDirectExecutionOutput.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.NegativeOptimisticDirectExecutionOutput.xml @@ -1,43 +1,4 @@  - - - Null Partition Key Value - SELECT * FROM c - - /pk - - Hash - - - false - - - - - None Partition Key Value - SELECT * FROM c - - /pk - - Hash - - - false - - - - - C# Null Partition Key Value - SELECT * FROM c - - /pk - - Hash - - - false - - Single Partition Key with Parallel continuation token diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.PositiveOptimisticDirectExecutionOutput.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.PositiveOptimisticDirectExecutionOutput.xml index 2482925bd8..5e496fc564 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.PositiveOptimisticDirectExecutionOutput.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/OptimisticDirectExecutionQueryBaselineTests.PositiveOptimisticDirectExecutionOutput.xml @@ -64,4 +64,43 @@ true + + + Null Partition Key Value + SELECT * FROM c + + /pk + + Hash + + + true + + + + + None Partition Key Value + SELECT * FROM c + + /pk + + Hash + + + true + + + + + C# Null Partition Key Value + SELECT * FROM c + + /pk + + Hash + + + true + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs index b6a78c111d..20de3f1f03 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs @@ -36,10 +36,6 @@ public class OptimisticDirectExecutionQueryBaselineTests : BaselineTests testVariations = new List { CreateInput( @@ -77,7 +73,32 @@ public void PositiveOptimisticDirectExecutionOutput() expectedOptimisticDirectExecution: true, partitionKeyPath: @"/pk", partitionKeyValue: "a", - continuationToken: cosmosElementOdeContinuationToken), + continuationToken: CosmosElement.Parse( + "{\"OptimisticDirectExecutionToken\":{\"token\":\"{\\\"resourceId\\\":\\\"AQAAAMmFOw8LAAAAAAAAAA==\\\"," + + "\\\"skipCount\\\":1}\", \"range\":{\"min\":\"\",\"max\":\"FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF\"}}}")), + + // Below cases are Ode because they have a collection with a single physical partition. + // Added emulator tests (TestPassingOptimisticDirectExecutionQueries()) to verify the negation of the below cases. + CreateInput( + description: @"Null Partition Key Value", + query: "SELECT * FROM c", + expectedOptimisticDirectExecution: true, + partitionKeyPath: @"/pk", + partitionKeyValue: Cosmos.PartitionKey.Null), + + CreateInput( + description: @"None Partition Key Value", + query: "SELECT * FROM c", + expectedOptimisticDirectExecution: true, + partitionKeyPath: @"/pk", + partitionKeyValue: Cosmos.PartitionKey.None), + + CreateInput( + description: @"C# Null Partition Key Value", + query: "SELECT * FROM c", + expectedOptimisticDirectExecution: true, + partitionKeyPath: @"/pk", + partitionKeyValue: null), }; this.ExecuteTestSuite(testVariations); } @@ -105,27 +126,6 @@ public void NegativeOptimisticDirectExecutionOutput() List testVariations = new List { - CreateInput( - description: @"Null Partition Key Value", - query: "SELECT * FROM c", - expectedOptimisticDirectExecution: false, - partitionKeyPath: @"/pk", - partitionKeyValue: Cosmos.PartitionKey.Null), - - CreateInput( - description: @"None Partition Key Value", - query: "SELECT * FROM c", - expectedOptimisticDirectExecution: false, - partitionKeyPath: @"/pk", - partitionKeyValue: Cosmos.PartitionKey.None), - - CreateInput( - description: @"C# Null Partition Key Value", - query: "SELECT * FROM c", - expectedOptimisticDirectExecution: false, - partitionKeyPath: @"/pk", - partitionKeyValue: null), - CreateInput( description: @"Single Partition Key with Parallel continuation token", query: "SELECT * FROM c", @@ -798,7 +798,7 @@ public override Task> TryGetOverlappingRangesAs } public override async Task> TryGetPartitionedQueryExecutionInfoAsync(SqlQuerySpec sqlQuerySpec, ResourceType resourceType, PartitionKeyDefinition partitionKeyDefinition, bool requireFormattableOrderByQuery, bool isContinuationExpected, bool allowNonValueAggregateQuery, bool hasLogicalPartitionKey, bool allowDCount, bool useSystemPrefix, Cosmos.GeospatialType geospatialType, CancellationToken cancellationToken) - { + { CosmosSerializerCore serializerCore = new(); using StreamReader streamReader = new(serializerCore.ToStreamSqlQuerySpec(sqlQuerySpec, Documents.ResourceType.Document)); string sqlQuerySpecJsonString = streamReader.ReadToEnd();