Beispiel #1
0
        public async Task <int> IncrementDeletedInstanceRetryAsync(VersionedInstanceIdentifier versionedInstanceIdentifier, DateTimeOffset cleanupAfter, CancellationToken cancellationToken = default)
        {
            await _sqlServerIndexSchema.EnsureInitialized();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionFactoryWrapper.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.IncrementDeletedInstanceRetry.PopulateCommand(
                        sqlCommandWrapper,
                        versionedInstanceIdentifier.StudyInstanceUid,
                        versionedInstanceIdentifier.SeriesInstanceUid,
                        versionedInstanceIdentifier.SopInstanceUid,
                        versionedInstanceIdentifier.Version,
                        cleanupAfter);

                    try
                    {
                        return((int)(await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken)));
                    }
                    catch (SqlException ex)
                    {
                        throw new DataStoreException(ex);
                    }
                }
        }
        public async Task <UpsertOutcome> UpsertAsync(ResourceWrapper resource, WeakETag weakETag, bool allowCreate, bool keepHistory, CancellationToken cancellationToken)
        {
            int?eTag = weakETag == null
                ? (int?)null
                : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.

            var resourceMetadata = new ResourceMetadata(
                resource.CompartmentIndices,
                resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)),
                resource.LastModifiedClaims);

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                    using (var stream = new RecyclableMemoryStream(_memoryStreamManager))
                    {
                        CompressedRawResourceConverter.WriteCompressedRawResource(stream, resource.RawResource.Data);

                        stream.Seek(0, 0);

                        PopulateUpsertResourceCommand(sqlCommandWrapper, resource, resourceMetadata, allowCreate, keepHistory, eTag, stream);

                        try
                        {
                            var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                            if (newVersion == null)
                            {
                                // indicates a redundant delete
                                return(null);
                            }

                            resource.Version = newVersion.ToString();

                            return(new UpsertOutcome(resource, newVersion == 1 ? SaveOutcomeType.Created : SaveOutcomeType.Updated));
                        }
                        catch (SqlException e)
                        {
                            switch (e.Number)
                            {
                            case SqlErrorCodes.PreconditionFailed:
                                throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag?.VersionId));

                            case SqlErrorCodes.NotFound:
                                if (weakETag != null)
                                {
                                    throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId));
                                }

                                goto default;

                            case SqlErrorCodes.MethodNotAllowed:
                                throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed);

                            default:
                                _logger.LogError(e, "Error from SQL database on upsert");
                                throw;
                            }
                        }
                    }
        }
        public override async Task <long> BeginCreateInstanceIndexAsync(int partitionKey, DicomDataset instance, IEnumerable <QueryTag> queryTags, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(instance, nameof(instance));
            EnsureArg.IsNotNull(queryTags, nameof(queryTags));

            using (SqlConnectionWrapper sqlConnectionWrapper = await SqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    var rows = ExtendedQueryTagDataRowsBuilder.Build(instance, queryTags.Where(tag => tag.IsExtendedQueryTag), Version);
                    VLatest.AddInstanceV6TableValuedParameters parameters = new VLatest.AddInstanceV6TableValuedParameters(
                        rows.StringRows,
                        rows.LongRows,
                        rows.DoubleRows,
                        rows.DateTimeWithUtcRows,
                        rows.PersonNameRows
                        );

                    VLatest.AddInstanceV6.PopulateCommand(
                        sqlCommandWrapper,
                        partitionKey,
                        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.GetStringDateAsDate(DicomTag.StudyDate),
                        instance.GetSingleValueOrDefault <string>(DicomTag.StudyDescription),
                        instance.GetSingleValueOrDefault <string>(DicomTag.AccessionNumber),
                        instance.GetSingleValueOrDefault <string>(DicomTag.Modality),
                        instance.GetStringDateAsDate(DicomTag.PerformedProcedureStepStartDate),
                        instance.GetStringDateAsDate(DicomTag.PatientBirthDate),
                        instance.GetSingleValueOrDefault <string>(DicomTag.ManufacturerModelName),
                        (byte)IndexStatus.Creating,
                        parameters);

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

                            throw new InstanceAlreadyExistsException();
                        }

                        throw new DataStoreException(ex);
                    }
                }
        }
