Beispiel #1
0
        public async Task <TryCatch <PartitionedQueryExecutionInfo> > TryGetQueryPlanAsync(
            SqlQuerySpec sqlQuerySpec,
            PartitionKeyDefinition partitionKeyDefinition,
            QueryFeatures supportedQueryFeatures,
            bool hasLogicalPartitionKey,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (sqlQuerySpec == null)
            {
                throw new ArgumentNullException($"{nameof(sqlQuerySpec)}");
            }

            if (partitionKeyDefinition == null)
            {
                throw new ArgumentNullException($"{nameof(partitionKeyDefinition)}");
            }

            TryCatch <PartitionedQueryExecutionInfo> tryGetQueryInfo = await this.TryGetQueryInfoAsync(
                sqlQuerySpec,
                partitionKeyDefinition,
                hasLogicalPartitionKey,
                cancellationToken);

            if (!tryGetQueryInfo.Succeeded)
            {
                return(tryGetQueryInfo);
            }

            if (QueryPlanExceptionFactory.TryGetUnsupportedException(
                    tryGetQueryInfo.Result.QueryInfo,
                    supportedQueryFeatures,
                    out Exception queryPlanHandlerException))
            {
                return(TryCatch <PartitionedQueryExecutionInfo> .FromException(queryPlanHandlerException));
            }

            return(tryGetQueryInfo);
        }
Beispiel #2
0
            private static void AddExceptionsForAggregateQueries(
                QueryInfo queryInfo,
                QueryFeatures supportedQueryFeatures,
                Lazy <List <Exception> > exceptions)
            {
                if (queryInfo.HasAggregates)
                {
                    bool isSingleAggregate = (queryInfo.Aggregates.Length == 1) ||
                                             (queryInfo.GroupByAliasToAggregateType.Values.Where(aggregateOperator => aggregateOperator.HasValue).Count() == 1);
                    if (isSingleAggregate)
                    {
                        if (queryInfo.HasSelectValue)
                        {
                            if (!supportedQueryFeatures.HasFlag(QueryFeatures.Aggregate))
                            {
                                exceptions.Value.Add(QueryContainsUnsupportedAggregates);
                            }
                        }
                        else
                        {
                            if (!supportedQueryFeatures.HasFlag(QueryFeatures.NonValueAggregate))
                            {
                                exceptions.Value.Add(QueryContainsUnsupportedNonValueAggregate);
                            }
                        }
                    }
                    else
                    {
                        if (!supportedQueryFeatures.HasFlag(QueryFeatures.NonValueAggregate))
                        {
                            exceptions.Value.Add(QueryContainsUnsupportedNonValueAggregate);
                        }

                        if (!supportedQueryFeatures.HasFlag(QueryFeatures.MultipleAggregates))
                        {
                            exceptions.Value.Add(QueryContainsUnsupportedMultipleAggregates);
                        }
                    }
                }
            }
Beispiel #3
0
        /// <summary>
        /// Used in the compute gateway to support legacy gateways query execution pattern.
        /// </summary>
        public async Task <TryCatch <(PartitionedQueryExecutionInfo queryPlan, bool supported)> > TryGetQueryInfoAndIfSupportedAsync(
            QueryFeatures supportedQueryFeatures,
            SqlQuerySpec sqlQuerySpec,
            Documents.ResourceType resourceType,
            PartitionKeyDefinition partitionKeyDefinition,
            bool hasLogicalPartitionKey,
            bool useSystemPrefix,
            CancellationToken cancellationToken = default)
        {
            if (sqlQuerySpec == null)
            {
                throw new ArgumentNullException(nameof(sqlQuerySpec));
            }

            if (partitionKeyDefinition == null)
            {
                throw new ArgumentNullException(nameof(partitionKeyDefinition));
            }

            cancellationToken.ThrowIfCancellationRequested();

            TryCatch <PartitionedQueryExecutionInfo> tryGetQueryInfo = await this.TryGetQueryInfoAsync(
                sqlQuerySpec,
                resourceType,
                partitionKeyDefinition,
                hasLogicalPartitionKey,
                useSystemPrefix,
                cancellationToken);

            if (tryGetQueryInfo.Failed)
            {
                return(TryCatch <(PartitionedQueryExecutionInfo, bool)> .FromException(tryGetQueryInfo.Exception));
            }

            QueryFeatures neededQueryFeatures = QueryPlanSupportChecker.GetNeededQueryFeatures(
                tryGetQueryInfo.Result.QueryInfo,
                supportedQueryFeatures);

            return(TryCatch <(PartitionedQueryExecutionInfo, bool)> .FromResult((tryGetQueryInfo.Result, neededQueryFeatures == QueryFeatures.None)));
        }
