Пример #1
0
        public async Task <ChangeFeedEntry> GetChangeFeedLatestAsync(CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper())
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.GetChangeFeedLatest.PopulateCommand(sqlCommandWrapper);

                    using (var reader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        if (await reader.ReadAsync(cancellationToken))
                        {
                            (long rSeq, DateTimeOffset rTimestamp, int rAction, string rStudyInstanceUid, string rSeriesInstanceUid, string rSopInstanceUid, long oWatermark, long?cWatermark) = reader.ReadRow(
                                VLatest.ChangeFeed.Sequence,
                                VLatest.ChangeFeed.Timestamp,
                                VLatest.ChangeFeed.Action,
                                VLatest.ChangeFeed.StudyInstanceUid,
                                VLatest.ChangeFeed.SeriesInstanceUid,
                                VLatest.ChangeFeed.SopInstanceUid,
                                VLatest.ChangeFeed.OriginalWatermark,
                                VLatest.ChangeFeed.CurrentWatermark);

                            return(new ChangeFeedEntry(
                                       rSeq,
                                       rTimestamp,
                                       (ChangeFeedAction)rAction,
                                       rStudyInstanceUid,
                                       rSeriesInstanceUid,
                                       rSopInstanceUid,
                                       oWatermark,
                                       cWatermark,
                                       ConvertWatermarkToCurrentState(oWatermark, cWatermark)));
                        }
                    }
                }

            return(null);
        }
Пример #2
0
        public async Task <long> CreateInstanceIndexAsync(DicomDataset instance, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(instance, nameof(instance));

            await _sqlServerIndexSchema.EnsureInitialized();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionFactoryWrapper.ObtainSqlConnectionWrapper())
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.AddInstance.PopulateCommand(
                        sqlCommandWrapper,
                        instance.GetString(DicomTag.StudyInstanceUID),
                        instance.GetString(DicomTag.SeriesInstanceUID),
                        instance.GetString(DicomTag.SOPInstanceUID),
                        instance.GetSingleValueOrDefault <string>(DicomTag.PatientID),
                        instance.GetSingleValueOrDefault <string>(DicomTag.PatientName),
                        instance.GetSingleValueOrDefault <string>(DicomTag.ReferringPhysicianName),
                        instance.GetStringDateAsDateTime(DicomTag.StudyDate),
                        instance.GetSingleValueOrDefault <string>(DicomTag.StudyDescription),
                        instance.GetSingleValueOrDefault <string>(DicomTag.AccessionNumber),
                        instance.GetSingleValueOrDefault <string>(DicomTag.Modality),
                        instance.GetStringDateAsDateTime(DicomTag.PerformedProcedureStepStartDate),
                        (byte)IndexStatus.Creating);

                    try
                    {
                        return((long)(await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken)));
                    }
                    catch (SqlException ex)
                    {
                        switch (ex.Number)
                        {
                        case SqlErrorCodes.Conflict:
                        {
                            if (ex.State == (byte)IndexStatus.Creating)
                            {
                                throw new PendingInstanceException();
                            }

                            throw new InstanceAlreadyExistsException();
                        }
                        }

                        throw new DataStoreException(ex);
                    }
                }
        }
Пример #3
0
        private async Task <IEnumerable <VersionedInstanceIdentifier> > GetInstanceIdentifierImp(
            string studyInstanceUid,
            CancellationToken cancellationToken,
            string seriesInstanceUid = null,
            string sopInstanceUid    = null)
        {
            var results = new List <VersionedInstanceIdentifier>();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper())
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.GetInstance.PopulateCommand(
                        sqlCommandWrapper,
                        validStatus: (byte)IndexStatus.Created,
                        studyInstanceUid,
                        seriesInstanceUid,
                        sopInstanceUid);

                    using (var reader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        while (await reader.ReadAsync(cancellationToken))
                        {
                            (string rStudyInstanceUid, string rSeriesInstanceUid, string rSopInstanceUid, long watermark) = reader.ReadRow(
                                VLatest.Instance.StudyInstanceUid,
                                VLatest.Instance.SeriesInstanceUid,
                                VLatest.Instance.SopInstanceUid,
                                VLatest.Instance.Watermark);

                            results.Add(new VersionedInstanceIdentifier(
                                            rStudyInstanceUid,
                                            rSeriesInstanceUid,
                                            rSopInstanceUid,
                                            watermark));
                        }
                    }
                }

            return(results);
        }
