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); }
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); } } }
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); }
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)); }
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)); } } }