Beispiel #4
0
        public async Task <ResourceWrapper> UpdateSearchParameterIndicesAsync(ResourceWrapper resource, WeakETag weakETag, CancellationToken cancellationToken)
        {
            int?eTag = weakETag == null
                ? null
                : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    if (_schemaInformation.Current >= SchemaVersionConstants.AddMinMaxForDateAndStringSearchParamVersion)
                    {
                        VLatest.ReindexResource.PopulateCommand(
                            sqlCommandWrapper,
                            resourceTypeId: _model.GetResourceTypeId(resource.ResourceTypeName),
                            resourceId: resource.ResourceId,
                            eTag,
                            searchParamHash: resource.SearchParameterHash,
                            tableValuedParameters: _reindexResourceTvpGeneratorVLatest.Generate(new List <ResourceWrapper> {
                            resource
                        }));
                    }
                    else
                    {
                        V17.ReindexResource.PopulateCommand(
                            sqlCommandWrapper,
                            resourceTypeId: _model.GetResourceTypeId(resource.ResourceTypeName),
                            resourceId: resource.ResourceId,
                            eTag,
                            searchParamHash: resource.SearchParameterHash,
                            tableValuedParameters: _reindexResourceTvpGeneratorV17.Generate(new List <ResourceWrapper> {
                            resource
                        }));
                    }

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                        return(resource);
                    }
                    catch (SqlException e)
                    {
                        switch (e.Number)
                        {
                        case SqlErrorCodes.PreconditionFailed:
                            _logger.LogError(string.Format(Core.Resources.ResourceVersionConflict, weakETag));
                            throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag));

                        default:
                            _logger.LogError(e, "Error from SQL database on reindex.");
                            throw;
                        }
                    }
                }
        }
Beispiel #5
0
        public override async Task <long> CreateInstanceIndexAsync(DicomDataset instance, IEnumerable <QueryTag> queryTags, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(instance, nameof(instance));
            EnsureArg.IsNotNull(queryTags, nameof(queryTags));

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionFactoryWrapper.ObtainSqlConnectionWrapperAsync(cancellationToken))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    // Build parameter for extended query tag.
                    VLatest.AddInstanceTableValuedParameters parameters = AddInstanceTableValuedParametersBuilder.Build(
                        instance,
                        queryTags.Where(tag => tag.IsExtendedQueryTag));

                    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.GetStringDateAsDate(DicomTag.StudyDate),
                        instance.GetSingleValueOrDefault <string>(DicomTag.StudyDescription),
                        instance.GetSingleValueOrDefault <string>(DicomTag.AccessionNumber),
                        instance.GetSingleValueOrDefault <string>(DicomTag.Modality),
                        instance.GetStringDateAsDate(DicomTag.PerformedProcedureStepStartDate),
                        (byte)IndexStatus.Creating,
                        parameters);

                    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);
                    }
                }
        }
Beispiel #6
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);
                    }
                }
        }
        public async Task <ExportJobOutcome> UpdateExportJobAsync(ExportJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            byte[] rowVersionAsBytes = GetRowVersionAsBytes(eTag);

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.UpdateExportJob.PopulateCommand(
                        sqlCommandWrapper,
                        jobRecord.Id,
                        jobRecord.Status.ToString(),
                        JsonConvert.SerializeObject(jobRecord, _jsonSerializerSettings),
                        rowVersionAsBytes);

                    try
                    {
                        var rowVersion = (byte[])await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                        if (rowVersion.NullIfEmpty() == null)
                        {
                            throw new OperationFailedException(string.Format(Core.Resources.OperationFailed, OperationsConstants.Export, "Failed to create export job because no row version was returned."), HttpStatusCode.InternalServerError);
                        }

                        return(new ExportJobOutcome(jobRecord, GetRowVersionAsEtag(rowVersion)));
                    }
                    catch (SqlException e)
                    {
                        if (e.Number == SqlErrorCodes.PreconditionFailed)
                        {
                            throw new JobConflictException();
                        }
                        else if (e.Number == SqlErrorCodes.NotFound)
                        {
                            throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                        }
                        else
                        {
                            _logger.LogError(e, "Error from SQL database on export job update.");
                            throw;
                        }
                    }
                }
        }