Пример #4
0
        public async Task <QueryResult> QueryAsync(
            QueryExpression query,
            CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(query, nameof(query));

            var results = new List <VersionedInstanceIdentifier>(query.EvaluatedLimit);

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper())
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    var stringBuilder     = new IndentedStringBuilder(new StringBuilder());
                    var sqlQueryGenerator = new SqlQueryGenerator(stringBuilder, query, new SqlQueryParameterManager(sqlCommandWrapper.Parameters));

                    sqlCommandWrapper.CommandText = stringBuilder.ToString();
                    LogSqlCommand(sqlCommandWrapper);

                    using (var reader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        while (await reader.ReadAsync(cancellationToken))
                        {
                            (string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid, long watermark) = reader.ReadRow(
                                VLatest.Instance.StudyInstanceUid,
                                VLatest.Instance.SeriesInstanceUid,
                                VLatest.Instance.SopInstanceUid,
                                VLatest.Instance.Watermark);

                            results.Add(new VersionedInstanceIdentifier(
                                            studyInstanceUid,
                                            seriesInstanceUid,
                                            sopInstanceUid,
                                            watermark));
                        }
                    }
                }

            return(new QueryResult(results));
        }
Пример #5
0
        private async Task <SearchResult> SearchImpl(SearchOptions searchOptions, bool historySearch, CancellationToken cancellationToken)
        {
            await _model.EnsureInitialized();

            Expression searchExpression = searchOptions.Expression;

            // AND in the continuation token
            if (!string.IsNullOrWhiteSpace(searchOptions.ContinuationToken) && !searchOptions.CountOnly)
            {
                if (long.TryParse(searchOptions.ContinuationToken, NumberStyles.None, CultureInfo.InvariantCulture, out var token))
                {
                    var tokenExpression = Expression.SearchParameter(SqlSearchParameters.ResourceSurrogateIdParameter, Expression.GreaterThan(SqlFieldName.ResourceSurrogateId, null, token));
                    searchExpression = searchExpression == null ? tokenExpression : (Expression)Expression.And(tokenExpression, searchExpression);
                }
                else
                {
                    throw new BadRequestException(Resources.InvalidContinuationToken);
                }
            }

            SqlRootExpression expression = (SqlRootExpression)searchExpression
                                           ?.AcceptVisitor(LastUpdatedToResourceSurrogateIdRewriter.Instance)
                                           .AcceptVisitor(DateTimeEqualityRewriter.Instance)
                                           .AcceptVisitor(FlatteningRewriter.Instance)
                                           .AcceptVisitor(_sqlRootExpressionRewriter)
                                           .AcceptVisitor(DenormalizedPredicateRewriter.Instance)
                                           .AcceptVisitor(NormalizedPredicateReorderer.Instance)
                                           .AcceptVisitor(_chainFlatteningRewriter)
                                           .AcceptVisitor(DateTimeBoundedRangeRewriter.Instance)
                                           .AcceptVisitor(_stringOverflowRewriter)
                                           .AcceptVisitor(NumericRangeRewriter.Instance)
                                           .AcceptVisitor(MissingSearchParamVisitor.Instance)
                                           .AcceptVisitor(IncludeDenormalizedRewriter.Instance)
                                           .AcceptVisitor(TopRewriter.Instance, searchOptions)
                                           .AcceptVisitor(IncludeRewriter.Instance)
                                           ?? SqlRootExpression.WithDenormalizedExpressions();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    var stringBuilder = new IndentedStringBuilder(new StringBuilder());

                    EnableTimeAndIoMessageLogging(stringBuilder, sqlConnectionWrapper);

                    var queryGenerator = new SqlQueryGenerator(stringBuilder, new SqlQueryParameterManager(sqlCommandWrapper.Parameters), _model, historySearch);

                    expression.AcceptVisitor(queryGenerator, searchOptions);

                    sqlCommandWrapper.CommandText = stringBuilder.ToString();

                    LogSqlCommand(sqlCommandWrapper);

                    using (var reader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        if (searchOptions.CountOnly)
                        {
                            await reader.ReadAsync(cancellationToken);

                            return(new SearchResult(reader.GetInt32(0), searchOptions.UnsupportedSearchParams));
                        }

                        var  resources         = new List <SearchResultEntry>(searchOptions.MaxItemCount);
                        long?newContinuationId = null;
                        bool moreResults       = false;
                        int  matchCount        = 0;

                        while (await reader.ReadAsync(cancellationToken))
                        {
                            (short resourceTypeId, string resourceId, int version, bool isDeleted, long resourceSurrogateId, string requestMethod, bool isMatch, Stream rawResourceStream) = reader.ReadRow(
                                VLatest.Resource.ResourceTypeId,
                                VLatest.Resource.ResourceId,
                                VLatest.Resource.Version,
                                VLatest.Resource.IsDeleted,
                                VLatest.Resource.ResourceSurrogateId,
                                VLatest.Resource.RequestMethod,
                                _isMatch,
                                VLatest.Resource.RawResource);

                            // If we get to this point, we know there are more results so we need a continuation token
                            // Additionally, this resource shouldn't be included in the results
                            if (matchCount >= searchOptions.MaxItemCount && isMatch)
                            {
                                moreResults = true;
                                continue;
                            }

                            // See if this resource is a continuation token candidate and increase the count
                            if (isMatch)
                            {
                                newContinuationId = resourceSurrogateId;
                                matchCount++;
                            }

                            string rawResource;

                            using (rawResourceStream)
                                using (var gzipStream = new GZipStream(rawResourceStream, CompressionMode.Decompress))
                                    using (var streamReader = new StreamReader(gzipStream, SqlServerFhirDataStore.ResourceEncoding))
                                    {
                                        rawResource = await streamReader.ReadToEndAsync();
                                    }

                            resources.Add(new SearchResultEntry(
                                              new ResourceWrapper(
                                                  resourceId,
                                                  version.ToString(CultureInfo.InvariantCulture),
                                                  _model.GetResourceTypeName(resourceTypeId),
                                                  new RawResource(rawResource, FhirResourceFormat.Json),
                                                  new ResourceRequest(requestMethod),
                                                  new DateTimeOffset(ResourceSurrogateIdHelper.ResourceSurrogateIdToLastUpdated(resourceSurrogateId), TimeSpan.Zero),
                                                  isDeleted,
                                                  null,
                                                  null,
                                                  null),
                                              isMatch ? SearchEntryMode.Match : SearchEntryMode.Include));
                        }

                        // call NextResultAsync to get the info messages
                        await reader.NextResultAsync(cancellationToken);

                        IReadOnlyList <(string parameterName, string reason)> unsupportedSortingParameters;
                        if (searchOptions.Sort?.Count > 0)
                        {
                            // we don't currently support sort
                            unsupportedSortingParameters = searchOptions.UnsupportedSortingParams.Concat(searchOptions.Sort.Select(s => (s.searchParameterInfo.Name, Core.Resources.SortNotSupported))).ToList();
                        }
                        else
                        {
                            unsupportedSortingParameters = searchOptions.UnsupportedSortingParams;
                        }

                        return(new SearchResult(resources, searchOptions.UnsupportedSearchParams, unsupportedSortingParameters, moreResults ? newContinuationId.Value.ToString(CultureInfo.InvariantCulture) : null));
                    }
                }
        }
        private async Task <SearchResult> SearchImpl(SearchOptions searchOptions, bool historySearch, CancellationToken cancellationToken)
        {
            Expression searchExpression = searchOptions.Expression;

            // AND in the continuation token
            if (!string.IsNullOrWhiteSpace(searchOptions.ContinuationToken) && !searchOptions.CountOnly)
            {
                var continuationToken = ContinuationToken.FromString(searchOptions.ContinuationToken);
                if (continuationToken != null)
                {
                    // in case it's a _lastUpdated sort optimization
                    if (string.IsNullOrEmpty(continuationToken.SortValue))
                    {
                        (SearchParameterInfo searchParamInfo, SortOrder sortOrder) = searchOptions.GetFirstSupportedSortParam();

                        Expression lastUpdatedExpression = sortOrder == SortOrder.Ascending ? Expression.GreaterThan(SqlFieldName.ResourceSurrogateId, null, continuationToken.ResourceSurrogateId)
                                                                                                    : Expression.LessThan(SqlFieldName.ResourceSurrogateId, null, continuationToken.ResourceSurrogateId);

                        var tokenExpression = Expression.SearchParameter(SqlSearchParameters.ResourceSurrogateIdParameter, lastUpdatedExpression);
                        searchExpression = searchExpression == null ? tokenExpression : (Expression)Expression.And(tokenExpression, searchExpression);
                    }
                }
                else
                {
                    throw new BadRequestException(Resources.InvalidContinuationToken);
                }
            }

            if (searchOptions.CountOnly)
            {
                // if we're only returning a count, discard any _include parameters since included resources are not counted.
                searchExpression = searchExpression?.AcceptVisitor(RemoveIncludesRewriter.Instance);
            }

            SqlRootExpression expression = (SqlRootExpression)searchExpression
                                           ?.AcceptVisitor(LastUpdatedToResourceSurrogateIdRewriter.Instance)
                                           .AcceptVisitor(DateTimeEqualityRewriter.Instance)
                                           .AcceptVisitor(FlatteningRewriter.Instance)
                                           .AcceptVisitor(_sqlRootExpressionRewriter)
                                           .AcceptVisitor(_sortRewriter, searchOptions)
                                           .AcceptVisitor(DenormalizedPredicateRewriter.Instance)
                                           .AcceptVisitor(NormalizedPredicateReorderer.Instance)
                                           .AcceptVisitor(_chainFlatteningRewriter)
                                           .AcceptVisitor(DateTimeBoundedRangeRewriter.Instance)
                                           .AcceptVisitor(_stringOverflowRewriter)
                                           .AcceptVisitor(NumericRangeRewriter.Instance)
                                           .AcceptVisitor(MissingSearchParamVisitor.Instance)
                                           .AcceptVisitor(IncludeDenormalizedRewriter.Instance)
                                           .AcceptVisitor(TopRewriter.Instance, searchOptions)
                                           .AcceptVisitor(IncludeRewriter.Instance)
                                           ?? SqlRootExpression.WithDenormalizedExpressions();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    var stringBuilder = new IndentedStringBuilder(new StringBuilder());

                    EnableTimeAndIoMessageLogging(stringBuilder, sqlConnectionWrapper);

                    var queryGenerator = new SqlQueryGenerator(stringBuilder, new SqlQueryParameterManager(sqlCommandWrapper.Parameters), _model, historySearch, _schemaInformation);

                    expression.AcceptVisitor(queryGenerator, searchOptions);

                    sqlCommandWrapper.CommandText = stringBuilder.ToString();

                    LogSqlCommand(sqlCommandWrapper);

                    using (var reader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        if (searchOptions.CountOnly)
                        {
                            await reader.ReadAsync(cancellationToken);

                            return(new SearchResult(reader.GetInt32(0), searchOptions.UnsupportedSearchParams));
                        }

                        var  resources         = new List <SearchResultEntry>(searchOptions.MaxItemCount);
                        long?newContinuationId = null;
                        bool moreResults       = false;
                        int  matchCount        = 0;

                        // Currently we support only date time sort type.
                        DateTime?sortValue = null;

                        while (await reader.ReadAsync(cancellationToken))
                        {
                            (short resourceTypeId, string resourceId, int version, bool isDeleted, long resourceSurrogateId, string requestMethod, bool isMatch, bool isPartialEntry, bool isRawResourceMetaSet, Stream rawResourceStream) = reader.ReadRow(
                                VLatest.Resource.ResourceTypeId,
                                VLatest.Resource.ResourceId,
                                VLatest.Resource.Version,
                                VLatest.Resource.IsDeleted,
                                VLatest.Resource.ResourceSurrogateId,
                                VLatest.Resource.RequestMethod,
                                _isMatch,
                                _isPartial,
                                VLatest.Resource.IsRawResourceMetaSet,
                                VLatest.Resource.RawResource);

                            // If we get to this point, we know there are more results so we need a continuation token
                            // Additionally, this resource shouldn't be included in the results
                            if (matchCount >= searchOptions.MaxItemCount && isMatch)
                            {
                                moreResults = true;

                                // At this point we are at the last row.
                                // if we have more columns, it means sort expressions were added.
                                if (reader.FieldCount > 10)
                                {
                                    sortValue = reader.GetValue(SortValueColumnName) as DateTime?;
                                }

                                continue;
                            }

                            // See if this resource is a continuation token candidate and increase the count
                            if (isMatch)
                            {
                                newContinuationId = resourceSurrogateId;
                                matchCount++;
                            }

                            string rawResource;

                            using (rawResourceStream)
                                using (var gzipStream = new GZipStream(rawResourceStream, CompressionMode.Decompress))
                                    using (var streamReader = new StreamReader(gzipStream, SqlServerFhirDataStore.ResourceEncoding))
                                    {
                                        rawResource = await streamReader.ReadToEndAsync();
                                    }

                            // as long as at least one entry was marked as partial, this resultset
                            // should be marked as partial
                            _isResultPartial = _isResultPartial || isPartialEntry;

                            resources.Add(new SearchResultEntry(
                                              new ResourceWrapper(
                                                  resourceId,
                                                  version.ToString(CultureInfo.InvariantCulture),
                                                  _model.GetResourceTypeName(resourceTypeId),
                                                  new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: isRawResourceMetaSet),
                                                  new ResourceRequest(requestMethod),
                                                  new DateTimeOffset(ResourceSurrogateIdHelper.ResourceSurrogateIdToLastUpdated(resourceSurrogateId), TimeSpan.Zero),
                                                  isDeleted,
                                                  null,
                                                  null,
                                                  null),
                                              isMatch ? SearchEntryMode.Match : SearchEntryMode.Include));
                        }

                        // call NextResultAsync to get the info messages
                        await reader.NextResultAsync(cancellationToken);

                        IReadOnlyList <(string parameterName, string reason)> unsupportedSortingParameters;
                        if (searchOptions.Sort?.Count > 0)
                        {
                            unsupportedSortingParameters = searchOptions
                                                           .UnsupportedSortingParams
                                                           .Concat(searchOptions.Sort
                                                                   .Where(x => !x.searchParameterInfo.IsSortSupported())
                                                                   .Select(s => (s.searchParameterInfo.Name, Core.Resources.SortNotSupported))).ToList();
                        }
                        else
                        {
                            unsupportedSortingParameters = searchOptions.UnsupportedSortingParams;
                        }

                        // Continuation token prep
                        ContinuationToken continuationToken = null;
                        if (moreResults)
                        {
                            if (sortValue.HasValue)
                            {
                                continuationToken = new ContinuationToken(new object[]
                                {
                                    sortValue.Value.ToString("o"),
                                    newContinuationId ?? 0,
                                });
                            }
                            else
                            {
                                continuationToken = new ContinuationToken(new object[]
                                {
                                    newContinuationId ?? 0,
                                });
                            }
                        }

                        return(new SearchResult(resources, searchOptions.UnsupportedSearchParams, unsupportedSortingParameters, continuationToken?.ToJson(), _isResultPartial));
                    }
                }
        }