Beispiel #4
0
 private static void AddExceptionsForOrderByQueries(
     QueryInfo queryInfo,
     QueryFeatures supportedQueryFeatures,
     Lazy <List <Exception> > exceptions)
 {
     if (queryInfo.HasOrderBy)
     {
         if (queryInfo.OrderByExpressions.Length == 1)
         {
             if (!supportedQueryFeatures.HasFlag(QueryFeatures.OrderBy))
             {
                 exceptions.Value.Add(QueryContainsUnsupportedOrderBy);
             }
         }
         else
         {
             if (!supportedQueryFeatures.HasFlag(QueryFeatures.MultipleOrderBy))
             {
                 exceptions.Value.Add(QueryContainsUnsupportedMultipleOrderBy);
             }
         }
     }
 }
Beispiel #5
0
            public static void ThrowIfNotSupported(
                QueryInfo queryInfo,
                QueryFeatures supportedQueryFeatures)
            {
                QueryFeatures neededQueryFeatures = QueryPlanSupportChecker.GetNeededQueryFeatures(
                    queryInfo,
                    supportedQueryFeatures);

                if (neededQueryFeatures != QueryFeatures.None)
                {
                    List <Exception> queryPlanHandlerExceptions = new List <Exception>();
                    foreach (QueryFeatures queryFeature in QueryPlanExceptionFactory.QueryFeatureList)
                    {
                        if ((neededQueryFeatures & queryFeature) == queryFeature)
                        {
                            Exception unsupportedFeatureException = QueryPlanExceptionFactory.FeatureToUnsupportedException[queryFeature];
                            queryPlanHandlerExceptions.Add(unsupportedFeatureException);
                        }
                    }

                    throw new QueryPlanHandlerException(queryPlanHandlerExceptions);
                }
            }
Beispiel #6
0
        /// <summary>
        /// Used in the compute gateway to support legacy gateways query execution pattern.
        /// </summary>
        public async Task <((Exception, PartitionedQueryExecutionInfo), bool)> TryGetQueryInfoAndIfSupportedAsync(
            QueryFeatures supportedQueryFeatures,
            SqlQuerySpec sqlQuerySpec,
            PartitionKeyDefinition partitionKeyDefinition,
            bool hasLogicalPartitionKey,
            CancellationToken cancellationToken = default)
        {
            if (sqlQuerySpec == null)
            {
                throw new ArgumentNullException(nameof(sqlQuerySpec));
            }

            if (partitionKeyDefinition == null)
            {
                throw new ArgumentNullException(nameof(partitionKeyDefinition));
            }

            cancellationToken.ThrowIfCancellationRequested();

            TryCatch <PartitionedQueryExecutionInfo> tryGetQueryInfo = await this.TryGetQueryInfoAsync(
                sqlQuerySpec,
                partitionKeyDefinition,
                hasLogicalPartitionKey,
                cancellationToken);

            if (!tryGetQueryInfo.Succeeded)
            {
                // Failed to get QueryInfo, so vacously not supported.
                return((tryGetQueryInfo.Exception, null), false);
            }

            QueryFeatures neededQueryFeatures = QueryPlanSupportChecker.GetNeededQueryFeatures(
                tryGetQueryInfo.Result.QueryInfo,
                supportedQueryFeatures);

            return((null, tryGetQueryInfo.Result), neededQueryFeatures == QueryFeatures.None);
        }