Beispiel #8
0
        public virtual async Task <long> BeginCreateInstanceIndexAsync(int partitionKey, DicomDataset instance, IEnumerable <QueryTag> queryTags, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(instance, nameof(instance));
            EnsureArg.IsNotNull(queryTags, nameof(queryTags));

            using (SqlConnectionWrapper sqlConnectionWrapper = await SqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    V1.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.GetStringDateAsDate(DicomTag.StudyDate),
                        instance.GetSingleValueOrDefault <string>(DicomTag.StudyDescription),
                        instance.GetSingleValueOrDefault <string>(DicomTag.AccessionNumber),
                        instance.GetSingleValueOrDefault <string>(DicomTag.Modality),
                        instance.GetStringDateAsDate(DicomTag.PerformedProcedureStepStartDate),
                        (byte)IndexStatus.Creating);

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

                            throw new InstanceAlreadyExistsException();
                        }

                        throw new DataStoreException(ex);
                    }
                }
        }
Beispiel #9
0
        private async Task DeleteInstanceAsync(string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid, DateTimeOffset cleanupAfter, CancellationToken cancellationToken)
        {
            await _sqlServerIndexSchema.EnsureInitialized();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionFactoryWrapper.ObtainSqlConnectionWrapper())
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.DeleteInstance.PopulateCommand(
                        sqlCommandWrapper,
                        cleanupAfter,
                        (byte)IndexStatus.Created,
                        studyInstanceUid,
                        seriesInstanceUid,
                        sopInstanceUid);

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                    }
                    catch (SqlException ex)
                    {
                        switch (ex.Number)
                        {
                        case SqlErrorCodes.NotFound:
                            if (!string.IsNullOrEmpty(sopInstanceUid))
                            {
                                throw new InstanceNotFoundException();
                            }

                            if (!string.IsNullOrEmpty(seriesInstanceUid))
                            {
                                throw new SeriesNotFoundException();
                            }

                            throw new StudyNotFoundException();

                        default:
                            throw new DataStoreException(ex);
                        }
                    }
                }
        }
Beispiel #10
0
        public async Task <ReindexJobWrapper> CreateReindexJobAsync(ReindexJobRecord jobRecord, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.CreateReindexJob.PopulateCommand(
                        sqlCommandWrapper,
                        jobRecord.Id,
                        jobRecord.Status.ToString(),
                        JsonConvert.SerializeObject(jobRecord, _jsonSerializerSettings));

                    var rowVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                    if (rowVersion == null)
                    {
                        throw new OperationFailedException(string.Format(Core.Resources.OperationFailed, OperationsConstants.Reindex, "Failed to create reindex job because no row version was returned."), HttpStatusCode.InternalServerError);
                    }

                    return(new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId(rowVersion.ToString())));
                }
        }
Beispiel #11
0
        public async Task DeleteDeletedInstanceAsync(VersionedInstanceIdentifier versionedInstanceIdentifier, CancellationToken cancellationToken = default)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionFactoryWrapper.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.DeleteDeletedInstance.PopulateCommand(
                        sqlCommandWrapper,
                        versionedInstanceIdentifier.StudyInstanceUid,
                        versionedInstanceIdentifier.SeriesInstanceUid,
                        versionedInstanceIdentifier.SopInstanceUid,
                        versionedInstanceIdentifier.Version);

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                    }
                    catch (SqlException ex)
                    {
                        throw new DataStoreException(ex);
                    }
                }
        }
