Skip to content
Draft
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 @@ -142,13 +142,56 @@ public override async Task<TryCatch<QueryPage>> ExecuteItemQueryAsync(
ITrace trace,
CancellationToken cancellationToken)
{
requestOptions.MaxItemCount = pageSize;
// Create a copy of the requestOptions to avoid modifying the original object
// that users might be caching. This fixes issue #5225.
QueryRequestOptions requestOptionsCopy = new QueryRequestOptions
{
ResponseContinuationTokenLimitInKb = requestOptions.ResponseContinuationTokenLimitInKb,
EnableScanInQuery = requestOptions.EnableScanInQuery,
EnableLowPrecisionOrderBy = requestOptions.EnableLowPrecisionOrderBy,
EnableOptimisticDirectExecution = requestOptions.EnableOptimisticDirectExecution,
MaxBufferedItemCount = requestOptions.MaxBufferedItemCount,
MaxItemCount = requestOptions.MaxItemCount,
MaxConcurrency = requestOptions.MaxConcurrency,
PartitionKey = requestOptions.PartitionKey,
PopulateIndexMetrics = requestOptions.PopulateIndexMetrics,
PopulateQueryAdvice = requestOptions.PopulateQueryAdvice,
ConsistencyLevel = requestOptions.ConsistencyLevel,
SessionToken = requestOptions.SessionToken,
DedicatedGatewayRequestOptions = requestOptions.DedicatedGatewayRequestOptions,
QueryTextMode = requestOptions.QueryTextMode,
CosmosElementContinuationToken = requestOptions.CosmosElementContinuationToken,
StartId = requestOptions.StartId,
EndId = requestOptions.EndId,
EnumerationDirection = requestOptions.EnumerationDirection,
CosmosSerializationFormatOptions = requestOptions.CosmosSerializationFormatOptions,
SupportedSerializationFormats = requestOptions.SupportedSerializationFormats,
ReturnResultsInDeterministicOrder = requestOptions.ReturnResultsInDeterministicOrder,
TestSettings = requestOptions.TestSettings,
FeedRange = requestOptions.FeedRange,
IsHybridSearchQueryPlanOptimizationDisabled = requestOptions.IsHybridSearchQueryPlanOptimizationDisabled,
EnableDistributedQueryGatewayMode = requestOptions.EnableDistributedQueryGatewayMode,
// Base RequestOptions properties
IfMatchEtag = requestOptions.IfMatchEtag,
IfNoneMatchEtag = requestOptions.IfNoneMatchEtag,
Properties = requestOptions.Properties,
AddRequestHeaders = requestOptions.AddRequestHeaders,
PriorityLevel = requestOptions.PriorityLevel,
CosmosThresholdOptions = requestOptions.CosmosThresholdOptions,
ExcludeRegions = requestOptions.ExcludeRegions,
AvailabilityStrategy = requestOptions.AvailabilityStrategy,
IsEffectivePartitionKeyRouting = requestOptions.IsEffectivePartitionKeyRouting,
BaseConsistencyLevel = requestOptions.BaseConsistencyLevel
};

// Now modify the copy instead of the original
requestOptionsCopy.MaxItemCount = pageSize;

ResponseMessage message = await this.clientContext.ProcessResourceOperationStreamAsync(
resourceUri: resourceUri,
resourceType: resourceType,
operationType: operationType,
requestOptions: requestOptions,
requestOptions: requestOptionsCopy,
feedRange: feedRange,
cosmosContainerCore: this.cosmosContainerCore,
streamPayload: this.clientContext.SerializerCore.ToStreamSqlQuerySpec(sqlQuerySpec, resourceType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand All @@ -22,5 +23,78 @@ public void StatelessTest()

Assert.IsNull(testMessage.Headers.ContinuationToken);
}

[TestMethod]
public void MaxItemCountNotModifiedInOriginalQueryRequestOptions_SimpleCopyTest()
{
// This test verifies that when QueryRequestOptions properties are copied,
// the original object is not modified. This reproduces the issue described in GitHub issue #5225.

QueryRequestOptions originalOptions = new QueryRequestOptions
{
MaxItemCount = -1,
MaxConcurrency = 10,
EnableScanInQuery = true,
SessionToken = "test-session-token"
};

int originalMaxItemCount = originalOptions.MaxItemCount.Value;
int? originalMaxConcurrency = originalOptions.MaxConcurrency;
bool? originalEnableScanInQuery = originalOptions.EnableScanInQuery;
string originalSessionToken = originalOptions.SessionToken;

// Simulate the copy logic that would happen in CosmosQueryClientCore.ExecuteItemQueryAsync
QueryRequestOptions requestOptionsCopy = new QueryRequestOptions
{
ResponseContinuationTokenLimitInKb = originalOptions.ResponseContinuationTokenLimitInKb,
EnableScanInQuery = originalOptions.EnableScanInQuery,
EnableLowPrecisionOrderBy = originalOptions.EnableLowPrecisionOrderBy,
EnableOptimisticDirectExecution = originalOptions.EnableOptimisticDirectExecution,
MaxBufferedItemCount = originalOptions.MaxBufferedItemCount,
MaxItemCount = originalOptions.MaxItemCount,
MaxConcurrency = originalOptions.MaxConcurrency,
PartitionKey = originalOptions.PartitionKey,
PopulateIndexMetrics = originalOptions.PopulateIndexMetrics,
PopulateQueryAdvice = originalOptions.PopulateQueryAdvice,
ConsistencyLevel = originalOptions.ConsistencyLevel,
SessionToken = originalOptions.SessionToken,
DedicatedGatewayRequestOptions = originalOptions.DedicatedGatewayRequestOptions,
QueryTextMode = originalOptions.QueryTextMode,
// Base RequestOptions properties
IfMatchEtag = originalOptions.IfMatchEtag,
IfNoneMatchEtag = originalOptions.IfNoneMatchEtag,
Properties = originalOptions.Properties,
AddRequestHeaders = originalOptions.AddRequestHeaders,
PriorityLevel = originalOptions.PriorityLevel,
CosmosThresholdOptions = originalOptions.CosmosThresholdOptions,
ExcludeRegions = originalOptions.ExcludeRegions
};

// Simulate the modification that would happen in ExecuteItemQueryAsync
int pageSize = 5;
requestOptionsCopy.MaxItemCount = pageSize;

// Assert: The original QueryRequestOptions should NOT be modified
Assert.AreEqual(originalMaxItemCount, originalOptions.MaxItemCount.Value,
"Original QueryRequestOptions.MaxItemCount should not be modified");
Assert.AreEqual(originalMaxConcurrency, originalOptions.MaxConcurrency,
"Original QueryRequestOptions.MaxConcurrency should not be modified");
Assert.AreEqual(originalEnableScanInQuery, originalOptions.EnableScanInQuery,
"Original QueryRequestOptions.EnableScanInQuery should not be modified");
Assert.AreEqual(originalSessionToken, originalOptions.SessionToken,
"Original QueryRequestOptions.SessionToken should not be modified");

// Assert: The copy should have the new MaxItemCount
Assert.AreEqual(pageSize, requestOptionsCopy.MaxItemCount.Value,
"Copied QueryRequestOptions.MaxItemCount should be updated to pageSize");

// Assert: Other properties should be preserved in the copy
Assert.AreEqual(originalMaxConcurrency, requestOptionsCopy.MaxConcurrency,
"Copied QueryRequestOptions.MaxConcurrency should match original");
Assert.AreEqual(originalEnableScanInQuery, requestOptionsCopy.EnableScanInQuery,
"Copied QueryRequestOptions.EnableScanInQuery should match original");
Assert.AreEqual(originalSessionToken, requestOptionsCopy.SessionToken,
"Copied QueryRequestOptions.SessionToken should match original");
}
}
}
Loading