private static async Task <TryCatch <CosmosQueryExecutionContext> > TryCreateCoreContextAsync( CosmosQueryContext cosmosQueryContext, InputParameters inputParameters, CancellationToken cancellationToken) { // The default using (cosmosQueryContext.CreateDiagnosticScope("CreateQueryPipeline")) { // Try to parse the continuation token. CosmosElement continuationToken = inputParameters.InitialUserContinuationToken; PartitionedQueryExecutionInfo queryPlanFromContinuationToken = inputParameters.PartitionedQueryExecutionInfo; if (continuationToken != null) { if (!PipelineContinuationToken.TryCreateFromCosmosElement( continuationToken, out PipelineContinuationToken pipelineContinuationToken)) { return(TryCatch <CosmosQueryExecutionContext> .FromException( new MalformedContinuationTokenException( $"Malformed {nameof(PipelineContinuationToken)}: {continuationToken}."))); } if (PipelineContinuationToken.IsTokenFromTheFuture(pipelineContinuationToken)) { return(TryCatch <CosmosQueryExecutionContext> .FromException( new MalformedContinuationTokenException( $"{nameof(PipelineContinuationToken)} Continuation token is from a newer version of the SDK. " + $"Upgrade the SDK to avoid this issue." + $"{continuationToken}."))); } if (!PipelineContinuationToken.TryConvertToLatest( pipelineContinuationToken, out PipelineContinuationTokenV1_1 latestVersionPipelineContinuationToken)) { return(TryCatch <CosmosQueryExecutionContext> .FromException( new MalformedContinuationTokenException( $"{nameof(PipelineContinuationToken)}: '{continuationToken}' is no longer supported."))); } continuationToken = latestVersionPipelineContinuationToken.SourceContinuationToken; if (latestVersionPipelineContinuationToken.QueryPlan != null) { queryPlanFromContinuationToken = latestVersionPipelineContinuationToken.QueryPlan; } } CosmosQueryClient cosmosQueryClient = cosmosQueryContext.QueryClient; ContainerQueryProperties containerQueryProperties = await cosmosQueryClient.GetCachedContainerQueryPropertiesAsync( cosmosQueryContext.ResourceLink, inputParameters.PartitionKey, cancellationToken); cosmosQueryContext.ContainerResourceId = containerQueryProperties.ResourceId; PartitionedQueryExecutionInfo partitionedQueryExecutionInfo; if (inputParameters.ForcePassthrough) { partitionedQueryExecutionInfo = new PartitionedQueryExecutionInfo() { QueryInfo = new QueryInfo() { Aggregates = null, DistinctType = DistinctQueryType.None, GroupByAliases = null, GroupByAliasToAggregateType = null, GroupByExpressions = null, HasSelectValue = false, Limit = null, Offset = null, OrderBy = null, OrderByExpressions = null, RewrittenQuery = null, Top = null, }, QueryRanges = new List <Documents.Routing.Range <string> >(), }; } else if (queryPlanFromContinuationToken != null) { partitionedQueryExecutionInfo = queryPlanFromContinuationToken; } else { // If the query would go to gateway, but we have a partition key, // then try seeing if we can execute as a passthrough using client side only logic. // This is to short circuit the need to go to the gateway to get the query plan. if (cosmosQueryContext.QueryClient.ByPassQueryParsing() && inputParameters.PartitionKey.HasValue) { bool parsed; SqlQuery sqlQuery; using (cosmosQueryContext.CreateDiagnosticScope("QueryParsing")) { parsed = SqlQuery.TryParse(inputParameters.SqlQuerySpec.QueryText, out sqlQuery); } if (parsed) { bool hasDistinct = sqlQuery.SelectClause.HasDistinct; bool hasGroupBy = sqlQuery.GroupByClause != default; bool hasAggregates = AggregateProjectionDetector.HasAggregate(sqlQuery.SelectClause.SelectSpec); bool createPassthroughQuery = !hasAggregates && !hasDistinct && !hasGroupBy; if (createPassthroughQuery) { TestInjections.ResponseStats responseStats = inputParameters?.TestInjections?.Stats; if (responseStats != null) { responseStats.PipelineType = TestInjections.PipelineType.Passthrough; } // Only thing that matters is that we target the correct range. Documents.PartitionKeyDefinition partitionKeyDefinition = GetPartitionKeyDefinition(inputParameters, containerQueryProperties); List <Documents.PartitionKeyRange> targetRanges = await cosmosQueryContext.QueryClient.GetTargetPartitionKeyRangesByEpkStringAsync( cosmosQueryContext.ResourceLink, containerQueryProperties.ResourceId, inputParameters.PartitionKey.Value.InternalKey.GetEffectivePartitionKeyString(partitionKeyDefinition)); return(await CosmosQueryExecutionContextFactory.TryCreatePassthroughQueryExecutionContextAsync( cosmosQueryContext, inputParameters, partitionedQueryExecutionInfo : new PartitionedQueryExecutionInfo(), targetRanges, containerQueryProperties.ResourceId, cancellationToken)); } } } if (cosmosQueryContext.QueryClient.ByPassQueryParsing()) { // For non-Windows platforms(like Linux and OSX) in .NET Core SDK, we cannot use ServiceInterop, so need to bypass in that case. // We are also now bypassing this for 32 bit host process running even on Windows as there are many 32 bit apps that will not work without this partitionedQueryExecutionInfo = await QueryPlanRetriever.GetQueryPlanThroughGatewayAsync( cosmosQueryContext, inputParameters.SqlQuerySpec, cosmosQueryContext.ResourceLink, inputParameters.PartitionKey, cancellationToken); } else { using (cosmosQueryContext.CreateDiagnosticScope("ServiceInterop")) { Documents.PartitionKeyDefinition partitionKeyDefinition = GetPartitionKeyDefinition(inputParameters, containerQueryProperties); partitionedQueryExecutionInfo = await QueryPlanRetriever.GetQueryPlanWithServiceInteropAsync( cosmosQueryContext.QueryClient, inputParameters.SqlQuerySpec, partitionKeyDefinition, inputParameters.PartitionKey != null, cancellationToken); } } } return(await TryCreateFromPartitionedQuerExecutionInfoAsync( partitionedQueryExecutionInfo, containerQueryProperties, cosmosQueryContext, inputParameters, cancellationToken)); } }
private static async Task <TryCatch <CosmosQueryExecutionContext> > TryCreateCoreContextAsync( CosmosQueryContext cosmosQueryContext, InputParameters inputParameters, CancellationToken cancellationToken) { // The default using (cosmosQueryContext.CreateDiagnosticScope("CreateQueryPipeline")) { // Try to parse the continuation token. CosmosElement continuationToken = inputParameters.InitialUserContinuationToken; PartitionedQueryExecutionInfo queryPlanFromContinuationToken = inputParameters.PartitionedQueryExecutionInfo; if (continuationToken != null) { if (!PipelineContinuationToken.TryCreateFromCosmosElement( continuationToken, out PipelineContinuationToken pipelineContinuationToken)) { return(TryCatch <CosmosQueryExecutionContext> .FromException( new MalformedContinuationTokenException( $"Malformed {nameof(PipelineContinuationToken)}: {continuationToken}."))); } if (PipelineContinuationToken.IsTokenFromTheFuture(pipelineContinuationToken)) { return(TryCatch <CosmosQueryExecutionContext> .FromException( new MalformedContinuationTokenException( $"{nameof(PipelineContinuationToken)} Continuation token is from a newer version of the SDK. " + $"Upgrade the SDK to avoid this issue." + $"{continuationToken}."))); } if (!PipelineContinuationToken.TryConvertToLatest( pipelineContinuationToken, out PipelineContinuationTokenV1_1 latestVersionPipelineContinuationToken)) { return(TryCatch <CosmosQueryExecutionContext> .FromException( new MalformedContinuationTokenException( $"{nameof(PipelineContinuationToken)}: '{continuationToken}' is no longer supported."))); } continuationToken = latestVersionPipelineContinuationToken.SourceContinuationToken; if (latestVersionPipelineContinuationToken.QueryPlan != null) { queryPlanFromContinuationToken = latestVersionPipelineContinuationToken.QueryPlan; } } CosmosQueryClient cosmosQueryClient = cosmosQueryContext.QueryClient; ContainerQueryProperties containerQueryProperties = await cosmosQueryClient.GetCachedContainerQueryPropertiesAsync( cosmosQueryContext.ResourceLink, inputParameters.PartitionKey, cancellationToken); cosmosQueryContext.ContainerResourceId = containerQueryProperties.ResourceId; PartitionedQueryExecutionInfo partitionedQueryExecutionInfo; if (queryPlanFromContinuationToken != null) { partitionedQueryExecutionInfo = queryPlanFromContinuationToken; } else { if (cosmosQueryContext.QueryClient.ByPassQueryParsing()) { // For non-Windows platforms(like Linux and OSX) in .NET Core SDK, we cannot use ServiceInterop, so need to bypass in that case. // We are also now bypassing this for 32 bit host process running even on Windows as there are many 32 bit apps that will not work without this partitionedQueryExecutionInfo = await QueryPlanRetriever.GetQueryPlanThroughGatewayAsync( cosmosQueryContext, inputParameters.SqlQuerySpec, cosmosQueryContext.ResourceLink, inputParameters.PartitionKey, cancellationToken); } else { using (cosmosQueryContext.CreateDiagnosticScope("ServiceInterop")) { //todo:elasticcollections this may rely on information from collection cache which is outdated //if collection is deleted/created with same name. //need to make it not rely on information from collection cache. Documents.PartitionKeyDefinition partitionKeyDefinition; if ((inputParameters.Properties != null) && inputParameters.Properties.TryGetValue(InternalPartitionKeyDefinitionProperty, out object partitionKeyDefinitionObject)) { if (partitionKeyDefinitionObject is Documents.PartitionKeyDefinition definition) { partitionKeyDefinition = definition; } else { throw new ArgumentException( "partitionkeydefinition has invalid type", nameof(partitionKeyDefinitionObject)); } } else { partitionKeyDefinition = containerQueryProperties.PartitionKeyDefinition; } partitionedQueryExecutionInfo = await QueryPlanRetriever.GetQueryPlanWithServiceInteropAsync( cosmosQueryContext.QueryClient, inputParameters.SqlQuerySpec, partitionKeyDefinition, inputParameters.PartitionKey != null, cancellationToken); } } } return(await TryCreateFromPartitionedQuerExecutionInfoAsync( partitionedQueryExecutionInfo, containerQueryProperties, cosmosQueryContext, inputParameters, cancellationToken)); } }