Beispiel #12
0
        public async Task UpdateInstanceIndexStatusAsync(
            VersionedInstanceIdentifier versionedInstanceIdentifier,
            IndexStatus status,
            CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(versionedInstanceIdentifier, nameof(versionedInstanceIdentifier));
            EnsureArg.IsTrue(Enum.IsDefined(typeof(IndexStatus), status));
            EnsureArg.IsTrue((int)status < byte.MaxValue);

            await _sqlServerIndexSchema.EnsureInitialized();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionFactoryWrapper.ObtainSqlConnectionWrapper())
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.UpdateInstanceStatus.PopulateCommand(
                        sqlCommandWrapper,
                        versionedInstanceIdentifier.StudyInstanceUid,
                        versionedInstanceIdentifier.SeriesInstanceUid,
                        versionedInstanceIdentifier.SopInstanceUid,
                        versionedInstanceIdentifier.Version,
                        (byte)status);

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                    }
                    catch (SqlException ex)
                    {
                        switch (ex.Number)
                        {
                        case SqlErrorCodes.NotFound:
                            throw new InstanceNotFoundException();

                        default:
                            throw new DataStoreException(ex);
                        }
                    }
                }
        }
Beispiel #13
0
 public async Task <int> UpsertInstanceSchemaInformationAsync(string name, SchemaInformation schemaInformation, CancellationToken cancellationToken)
 {
     using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken: cancellationToken))
         using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
         {
             SchemaShared.UpsertInstanceSchema.PopulateCommand(
                 sqlCommandWrapper,
                 name,
                 schemaInformation.MaximumSupportedVersion,
                 schemaInformation.MinimumSupportedVersion,
                 _configuration.SchemaOptions.InstanceRecordExpirationTimeInMinutes);
             try
             {
                 return((int)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken));
             }
             catch (SqlException e)
             {
                 _logger.LogError(e, "Error from SQL database on upserting InstanceSchema information");
                 throw;
             }
         }
 }
        public override async Task EndCreateInstanceIndexAsync(
            int partitionKey,
            DicomDataset dicomDataset,
            long watermark,
            IEnumerable <QueryTag> queryTags,
            bool allowExpiredTags = false,
            CancellationToken cancellationToken = default)
        {
            EnsureArg.IsNotNull(dicomDataset, nameof(dicomDataset));
            EnsureArg.IsNotNull(queryTags, nameof(queryTags));

            using (SqlConnectionWrapper sqlConnectionWrapper = await SqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.UpdateInstanceStatusV6.PopulateCommand(
                        sqlCommandWrapper,
                        partitionKey,
                        dicomDataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty),
                        dicomDataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty),
                        dicomDataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty),
                        watermark,
                        (byte)IndexStatus.Created,
                        allowExpiredTags ? null : ExtendedQueryTagDataRowsBuilder.GetMaxTagKey(queryTags));

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                    }
                    catch (SqlException ex)
                    {
                        throw ex.Number switch
                              {
                                  SqlErrorCodes.NotFound => new InstanceNotFoundException(),
                                  SqlErrorCodes.Conflict when ex.State == 10 => new ExtendedQueryTagsOutOfDateException(),
                                  _ => new DataStoreException(ex),
                              };
                    }
                }
        }
