# HG changeset patch # User Sebastien Jodogne # Date 1354034917 -3600 # Node ID 1650557bd81ad62ae44c84d70e70a34158590e06 # Parent bee20e97883576de498138045bd6f70130d08be1 refactoring diff -r bee20e978835 -r 1650557bd81a OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Tue Nov 27 17:36:19 2012 +0100 +++ b/OrthancServer/DatabaseWrapper.h Tue Nov 27 17:48:37 2012 +0100 @@ -181,5 +181,10 @@ { return new SQLite::Transaction(db_); } + + const char* GetErrorMessage() const + { + return db_.GetErrorMessage(); + } }; } diff -r bee20e978835 -r 1650557bd81a OrthancServer/PrepareDatabase-v1.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/PrepareDatabase-v1.sql Tue Nov 27 17:48:37 2012 +0100 @@ -0,0 +1,136 @@ +CREATE TABLE GlobalProperties( + name TEXT PRIMARY KEY, + value TEXT + ); + +CREATE TABLE Resources( + uuid TEXT PRIMARY KEY, + resourceType INTEGER + ); + +CREATE TABLE Patients( + uuid TEXT PRIMARY KEY, + dicomPatientId TEXT + ); + +CREATE TABLE Studies( + uuid TEXT PRIMARY KEY, + parentPatient TEXT REFERENCES Patients(uuid) ON DELETE CASCADE, + dicomStudy TEXT + ); + +CREATE TABLE Series( + uuid TEXT PRIMARY KEY, + parentStudy TEXT REFERENCES Studies(uuid) ON DELETE CASCADE, + dicomSeries TEXT, + expectedNumberOfInstances INTEGER + ); + +CREATE TABLE Instances( + uuid TEXT PRIMARY KEY, + parentSeries TEXT REFERENCES Series(uuid) ON DELETE CASCADE, + dicomInstance TEXT, + fileUuid TEXT, + fileSize INTEGER, + jsonUuid TEXT, + distantAet TEXT, + indexInSeries INTEGER + ); + +CREATE TABLE MainDicomTags( + uuid TEXT, + tagGroup INTEGER, + tagElement INTEGER, + value TEXT, + PRIMARY KEY(uuid, tagGroup, tagElement) + ); + +CREATE TABLE Changes( + seq INTEGER PRIMARY KEY AUTOINCREMENT, + basePath TEXT, + uuid TEXT + ); + + +CREATE INDEX PatientToStudies ON Studies(parentPatient); +CREATE INDEX StudyToSeries ON Series(parentStudy); +CREATE INDEX SeriesToInstances ON Instances(parentSeries); + +CREATE INDEX DicomPatientIndex ON Patients(dicomPatientId); +CREATE INDEX DicomStudyIndex ON Studies(dicomStudy); +CREATE INDEX DicomSeriesIndex ON Series(dicomSeries); +CREATE INDEX DicomInstanceIndex ON Instances(dicomInstance); + +CREATE INDEX MainDicomTagsIndex ON MainDicomTags(uuid); +CREATE INDEX MainDicomTagsGroupElement ON MainDicomTags(tagGroup, tagElement); +CREATE INDEX MainDicomTagsValues ON MainDicomTags(value COLLATE BINARY); + +CREATE INDEX ChangesIndex ON Changes(uuid); + +CREATE TRIGGER InstanceRemoved +AFTER DELETE ON Instances +FOR EACH ROW BEGIN + DELETE FROM Resources WHERE uuid = old.uuid; + DELETE FROM MainDicomTags WHERE uuid = old.uuid; + DELETE FROM Changes WHERE uuid = old.uuid; + SELECT DeleteFromFileStorage(old.fileUuid); + SELECT DeleteFromFileStorage(old.jsonUuid); + SELECT SignalDeletedLevel(3, old.parentSeries); +END; + +CREATE TRIGGER SeriesRemoved +AFTER DELETE ON Series +FOR EACH ROW BEGIN + DELETE FROM Resources WHERE uuid = old.uuid; + DELETE FROM MainDicomTags WHERE uuid = old.uuid; + DELETE FROM Changes WHERE uuid = old.uuid; + SELECT SignalDeletedLevel(2, old.parentStudy); +END; + +CREATE TRIGGER StudyRemoved +AFTER DELETE ON Studies +FOR EACH ROW BEGIN + DELETE FROM Resources WHERE uuid = old.uuid; + DELETE FROM MainDicomTags WHERE uuid = old.uuid; + DELETE FROM Changes WHERE uuid = old.uuid; + SELECT SignalDeletedLevel(1, old.parentPatient); +END; + +CREATE TRIGGER PatientRemoved +AFTER DELETE ON Patients +FOR EACH ROW BEGIN + DELETE FROM Resources WHERE uuid = old.uuid; + DELETE FROM MainDicomTags WHERE uuid = old.uuid; + DELETE FROM Changes WHERE uuid = old.uuid; + SELECT SignalDeletedLevel(0, ""); +END; + + + + +CREATE TRIGGER InstanceRemovedUpwardCleaning +AFTER DELETE ON Instances +FOR EACH ROW + WHEN (SELECT COUNT(*) FROM Instances WHERE parentSeries = old.parentSeries) = 0 + BEGIN + SELECT DeleteFromFileStorage("deleting parent series"); -- TODO REMOVE THIS + DELETE FROM Series WHERE uuid = old.parentSeries; + END; + +CREATE TRIGGER SeriesRemovedUpwardCleaning +AFTER DELETE ON Series +FOR EACH ROW + WHEN (SELECT COUNT(*) FROM Series WHERE parentStudy = old.parentStudy) = 0 + BEGIN + SELECT DeleteFromFileStorage("deleting parent study"); -- TODO REMOVE THIS + DELETE FROM Studies WHERE uuid = old.parentStudy; + END; + +CREATE TRIGGER StudyRemovedUpwardCleaning +AFTER DELETE ON Studies +FOR EACH ROW + WHEN (SELECT COUNT(*) FROM Studies WHERE parentPatient = old.parentPatient) = 0 + BEGIN + SELECT DeleteFromFileStorage("deleting parent patient"); -- TODO REMOVE THIS + DELETE FROM Patients WHERE uuid = old.parentPatient; + END; diff -r bee20e978835 -r 1650557bd81a OrthancServer/PrepareDatabase.sql --- a/OrthancServer/PrepareDatabase.sql Tue Nov 27 17:36:19 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -CREATE TABLE GlobalProperties( - name TEXT PRIMARY KEY, - value TEXT - ); - -CREATE TABLE Resources( - uuid TEXT PRIMARY KEY, - resourceType INTEGER - ); - -CREATE TABLE Patients( - uuid TEXT PRIMARY KEY, - dicomPatientId TEXT - ); - -CREATE TABLE Studies( - uuid TEXT PRIMARY KEY, - parentPatient TEXT REFERENCES Patients(uuid) ON DELETE CASCADE, - dicomStudy TEXT - ); - -CREATE TABLE Series( - uuid TEXT PRIMARY KEY, - parentStudy TEXT REFERENCES Studies(uuid) ON DELETE CASCADE, - dicomSeries TEXT, - expectedNumberOfInstances INTEGER - ); - -CREATE TABLE Instances( - uuid TEXT PRIMARY KEY, - parentSeries TEXT REFERENCES Series(uuid) ON DELETE CASCADE, - dicomInstance TEXT, - fileUuid TEXT, - fileSize INTEGER, - jsonUuid TEXT, - distantAet TEXT, - indexInSeries INTEGER - ); - -CREATE TABLE MainDicomTags( - uuid TEXT, - tagGroup INTEGER, - tagElement INTEGER, - value TEXT, - PRIMARY KEY(uuid, tagGroup, tagElement) - ); - -CREATE TABLE Changes( - seq INTEGER PRIMARY KEY AUTOINCREMENT, - basePath TEXT, - uuid TEXT - ); - - -CREATE INDEX PatientToStudies ON Studies(parentPatient); -CREATE INDEX StudyToSeries ON Series(parentStudy); -CREATE INDEX SeriesToInstances ON Instances(parentSeries); - -CREATE INDEX DicomPatientIndex ON Patients(dicomPatientId); -CREATE INDEX DicomStudyIndex ON Studies(dicomStudy); -CREATE INDEX DicomSeriesIndex ON Series(dicomSeries); -CREATE INDEX DicomInstanceIndex ON Instances(dicomInstance); - -CREATE INDEX MainDicomTagsIndex ON MainDicomTags(uuid); -CREATE INDEX MainDicomTagsGroupElement ON MainDicomTags(tagGroup, tagElement); -CREATE INDEX MainDicomTagsValues ON MainDicomTags(value COLLATE BINARY); - -CREATE INDEX ChangesIndex ON Changes(uuid); - -CREATE TRIGGER InstanceRemoved -AFTER DELETE ON Instances -FOR EACH ROW BEGIN - DELETE FROM Resources WHERE uuid = old.uuid; - DELETE FROM MainDicomTags WHERE uuid = old.uuid; - DELETE FROM Changes WHERE uuid = old.uuid; - SELECT DeleteFromFileStorage(old.fileUuid); - SELECT DeleteFromFileStorage(old.jsonUuid); - SELECT SignalDeletedLevel(3, old.parentSeries); -END; - -CREATE TRIGGER SeriesRemoved -AFTER DELETE ON Series -FOR EACH ROW BEGIN - DELETE FROM Resources WHERE uuid = old.uuid; - DELETE FROM MainDicomTags WHERE uuid = old.uuid; - DELETE FROM Changes WHERE uuid = old.uuid; - SELECT SignalDeletedLevel(2, old.parentStudy); -END; - -CREATE TRIGGER StudyRemoved -AFTER DELETE ON Studies -FOR EACH ROW BEGIN - DELETE FROM Resources WHERE uuid = old.uuid; - DELETE FROM MainDicomTags WHERE uuid = old.uuid; - DELETE FROM Changes WHERE uuid = old.uuid; - SELECT SignalDeletedLevel(1, old.parentPatient); -END; - -CREATE TRIGGER PatientRemoved -AFTER DELETE ON Patients -FOR EACH ROW BEGIN - DELETE FROM Resources WHERE uuid = old.uuid; - DELETE FROM MainDicomTags WHERE uuid = old.uuid; - DELETE FROM Changes WHERE uuid = old.uuid; - SELECT SignalDeletedLevel(0, ""); -END; - - - - -CREATE TRIGGER InstanceRemovedUpwardCleaning -AFTER DELETE ON Instances -FOR EACH ROW - WHEN (SELECT COUNT(*) FROM Instances WHERE parentSeries = old.parentSeries) = 0 - BEGIN - SELECT DeleteFromFileStorage("deleting parent series"); -- TODO REMOVE THIS - DELETE FROM Series WHERE uuid = old.parentSeries; - END; - -CREATE TRIGGER SeriesRemovedUpwardCleaning -AFTER DELETE ON Series -FOR EACH ROW - WHEN (SELECT COUNT(*) FROM Series WHERE parentStudy = old.parentStudy) = 0 - BEGIN - SELECT DeleteFromFileStorage("deleting parent study"); -- TODO REMOVE THIS - DELETE FROM Studies WHERE uuid = old.parentStudy; - END; - -CREATE TRIGGER StudyRemovedUpwardCleaning -AFTER DELETE ON Studies -FOR EACH ROW - WHEN (SELECT COUNT(*) FROM Studies WHERE parentPatient = old.parentPatient) = 0 - BEGIN - SELECT DeleteFromFileStorage("deleting parent patient"); -- TODO REMOVE THIS - DELETE FROM Patients WHERE uuid = old.parentPatient; - END; diff -r bee20e978835 -r 1650557bd81a OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Tue Nov 27 17:36:19 2012 +0100 +++ b/OrthancServer/ServerIndex.cpp Tue Nov 27 17:48:37 2012 +0100 @@ -53,7 +53,7 @@ { namespace Internals { - class ServerIndexListenerTodo : public IServerIndexListener + class ServerIndexListener : public IServerIndexListener { private: FileStorage& fileStorage_; @@ -62,7 +62,7 @@ std::string remainingPublicId_; public: - ServerIndexListenerTodo(FileStorage& fileStorage) : + ServerIndexListener(FileStorage& fileStorage) : fileStorage_(fileStorage), hasRemainingLevel_(false) { @@ -221,259 +221,35 @@ } - void ServerIndex::StoreMainDicomTags(const std::string& uuid, - const DicomMap& map) - { - DicomArray flattened(map); - for (size_t i = 0; i < flattened.GetSize(); i++) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); - s.BindString(0, uuid); - s.BindInt(1, flattened.GetElement(i).GetTag().GetGroup()); - s.BindInt(2, flattened.GetElement(i).GetTag().GetElement()); - s.BindString(3, flattened.GetElement(i).GetValue().AsString()); - s.Run(); - } - } - - bool ServerIndex::HasInstance(DicomInstanceHasher& hasher) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Instances WHERE dicomInstance=?"); - s.BindString(0, hasher.GetInstanceUid()); - return s.Step(); - } - - - void ServerIndex::RecordChange(const std::string& resourceType, - const std::string& uuid) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?)"); - s.BindString(0, resourceType); - s.BindString(1, uuid); - s.Run(); - } - - - void ServerIndex::CreateInstance(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary, - const std::string& fileUuid, - uint64_t fileSize, - const std::string& jsonUuid, - const std::string& remoteAet) - { - SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); - s2.BindString(0, hasher.HashInstance()); - s2.BindInt(1, ResourceType_Instance); - s2.Run(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Instances VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); - s.BindString(0, hasher.HashInstance()); - s.BindString(1, hasher.HashSeries()); - s.BindString(2, hasher.GetInstanceUid()); - s.BindString(3, fileUuid); - s.BindInt64(4, fileSize); - s.BindString(5, jsonUuid); - s.BindString(6, remoteAet); - - const DicomValue* indexInSeries; - if ((indexInSeries = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || - (indexInSeries = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) - { - s.BindInt(7, boost::lexical_cast(indexInSeries->AsString())); - } - else - { - s.BindNull(7); - } - - s.Run(); - - RecordChange("instances", hasher.HashInstance()); - - DicomMap dicom; - dicomSummary.ExtractInstanceInformation(dicom); - StoreMainDicomTags(hasher.HashInstance(), dicom); - } - - void ServerIndex::RemoveInstance(const std::string& uuid) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Instances WHERE uuid=?"); - s.BindString(0, uuid); - s.Run(); - } - - bool ServerIndex::HasSeries(DicomInstanceHasher& hasher) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Series WHERE dicomSeries=?"); - s.BindString(0, hasher.GetSeriesUid()); - return s.Step(); - } - - void ServerIndex::CreateSeries(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary) - { - SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); - s2.BindString(0, hasher.HashSeries()); - s2.BindInt(1, ResourceType_Series); - s2.Run(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Series VALUES(?, ?, ?, ?)"); - s.BindString(0, hasher.HashSeries()); - s.BindString(1, hasher.HashStudy()); - s.BindString(2, hasher.GetSeriesUid()); - - const DicomValue* expectedNumberOfInstances; - if (//(expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_FRAMES)) != NULL || - (expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL || - //(expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL || - (expectedNumberOfInstances = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL) - { - s.BindInt(3, boost::lexical_cast(expectedNumberOfInstances->AsString())); - } - else - { - s.BindNull(3); - } - - s.Run(); - - RecordChange("series", hasher.HashSeries()); - - DicomMap dicom; - dicomSummary.ExtractSeriesInformation(dicom); - StoreMainDicomTags(hasher.HashSeries(), dicom); - } - - bool ServerIndex::HasStudy(DicomInstanceHasher& hasher) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Studies WHERE dicomStudy=?"); - s.BindString(0, hasher.GetStudyUid()); - return s.Step(); - } - - void ServerIndex::CreateStudy(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary) - { - SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); - s2.BindString(0, hasher.HashStudy()); - s2.BindInt(1, ResourceType_Study); - s2.Run(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Studies VALUES(?, ?, ?)"); - s.BindString(0, hasher.HashStudy()); - s.BindString(1, hasher.HashPatient()); - s.BindString(2, hasher.GetStudyUid()); - s.Run(); - - RecordChange("studies", hasher.HashStudy()); - - DicomMap dicom; - dicomSummary.ExtractStudyInformation(dicom); - StoreMainDicomTags(hasher.HashStudy(), dicom); - } - - - - bool ServerIndex::HasPatient(DicomInstanceHasher& hasher) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT uuid FROM Patients WHERE dicomPatientId=?"); - s.BindString(0,hasher.GetPatientId()); - return s.Step(); - } - - void ServerIndex::CreatePatient(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary) - { - std::string dicomPatientId = dicomSummary.GetValue(DICOM_TAG_PATIENT_ID).AsString(); - - SQLite::Statement s2(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(?, ?)"); - s2.BindString(0, hasher.HashPatient()); - s2.BindInt(1, ResourceType_Patient); - s2.Run(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Patients VALUES(?, ?)"); - s.BindString(0, hasher.HashPatient()); - s.BindString(1, dicomPatientId); - s.Run(); - - RecordChange("patients", hasher.HashPatient()); - - DicomMap dicom; - dicomSummary.ExtractPatientInformation(dicom); - StoreMainDicomTags(hasher.HashPatient(), dicom); - } - - bool ServerIndex::DeleteInternal(Json::Value& target, const std::string& uuid, - const std::string& tableName) + ResourceType expectedType) { boost::mutex::scoped_lock scoped_lock(mutex_); - - { - listener2_->Reset(); - - std::auto_ptr t(db2_->StartTransaction()); - t->Begin(); + listener_->Reset(); - int64_t id; - ResourceType type; - if (!db2_->LookupResource(uuid, id, type)) - { - return false; - } - - db2_->DeleteResource(id); - - if (listener2_->HasRemainingLevel()) - { - ResourceType type = listener2_->GetRemainingType(); - const std::string& uuid = listener2_->GetRemainingPublicId(); + std::auto_ptr t(db_->StartTransaction()); + t->Begin(); - target["RemainingAncestor"] = Json::Value(Json::objectValue); - target["RemainingAncestor"]["Path"] = std::string(GetBasePath(type)) + "/" + uuid; - target["RemainingAncestor"]["Type"] = ToString(type); - target["RemainingAncestor"]["ID"] = uuid; - } - else - { - target["RemainingAncestor"] = Json::nullValue; - } - - std::cout << target << std::endl; - - t->Commit(); - - return true; - } - - - - deletedLevels_->Clear(); - - SQLite::Statement s(db_, "DELETE FROM " + tableName + " WHERE uuid=?"); - s.BindString(0, uuid); - - if (!s.Run()) + int64_t id; + ResourceType type; + if (!db_->LookupResource(uuid, id, type) || + expectedType != type) { return false; } + + db_->DeleteResource(id); - if (db_.GetLastChangeCount() == 0) + if (listener_->HasRemainingLevel()) { - // Nothing was deleted, inexistent UUID - return false; - } - - if (deletedLevels_->HasRemainingLevel()) - { - std::string type(deletedLevels_->GetRemainingLevelType()); - const std::string& uuid = deletedLevels_->GetRemainingLevelUuid(); + ResourceType type = listener_->GetRemainingType(); + const std::string& uuid = listener_->GetRemainingPublicId(); target["RemainingAncestor"] = Json::Value(Json::objectValue); - target["RemainingAncestor"]["Path"] = "/" + type + "/" + uuid; - target["RemainingAncestor"]["Type"] = type; + target["RemainingAncestor"]["Path"] = std::string(GetBasePath(type)) + "/" + uuid; + target["RemainingAncestor"]["Type"] = ToString(type); target["RemainingAncestor"]["ID"] = uuid; } else @@ -481,6 +257,8 @@ target["RemainingAncestor"] = Json::nullValue; } + t->Commit(); + return true; } @@ -488,12 +266,11 @@ ServerIndex::ServerIndex(FileStorage& fileStorage, const std::string& dbPath) { - listener2_.reset(new Internals::ServerIndexListenerTodo(fileStorage)); + listener_.reset(new Internals::ServerIndexListener(fileStorage)); if (dbPath == ":memory:") { - db_.OpenInMemory(); - db2_.reset(new DatabaseWrapper(*listener2_)); + db_.reset(new DatabaseWrapper(*listener_)); } else { @@ -507,199 +284,120 @@ { } - db2_.reset(new DatabaseWrapper(p.string() + "/index2", *listener2_)); - - p /= "index"; - db_.Open(p.string()); - } - - db_.Register(new Internals::DeleteFromFileStorageFunction(fileStorage)); - deletedLevels_ = (Internals::SignalDeletedLevelFunction*) - db_.Register(new Internals::SignalDeletedLevelFunction); - - if (!db_.DoesTableExist("GlobalProperties")) - { - LOG(INFO) << "Creating the database"; - std::string query; - EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE); - db_.Execute(query); + db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_)); } } - StoreStatus ServerIndex::Store2(const DicomMap& dicomSummary, - const std::string& fileUuid, - uint64_t uncompressedFileSize, - const std::string& jsonUuid, - const std::string& remoteAet) - { - boost::mutex::scoped_lock scoped_lock(mutex_); - - DicomInstanceHasher hasher(dicomSummary); - - try - { - std::auto_ptr t(db2_->StartTransaction()); - t->Begin(); - - int64_t patient, study, series, instance; - ResourceType type; - bool isNewSeries = false; - - // Do nothing if the instance already exists - if (db2_->LookupResource(hasher.HashInstance(), patient, type)) - { - assert(type == ResourceType_Instance); - return StoreStatus_AlreadyStored; - } - - // Create the instance - instance = db2_->CreateResource(hasher.HashInstance(), ResourceType_Instance); - - DicomMap dicom; - dicomSummary.ExtractInstanceInformation(dicom); - db2_->SetMainDicomTags(instance, dicom); - - // Create the patient/study/series/instance hierarchy - if (!db2_->LookupResource(hasher.HashSeries(), series, type)) - { - // This is a new series - isNewSeries = true; - series = db2_->CreateResource(hasher.HashSeries(), ResourceType_Series); - dicomSummary.ExtractSeriesInformation(dicom); - db2_->SetMainDicomTags(series, dicom); - db2_->AttachChild(series, instance); - - if (!db2_->LookupResource(hasher.HashStudy(), study, type)) - { - // This is a new study - study = db2_->CreateResource(hasher.HashStudy(), ResourceType_Study); - dicomSummary.ExtractStudyInformation(dicom); - db2_->SetMainDicomTags(study, dicom); - db2_->AttachChild(study, series); - - if (!db2_->LookupResource(hasher.HashPatient(), patient, type)) - { - // This is a new patient - patient = db2_->CreateResource(hasher.HashPatient(), ResourceType_Patient); - dicomSummary.ExtractPatientInformation(dicom); - db2_->SetMainDicomTags(patient, dicom); - db2_->AttachChild(patient, study); - } - else - { - assert(type == ResourceType_Patient); - db2_->AttachChild(patient, study); - } - } - else - { - assert(type == ResourceType_Study); - db2_->AttachChild(study, series); - } - } - else - { - assert(type == ResourceType_Series); - db2_->AttachChild(series, instance); - } - - // Attach the files to the newly created instance - db2_->AttachFile(instance, AttachedFileType_Dicom, fileUuid, uncompressedFileSize); - db2_->AttachFile(instance, AttachedFileType_Json, jsonUuid, 0); // TODO "0" - - // Attach the metadata - db2_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, Toolbox::GetNowIsoString()); - db2_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); - - const DicomValue* value; - if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || - (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) - { - db2_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); - } - - if (isNewSeries) - { - if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL || - (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL || - (value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL) - { - db2_->SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString()); - } - } - - t->Commit(); - } - catch (OrthancException& e) - { - LOG(ERROR) << "EXCEPTION2 [" << e.What() << "]" << " " << db_.GetErrorMessage(); - } - - return StoreStatus_Failure; - } - - StoreStatus ServerIndex::Store(const DicomMap& dicomSummary, const std::string& fileUuid, uint64_t uncompressedFileSize, const std::string& jsonUuid, const std::string& remoteAet) { - Store2(dicomSummary, fileUuid, uncompressedFileSize, jsonUuid, remoteAet); - boost::mutex::scoped_lock scoped_lock(mutex_); DicomInstanceHasher hasher(dicomSummary); try { - SQLite::Transaction t(db_); - t.Begin(); + std::auto_ptr t(db_->StartTransaction()); + t->Begin(); - if (HasInstance(hasher)) + int64_t patient, study, series, instance; + ResourceType type; + bool isNewSeries = false; + + // Do nothing if the instance already exists + if (db_->LookupResource(hasher.HashInstance(), patient, type)) { + assert(type == ResourceType_Instance); return StoreStatus_AlreadyStored; - // TODO: Check consistency? } - if (HasPatient(hasher)) + // Create the instance + instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance); + + DicomMap dicom; + dicomSummary.ExtractInstanceInformation(dicom); + db_->SetMainDicomTags(instance, dicom); + + // Create the patient/study/series/instance hierarchy + if (!db_->LookupResource(hasher.HashSeries(), series, type)) { - // TODO: Check consistency? + // This is a new series + isNewSeries = true; + series = db_->CreateResource(hasher.HashSeries(), ResourceType_Series); + dicomSummary.ExtractSeriesInformation(dicom); + db_->SetMainDicomTags(series, dicom); + db_->AttachChild(series, instance); + + if (!db_->LookupResource(hasher.HashStudy(), study, type)) + { + // This is a new study + study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study); + dicomSummary.ExtractStudyInformation(dicom); + db_->SetMainDicomTags(study, dicom); + db_->AttachChild(study, series); + + if (!db_->LookupResource(hasher.HashPatient(), patient, type)) + { + // This is a new patient + patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient); + dicomSummary.ExtractPatientInformation(dicom); + db_->SetMainDicomTags(patient, dicom); + db_->AttachChild(patient, study); + } + else + { + assert(type == ResourceType_Patient); + db_->AttachChild(patient, study); + } + } + else + { + assert(type == ResourceType_Study); + db_->AttachChild(study, series); + } } else { - CreatePatient(hasher, dicomSummary); - } - - if (HasStudy(hasher)) - { - // TODO: Check consistency? - } - else - { - CreateStudy(hasher, dicomSummary); + assert(type == ResourceType_Series); + db_->AttachChild(series, instance); } - if (HasSeries(hasher)) + // Attach the files to the newly created instance + db_->AttachFile(instance, AttachedFileType_Dicom, fileUuid, uncompressedFileSize); + db_->AttachFile(instance, AttachedFileType_Json, jsonUuid, 0); // TODO "0" + + // Attach the metadata + db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, Toolbox::GetNowIsoString()); + db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); + + const DicomValue* value; + if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || + (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) { - // TODO: Check consistency? - } - else - { - CreateSeries(hasher, dicomSummary); + db_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); } - CreateInstance(hasher, dicomSummary, fileUuid, - uncompressedFileSize, jsonUuid, remoteAet); - - t.Commit(); + if (isNewSeries) + { + if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL || + (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL || + (value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL) + { + db_->SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString()); + } + } + + t->Commit(); + return StoreStatus_Success; - //t.Rollback(); } catch (OrthancException& e) { - LOG(ERROR) << "EXCEPTION [" << e.What() << "]" << " " << db_.GetErrorMessage(); + LOG(ERROR) << "EXCEPTION2 [" << e.What() << "]" << " " << db_->GetErrorMessage(); } return StoreStatus_Failure; @@ -744,20 +442,20 @@ uint64_t ServerIndex::GetTotalCompressedSize() { boost::mutex::scoped_lock scoped_lock(mutex_); - return db2_->GetTotalCompressedSize(); + return db_->GetTotalCompressedSize(); } uint64_t ServerIndex::GetTotalUncompressedSize() { boost::mutex::scoped_lock scoped_lock(mutex_); - return db2_->GetTotalUncompressedSize(); + return db_->GetTotalUncompressedSize(); } SeriesStatus ServerIndex::GetSeriesStatus(int id) { // Get the expected number of instances in this series (from the metadata) - std::string s = db2_->GetMetadata(id, MetadataType_Series_ExpectedNumberOfInstances); + std::string s = db_->GetMetadata(id, MetadataType_Series_ExpectedNumberOfInstances); size_t expected; try @@ -775,14 +473,14 @@ // Loop over the instances of this series std::list children; - db2_->GetChildrenInternalId(children, id); + db_->GetChildrenInternalId(children, id); std::set instances; for (std::list::const_iterator it = children.begin(); it != children.end(); it++) { // Get the index of this instance in the series - s = db2_->GetMetadata(*it, MetadataType_Instance_IndexInSeries); + s = db_->GetMetadata(*it, MetadataType_Instance_IndexInSeries); size_t index; try { @@ -820,11 +518,11 @@ - void ServerIndex::MainDicomTagsToJson2(Json::Value& target, - int64_t resourceId) + void ServerIndex::MainDicomTagsToJson(Json::Value& target, + int64_t resourceId) { DicomMap tags; - db2_->GetMainDicomTags(tags, resourceId); + db_->GetMainDicomTags(tags, resourceId); target["MainDicomTags"] = Json::objectValue; FromDcmtkBridge::ToJson(target["MainDicomTags"], tags); } @@ -840,7 +538,7 @@ // Lookup for the requested resource int64_t id; ResourceType type; - if (!db2_->LookupResource(publicId, id, type) || + if (!db_->LookupResource(publicId, id, type) || type != expectedType) { return false; @@ -850,12 +548,12 @@ if (type != ResourceType_Patient) { int64_t parentId; - if (!db2_->LookupParent(parentId, id)) + if (!db_->LookupParent(parentId, id)) { throw OrthancException(ErrorCode_InternalError); } - std::string parent = db2_->GetPublicId(parentId); + std::string parent = db_->GetPublicId(parentId); switch (type) { @@ -878,7 +576,7 @@ // List the children resources std::list children; - db2_->GetChildrenPublicId(children, id); + db_->GetChildrenPublicId(children, id); if (type != ResourceType_Instance) { @@ -926,9 +624,9 @@ result["Status"] = ToString(GetSeriesStatus(id)); int i; - if (db2_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) + if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) result["ExpectedNumberOfInstances"] = i; - else + else result["ExpectedNumberOfInstances"] = Json::nullValue; break; @@ -940,7 +638,7 @@ std::string fileUuid; uint64_t uncompressedSize; - if (!db2_->LookupFile(id, AttachedFileType_Dicom, fileUuid, uncompressedSize)) + if (!db_->LookupFile(id, AttachedFileType_Dicom, fileUuid, uncompressedSize)) { throw OrthancException(ErrorCode_InternalError); } @@ -949,7 +647,7 @@ result["FileUuid"] = fileUuid; int i; - if (db2_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) + if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) result["IndexInSeries"] = i; else result["IndexInSeries"] = Json::nullValue; @@ -963,7 +661,7 @@ // Record the remaining information result["ID"] = publicId; - MainDicomTagsToJson2(result, id); + MainDicomTagsToJson(result, id); return true; } @@ -978,7 +676,7 @@ int64_t id; ResourceType type; - if (!db2_->LookupResource(instanceUuid, id, type) || + if (!db_->LookupResource(instanceUuid, id, type) || type != ResourceType_Instance) { throw OrthancException(ErrorCode_InternalError); @@ -986,7 +684,7 @@ uint64_t compressedSize, uncompressedSize; - return db2_->LookupFile(id, contentType, fileUuid, compressedSize, uncompressedSize, compressionType); + return db_->LookupFile(id, contentType, fileUuid, compressedSize, uncompressedSize, compressionType); } @@ -995,7 +693,7 @@ ResourceType resourceType) { boost::mutex::scoped_lock scoped_lock(mutex_); - db2_->GetAllPublicIds(target, resourceType); + db_->GetAllPublicIds(target, resourceType); } @@ -1004,63 +702,66 @@ const std::string& filter, unsigned int maxResults) { - assert(target.type() == Json::objectValue); boost::mutex::scoped_lock scoped_lock(mutex_); + return false; + // TODO !!!! + + /*assert(target.type() == Json::objectValue); + boost::mutex::scoped_lock scoped_lock(mutex_); - if (filter.size() != 0 && - filter != "instances" && - filter != "series" && - filter != "studies" && - filter != "patients") - { + if (filter.size() != 0 && + filter != "instances" && + filter != "series" && + filter != "studies" && + filter != "patients") + { return false; - } + } - std::auto_ptr s; - if (filter.size() == 0) - { + std::auto_ptr s; + if (filter.size() == 0) + { s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " - "ORDER BY seq LIMIT ?")); + "ORDER BY seq LIMIT ?")); s->BindInt64(0, since); s->BindInt(1, maxResults); - } - else - { + } + else + { s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? " - "AND basePath=? ORDER BY seq LIMIT ?")); + "AND basePath=? ORDER BY seq LIMIT ?")); s->BindInt64(0, since); s->BindString(1, filter); s->BindInt(2, maxResults); - } + } - int64_t lastSeq = 0; - Json::Value results(Json::arrayValue); - while (s->Step()) - { + int64_t lastSeq = 0; + Json::Value results(Json::arrayValue); + while (s->Step()) + { int64_t seq = s->ColumnInt64(0); std::string basePath = s->ColumnString(1); std::string uuid = s->ColumnString(2); if (filter.size() == 0 || - filter == basePath) + filter == basePath) { - Json::Value change(Json::objectValue); - change["Seq"] = static_cast(seq); // TODO JsonCpp in 64bit - change["BasePath"] = basePath; - change["ID"] = uuid; - results.append(change); + Json::Value change(Json::objectValue); + change["Seq"] = static_cast(seq); // TODO JsonCpp in 64bit + change["BasePath"] = basePath; + change["ID"] = uuid; + results.append(change); } if (seq > lastSeq) { - lastSeq = seq; + lastSeq = seq; } - } + } - target["Results"] = results; - target["LastSeq"] = static_cast(lastSeq); // TODO JsonCpp in 64bit + target["Results"] = results; + target["LastSeq"] = static_cast(lastSeq); // TODO JsonCpp in 64bit - return true; + return true;*/ } - } diff -r bee20e978835 -r 1650557bd81a OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Tue Nov 27 17:36:19 2012 +0100 +++ b/OrthancServer/ServerIndex.h Tue Nov 27 17:48:37 2012 +0100 @@ -46,67 +46,24 @@ { namespace Internals { - class SignalDeletedLevelFunction; - class ServerIndexListenerTodo; + class ServerIndexListener; } class ServerIndex { private: - SQLite::Connection db_; boost::mutex mutex_; - std::auto_ptr listener2_; - std::auto_ptr db2_; - - // DO NOT delete the following one, SQLite::Connection will do it automatically - Internals::SignalDeletedLevelFunction* deletedLevels_; - - void StoreMainDicomTags(const std::string& uuid, - const DicomMap& map); - - bool HasPatient(DicomInstanceHasher& hasher); - - void CreatePatient(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary); - - bool HasStudy(DicomInstanceHasher& hasher); - - void CreateStudy(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary); + std::auto_ptr listener_; + std::auto_ptr db_; - bool HasSeries(DicomInstanceHasher& hasher); - - void CreateSeries(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary); - - bool HasInstance(DicomInstanceHasher& hasher); - - void CreateInstance(DicomInstanceHasher& hasher, - const DicomMap& dicomSummary, - const std::string& fileUuid, - uint64_t fileSize, - const std::string& jsonUuid, - const std::string& remoteAet); - - void RecordChange(const std::string& resourceType, - const std::string& uuid); - - void RemoveInstance(const std::string& uuid); - - void MainDicomTagsToJson2(Json::Value& result, - int64_t resourceId); + void MainDicomTagsToJson(Json::Value& result, + int64_t resourceId); bool DeleteInternal(Json::Value& target, const std::string& uuid, - const std::string& tableName); - - StoreStatus Store2(const DicomMap& dicomSummary, - const std::string& fileUuid, - uint64_t uncompressedFileSize, - const std::string& jsonUuid, - const std::string& remoteAet); + ResourceType expectedType); bool LookupResource(Json::Value& result, const std::string& publicId, @@ -172,25 +129,25 @@ bool DeletePatient(Json::Value& target, const std::string& patientUuid) { - return DeleteInternal(target, patientUuid, "Patients"); + return DeleteInternal(target, patientUuid, ResourceType_Patient); } bool DeleteStudy(Json::Value& target, const std::string& studyUuid) { - return DeleteInternal(target, studyUuid, "Studies"); + return DeleteInternal(target, studyUuid, ResourceType_Study); } bool DeleteSeries(Json::Value& target, const std::string& seriesUuid) { - return DeleteInternal(target, seriesUuid, "Series"); + return DeleteInternal(target, seriesUuid, ResourceType_Series); } bool DeleteInstance(Json::Value& target, const std::string& instanceUuid) { - return DeleteInternal(target, instanceUuid, "Instances"); + return DeleteInternal(target, instanceUuid, ResourceType_Instance); } bool GetChanges(Json::Value& target,