Exemple #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));
            }
        }
        public async Task ServiceInterop_BadRequestContainsOriginalCosmosException()
        {
            CosmosException          expectedException = new CosmosException("Some message", (HttpStatusCode)429, (int)Documents.SubStatusCodes.Unknown, Guid.NewGuid().ToString(), 0);
            Mock <CosmosQueryClient> queryClient       = new Mock <CosmosQueryClient>();

            queryClient.Setup(c => c.TryGetPartitionedQueryExecutionInfoAsync(
                                  It.IsAny <SqlQuerySpec>(),
                                  It.IsAny <ResourceType>(),
                                  It.IsAny <Documents.PartitionKeyDefinition>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <CancellationToken>())).ReturnsAsync(TryCatch <PartitionedQueryExecutionInfo> .FromException(expectedException));

            Mock <ITrace>   trace           = new Mock <ITrace>();
            CosmosException cosmosException = await Assert.ThrowsExceptionAsync <CosmosException>(() => QueryPlanRetriever.GetQueryPlanWithServiceInteropAsync(
                                                                                                      queryClient.Object,
                                                                                                      new SqlQuerySpec("selectttttt * from c"),
                                                                                                      ResourceType.Document,
                                                                                                      new Documents.PartitionKeyDefinition()
            {
                Paths = new Collection <string>()
                {
                    "/id"
                }
            },
                                                                                                      hasLogicalPartitionKey: false,
                                                                                                      useSystemPrefix: false,
                                                                                                      trace.Object,
                                                                                                      default));

            Assert.AreEqual(expectedException, cosmosException);
        }
        private static async Task <TryCatch <CosmosQueryExecutionContext> > TryCreateCoreContextAsync(
            CosmosQueryContext cosmosQueryContext,
            InputParameters inputParameters,
            CancellationToken cancellationToken)
        {
            // Try to parse the continuation token.
            string continuationToken = inputParameters.InitialUserContinuationToken;
            PartitionedQueryExecutionInfo queryPlanFromContinuationToken = inputParameters.PartitionedQueryExecutionInfo;

            if (continuationToken != null)
            {
                if (!PipelineContinuationToken.TryParse(
                        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.QueryClient,
                        inputParameters.SqlQuerySpec,
                        cosmosQueryContext.ResourceLink,
                        inputParameters.PartitionKey,
                        cancellationToken);
                }
                else
                {
                    //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));
        }
        public async Task ServiceInterop_BadRequestContainsInnerException()
        {
            ExpectedQueryPartitionProviderException innerException = new ExpectedQueryPartitionProviderException("some parsing error");
            Mock <CosmosQueryClient> queryClient = new Mock <CosmosQueryClient>();

            queryClient.Setup(c => c.TryGetPartitionedQueryExecutionInfoAsync(
                                  It.IsAny <SqlQuerySpec>(),
                                  It.IsAny <ResourceType>(),
                                  It.IsAny <Documents.PartitionKeyDefinition>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <bool>(),
                                  It.IsAny <CancellationToken>())).ReturnsAsync(TryCatch <PartitionedQueryExecutionInfo> .FromException(innerException));

            Mock <ITrace>   trace           = new Mock <ITrace>();
            CosmosException cosmosException = await Assert.ThrowsExceptionAsync <CosmosException>(() => QueryPlanRetriever.GetQueryPlanWithServiceInteropAsync(
                                                                                                      queryClient.Object,
                                                                                                      new SqlQuerySpec("selectttttt * from c"),
                                                                                                      ResourceType.Document,
                                                                                                      new Documents.PartitionKeyDefinition()
            {
                Paths = new Collection <string>()
                {
                    "/id"
                }
            },
                                                                                                      hasLogicalPartitionKey: false,
                                                                                                      useSystemPrefix: false,
                                                                                                      trace.Object,
                                                                                                      default));

            Assert.AreEqual(HttpStatusCode.BadRequest, cosmosException.StatusCode);
            Assert.AreEqual(innerException, cosmosException.InnerException);
            Assert.IsNotNull(cosmosException.Trace);
            Assert.IsNotNull(cosmosException.Diagnostics);
        }