Beispiel #15
0
        public virtual async Task EndCreateInstanceIndexAsync(
            int partitionKey,
            DicomDataset dicomDataset,
            long watermark,
            IEnumerable <QueryTag> queryTags,
            bool allowExpiredTags = false,
            CancellationToken cancellationToken = default)
        {
            EnsureArg.IsNotNull(dicomDataset, nameof(dicomDataset));

            using (SqlConnectionWrapper sqlConnectionWrapper = await SqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    V1.UpdateInstanceStatus.PopulateCommand(
                        sqlCommandWrapper,
                        dicomDataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty),
                        dicomDataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty),
                        dicomDataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty),
                        watermark,
                        (byte)IndexStatus.Created);

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                    }
                    catch (SqlException ex)
                    {
                        switch (ex.Number)
                        {
                        case SqlErrorCodes.NotFound:
                            throw new InstanceNotFoundException();

                        default:
                            throw new DataStoreException(ex);
                        }
                    }
                }
        }
        public override async Task <int> IncrementDeletedInstanceRetryAsync(VersionedInstanceIdentifier versionedInstanceIdentifier, DateTimeOffset cleanupAfter, CancellationToken cancellationToken = default)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await SqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.IncrementDeletedInstanceRetryV6.PopulateCommand(
                        sqlCommandWrapper,
                        versionedInstanceIdentifier.PartitionKey,
                        versionedInstanceIdentifier.StudyInstanceUid,
                        versionedInstanceIdentifier.SeriesInstanceUid,
                        versionedInstanceIdentifier.SopInstanceUid,
                        versionedInstanceIdentifier.Version,
                        cleanupAfter);

                    try
                    {
                        return((int)(await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken)));
                    }
                    catch (SqlException ex)
                    {
                        throw new DataStoreException(ex);
                    }
                }
        }
        public async Task <UpsertOutcome> UpsertAsync(ResourceWrapper resource, WeakETag weakETag, bool allowCreate, bool keepHistory, CancellationToken cancellationToken)
        {
            int etag = 0;

            if (weakETag != null && !int.TryParse(weakETag.VersionId, out etag))
            {
                // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.
                etag = -1;
            }

            var resourceMetadata = new ResourceMetadata(
                resource.CompartmentIndices,
                resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)),
                resource.LastModifiedClaims);

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                    using (var stream = new RecyclableMemoryStream(_memoryStreamManager))
                        using (var gzipStream = new GZipStream(stream, CompressionMode.Compress))
                            using (var writer = new StreamWriter(gzipStream, ResourceEncoding))
                            {
                                writer.Write(resource.RawResource.Data);
                                writer.Flush();

                                stream.Seek(0, 0);

                                VLatest.UpsertResource.PopulateCommand(
                                    sqlCommandWrapper,
                                    baseResourceSurrogateId: ResourceSurrogateIdHelper.LastUpdatedToResourceSurrogateId(resource.LastModified.UtcDateTime),
                                    resourceTypeId: _model.GetResourceTypeId(resource.ResourceTypeName),
                                    resourceId: resource.ResourceId,
                                    eTag: weakETag == null ? null : (int?)etag,
                                    allowCreate: allowCreate,
                                    isDeleted: resource.IsDeleted,
                                    keepHistory: keepHistory,
                                    requestMethod: resource.Request.Method,
                                    rawResource: stream,
                                    tableValuedParameters: _upsertResourceTvpGeneratorVLatest.Generate(resourceMetadata));

                                try
                                {
                                    var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                                    if (newVersion == null)
                                    {
                                        // indicates a redundant delete
                                        return(null);
                                    }

                                    resource.Version = newVersion.ToString();

                                    return(new UpsertOutcome(resource, newVersion == 1 ? SaveOutcomeType.Created : SaveOutcomeType.Updated));
                                }
                                catch (SqlException e)
                                {
                                    switch (e.Number)
                                    {
                                    case SqlErrorCodes.PreconditionFailed:
                                        throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag?.VersionId));

                                    case SqlErrorCodes.NotFound:
                                        if (weakETag != null)
                                        {
                                            throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId));
                                        }

                                        goto default;

                                    case SqlErrorCodes.MethodNotAllowed:
                                        throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed);

                                    default:
                                        _logger.LogError(e, "Error from SQL database on upsert");
                                        throw;
                                    }
                                }
                            }
        }