Beispiel #7
0
            public static void ThrowIfNotSupported(
                QueryInfo queryInfo,
                QueryFeatures supportedQueryFeatures)
            {
                Lazy <List <Exception> > exceptions = new Lazy <List <Exception> >(() => { return(new List <Exception>()); });

                QueryPlanExceptionFactory.AddExceptionsForAggregateQueries(
                    queryInfo,
                    supportedQueryFeatures,
                    exceptions);

                QueryPlanExceptionFactory.AddExceptionsForDistinctQueries(
                    queryInfo,
                    supportedQueryFeatures,
                    exceptions);

                QueryPlanExceptionFactory.AddExceptionsForTopQueries(
                    queryInfo,
                    supportedQueryFeatures,
                    exceptions);

                QueryPlanExceptionFactory.AddExceptionsForOrderByQueries(
                    queryInfo,
                    supportedQueryFeatures,
                    exceptions);

                QueryPlanExceptionFactory.AddExceptionsForOffsetLimitQueries(
                    queryInfo,
                    supportedQueryFeatures,
                    exceptions);

                if (exceptions.IsValueCreated)
                {
                    throw new QueryPlanHandlerException(exceptions.Value);
                }
            }
        /// <summary>
        /// Used in the compute gateway to support legacy gateway interface.
        /// </summary>
        public override async Task <TryExecuteQueryResult> TryExecuteQueryAsync(
            QueryFeatures supportedQueryFeatures,
            QueryDefinition queryDefinition,
            string continuationToken,
            FeedRangeInternal feedRangeInternal,
            QueryRequestOptions requestOptions,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (queryDefinition == null)
            {
                throw new ArgumentNullException(nameof(queryDefinition));
            }

            if (requestOptions == null)
            {
                throw new ArgumentNullException(nameof(requestOptions));
            }

            if (feedRangeInternal != null)
            {
                // The user has scoped down to a physical partition or logical partition.
                // In either case let the query execute as a passthrough.
                QueryIterator passthroughQueryIterator = QueryIterator.Create(
                    client: this.queryClient,
                    clientContext: this.ClientContext,
                    sqlQuerySpec: queryDefinition.ToSqlQuerySpec(),
                    continuationToken: continuationToken,
                    feedRangeInternal: feedRangeInternal,
                    queryRequestOptions: requestOptions,
                    resourceLink: this.LinkUri,
                    isContinuationExpected: false,
                    allowNonValueAggregateQuery: true,
                    forcePassthrough: true, // Forcing a passthrough, since we don't want to get the query plan nor try to rewrite it.
                    partitionedQueryExecutionInfo: null);

                return(new QueryPlanIsSupportedResult(passthroughQueryIterator));
            }

            cancellationToken.ThrowIfCancellationRequested();

            Documents.PartitionKeyDefinition partitionKeyDefinition;
            if (requestOptions.Properties != null &&
                requestOptions.Properties.TryGetValue("x-ms-query-partitionkey-definition", out object partitionKeyDefinitionObject))
            {
                if (partitionKeyDefinitionObject is Documents.PartitionKeyDefinition definition)
                {
                    partitionKeyDefinition = definition;
                }
                else
                {
                    throw new ArgumentException(
                              "partitionkeydefinition has invalid type",
                              nameof(partitionKeyDefinitionObject));
                }
            }
            else
            {
                ContainerQueryProperties containerQueryProperties = await this.queryClient.GetCachedContainerQueryPropertiesAsync(
                    this.LinkUri,
                    requestOptions.PartitionKey,
                    cancellationToken);

                partitionKeyDefinition = containerQueryProperties.PartitionKeyDefinition;
            }

            QueryPlanHandler queryPlanHandler = new QueryPlanHandler(this.queryClient);

            TryCatch <(PartitionedQueryExecutionInfo queryPlan, bool supported)> tryGetQueryInfoAndIfSupported = await queryPlanHandler.TryGetQueryInfoAndIfSupportedAsync(
                supportedQueryFeatures,
                queryDefinition.ToSqlQuerySpec(),
                partitionKeyDefinition,
                requestOptions.PartitionKey.HasValue,
                cancellationToken);

            if (tryGetQueryInfoAndIfSupported.Failed)
            {
                return(new FailedToGetQueryPlanResult(tryGetQueryInfoAndIfSupported.Exception));
            }

            (PartitionedQueryExecutionInfo queryPlan, bool supported) = tryGetQueryInfoAndIfSupported.Result;
            TryExecuteQueryResult tryExecuteQueryResult;

            if (supported)
            {
                QueryIterator queryIterator = QueryIterator.Create(
                    client: this.queryClient,
                    clientContext: this.ClientContext,
                    sqlQuerySpec: queryDefinition.ToSqlQuerySpec(),
                    continuationToken: continuationToken,
                    feedRangeInternal: feedRangeInternal,
                    queryRequestOptions: requestOptions,
                    resourceLink: this.LinkUri,
                    isContinuationExpected: false,
                    allowNonValueAggregateQuery: true,
                    forcePassthrough: false,
                    partitionedQueryExecutionInfo: queryPlan);

                tryExecuteQueryResult = new QueryPlanIsSupportedResult(queryIterator);
            }
            else
            {
                tryExecuteQueryResult = new QueryPlanNotSupportedResult(queryPlan);
            }

            return(tryExecuteQueryResult);
        }
        /// <summary>
        /// Used in the compute gateway to support legacy gateway interface.
        /// </summary>
        internal async Task <((Exception, PartitionedQueryExecutionInfo), (bool, QueryIterator))> TryExecuteQueryAsync(
            QueryFeatures supportedQueryFeatures,
            QueryDefinition queryDefinition,
            string continuationToken,
            QueryRequestOptions requestOptions,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (queryDefinition == null)
            {
                throw new ArgumentNullException(nameof(queryDefinition));
            }

            if (requestOptions == null)
            {
                throw new ArgumentNullException(nameof(requestOptions));
            }

            cancellationToken.ThrowIfCancellationRequested();

            Documents.PartitionKeyDefinition partitionKeyDefinition;
            if (requestOptions.Properties != null &&
                requestOptions.Properties.TryGetValue("x-ms-query-partitionkey-definition", out object partitionKeyDefinitionObject))
            {
                if (partitionKeyDefinitionObject is Documents.PartitionKeyDefinition definition)
                {
                    partitionKeyDefinition = definition;
                }
                else
                {
                    throw new ArgumentException(
                              "partitionkeydefinition has invalid type",
                              nameof(partitionKeyDefinitionObject));
                }
            }
            else
            {
                ContainerQueryProperties containerQueryProperties = await this.queryClient.GetCachedContainerQueryPropertiesAsync(
                    this.LinkUri,
                    requestOptions.PartitionKey,
                    cancellationToken);

                partitionKeyDefinition = containerQueryProperties.PartitionKeyDefinition;
            }

            QueryPlanHandler queryPlanHandler = new QueryPlanHandler(this.queryClient);

            ((Exception exception, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo), bool supported) = await queryPlanHandler.TryGetQueryInfoAndIfSupportedAsync(
                supportedQueryFeatures,
                queryDefinition.ToSqlQuerySpec(),
                partitionKeyDefinition,
                requestOptions.PartitionKey.HasValue,
                cancellationToken);

            if (exception != null)
            {
                return((exception, null), (false, null));
            }

            QueryIterator queryIterator;

            if (supported)
            {
                queryIterator = QueryIterator.Create(
                    client: this.queryClient,
                    clientContext: this.ClientContext,
                    sqlQuerySpec: queryDefinition.ToSqlQuerySpec(),
                    continuationToken: continuationToken,
                    queryRequestOptions: requestOptions,
                    resourceLink: this.LinkUri,
                    isContinuationExpected: false,
                    allowNonValueAggregateQuery: true,
                    partitionedQueryExecutionInfo: partitionedQueryExecutionInfo);
            }
            else
            {
                queryIterator = null;
            }

            return((null, partitionedQueryExecutionInfo), (supported, queryIterator));
        }
Beispiel #10
0
 /// <summary>
 /// Creates a <see cref="NotSupportedException"/> with message that says that <paramref name="feature"/>
 /// is not supported by current storage.
 /// </summary>
 /// <param name="feature">The feature.</param>
 /// <returns>Created exception.</returns>
 public static NotSupportedException NotSupported(QueryFeatures feature)
 {
     return(NotSupported(feature.ToString()));
 }
 /// <summary>
 /// Determines whether the specified active features is supported.
 /// </summary>
 public static bool Supports(this QueryFeatures available, QueryFeatures required)
 {
     return((available & required) == required);
 }