Пример #1
0
        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));
            }
        }