Beispiel #18
0
        public async Task <UpsertOutcome> UpsertAsync(
            ResourceWrapper resource,
            WeakETag weakETag,
            bool allowCreate,
            bool keepHistory,
            CancellationToken cancellationToken,
            bool requireETagOnUpdate = false)
        {
            int?eTag = weakETag == null
                ? null
                : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.

            var resourceMetadata = new ResourceMetadata(
                resource.CompartmentIndices,
                resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)),
                resource.LastModifiedClaims);

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                    using (var stream = new RecyclableMemoryStream(_memoryStreamManager))
                    {
                        _compressedRawResourceConverter.WriteCompressedRawResource(stream, resource.RawResource.Data);

                        stream.Seek(0, 0);

                        PopulateUpsertResourceCommand(sqlCommandWrapper, resource, resourceMetadata, allowCreate, keepHistory, requireETagOnUpdate, eTag, stream, _coreFeatures.SupportsResourceChangeCapture);

                        try
                        {
                            var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                            if (newVersion == null)
                            {
                                // indicates a redundant delete
                                return(null);
                            }

                            resource.Version = newVersion.ToString();

                            SaveOutcomeType saveOutcomeType;
                            if (newVersion == 1)
                            {
                                saveOutcomeType = SaveOutcomeType.Created;
                            }
                            else
                            {
                                saveOutcomeType = SaveOutcomeType.Updated;
                                resource.RawResource.IsMetaSet = false;
                            }

                            return(new UpsertOutcome(resource, saveOutcomeType));
                        }
                        catch (SqlException e)
                        {
                            switch (e.Number)
                            {
                            case SqlErrorCodes.PreconditionFailed:
                                if (weakETag != null)
                                {
                                    // The backwards compatibility behavior of Stu3 is to return 409 Conflict instead of a 412 Precondition Failed
                                    if (_modelInfoProvider.Version == FhirSpecification.Stu3)
                                    {
                                        throw new ResourceConflictException(weakETag);
                                    }

                                    throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag.VersionId));
                                }

                                goto default;

                            case SqlErrorCodes.NotFound:
                                if (weakETag != null)
                                {
                                    throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId));
                                }

                                goto default;

                            case SqlErrorCodes.MethodNotAllowed:
                                throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed);

                            case SqlErrorCodes.TimeoutExpired:
                                throw new RequestTimeoutException(Resources.ExecutionTimeoutExpired);

                            case 50400: // TODO: Add this to SQL error codes in AB#88286
                                // The backwards compatibility behavior of Stu3 is to return 412 Precondition Failed instead of a 400 Bad Request
                                if (_modelInfoProvider.Version == FhirSpecification.Stu3)
                                {
                                    throw new PreconditionFailedException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.ResourceTypeName));
                                }

                                throw new BadRequestException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.ResourceTypeName));

                            default:
                                _logger.LogError(e, "Error from SQL database on upsert");
                                throw;
                            }
                        }
                    }
        }
Beispiel #19
0
        public async Task BulkUpdateSearchParameterIndicesAsync(IReadOnlyCollection <ResourceWrapper> resources, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    if (_schemaInformation.Current >= SchemaVersionConstants.AddMinMaxForDateAndStringSearchParamVersion)
                    {
                        VLatest.BulkReindexResources.PopulateCommand(
                            sqlCommandWrapper,
                            _bulkReindexResourcesTvpGeneratorVLatest.Generate(resources.ToList()));
                    }
                    else
                    {
                        V17.BulkReindexResources.PopulateCommand(
                            sqlCommandWrapper,
                            _bulkReindexResourcesTvpGeneratorV17.Generate(resources.ToList()));
                    }

                    if (_schemaInformation.Current >= SchemaVersionConstants.BulkReindexReturnsFailuresVersion)
                    {
                        // We will reindex the rest of the batch if one resource has a versioning conflict
                        int?failedResourceCount;
                        try
                        {
                            failedResourceCount = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                        }
                        catch (SqlException e)
                        {
                            _logger.LogError(e, "Error from SQL database on reindex.");
                            throw;
                        }

                        if (failedResourceCount != 0)
                        {
                            string message    = string.Format(Core.Resources.ReindexingResourceVersionConflictWithCount, failedResourceCount);
                            string userAction = Core.Resources.ReindexingUserAction;

                            _logger.LogError(message);
                            throw new PreconditionFailedException(message + " " + userAction);
                        }
                    }
                    else
                    {
                        try
                        {
                            // We are running an earlier schema version where we will fail the whole batch if there is a conflict
                            await sqlCommandWrapper.ExecuteNonQueryAsync(cancellationToken);
                        }
                        catch (SqlException e)
                        {
                            switch (e.Number)
                            {
                            case SqlErrorCodes.PreconditionFailed:
                                string message    = Core.Resources.ReindexingResourceVersionConflict;
                                string userAction = Core.Resources.ReindexingUserAction;

                                _logger.LogError(message);
                                throw new PreconditionFailedException(message + " " + userAction);

                            default:
                                _logger.LogError(e, "Error from SQL database on reindex.");
                                throw;
                            }
                        }
                    }
                }
        }
Beispiel #20
0
        public async Task GivenATransientException_WhenScalarIsExecuted_ThenItShouldRetry()
        {
            _sqlCommandWrapper.ExecuteScalarAsync(DefaultCancellationToken).Throws(CreateTransientException());

            await ExecuteAndValidateExecuteScalarAsync(4);
        }