Mercurial > hg > orthanc-databases
changeset 677:5c8ed62ccbc5 sql-opti
re-implemented PatientRecyclingOrder with metadata
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 03 Jun 2025 17:55:51 +0200 |
parents | aa2e2d47465d |
children | 91543bb29a29 |
files | PostgreSQL/CMakeLists.txt PostgreSQL/NEWS PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/PostgreSQLIndex.h PostgreSQL/Plugins/SQL/Downgrades/Rev5ToRev4.sql PostgreSQL/Plugins/SQL/Downgrades/Rev99ToRev4.sql PostgreSQL/Plugins/SQL/PrepareIndex.sql PostgreSQL/Plugins/SQL/Upgrades/Rev4ToRev5.sql PostgreSQL/Plugins/SQL/Upgrades/Rev4ToRev99.sql |
diffstat | 9 files changed, 364 insertions(+), 135 deletions(-) [+] |
line wrap: on
line diff
--- a/PostgreSQL/CMakeLists.txt Wed Apr 23 17:39:48 2025 +0200 +++ b/PostgreSQL/CMakeLists.txt Tue Jun 03 17:55:51 2025 +0200 @@ -94,7 +94,7 @@ POSTGRESQL_UPGRADE_REV1_TO_REV2 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev1ToRev2.sql POSTGRESQL_UPGRADE_REV2_TO_REV3 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev2ToRev3.sql POSTGRESQL_UPGRADE_REV3_TO_REV4 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev3ToRev4.sql - POSTGRESQL_UPGRADE_REV4_TO_REV5 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev4ToRev5.sql + POSTGRESQL_UPGRADE_REV4_TO_REV99 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev4ToRev99.sql )
--- a/PostgreSQL/NEWS Wed Apr 23 17:39:48 2025 +0200 +++ b/PostgreSQL/NEWS Tue Jun 03 17:55:51 2025 +0200 @@ -13,6 +13,8 @@ Maintenance: * Optimized the CreateInstance SQL query. +* Internal: The PatientRecyclingOrder has been refactored and is now stored in + Patient metadata (18: IsProtected, 19: PatientRecyclingOrder) Release 7.2 (2025-02-27)
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Wed Apr 23 17:39:48 2025 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Tue Jun 03 17:55:51 2025 +0200 @@ -49,7 +49,7 @@ static const GlobalProperty GlobalProperty_HasComputeStatisticsReadOnly = GlobalProperty_DatabaseInternal4; } -#define CURRENT_DB_REVISION 5 +#define CURRENT_DB_REVISION 99 namespace OrthancDatabases { @@ -242,10 +242,10 @@ std::string query; Orthanc::EmbeddedResources::GetFileResource - (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV4_TO_REV5); + (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV4_TO_REV99); t.GetDatabaseTransaction().ExecuteMultiLines(query); hasAppliedAnUpgrade = true; - currentRevision = 5; + currentRevision = 99; } if (hasAppliedAnUpgrade) @@ -783,6 +783,111 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); } + void PostgreSQLIndex::SetProtectedPatient(DatabaseManager& manager, + int64_t internalId, + bool isProtected) + { + std::unique_ptr<DatabaseManager::CachedStatement> statement; + Dictionary args; + + if (isProtected) + { + statement.reset(new DatabaseManager::CachedStatement( + STATEMENT_FROM_HERE, manager, + "SELECT ProtectPatient(${id})")); + } + else + { + statement.reset(new DatabaseManager::CachedStatement( + STATEMENT_FROM_HERE, manager, + "SELECT UnprotectPatient(${id})")); + } + + statement->SetParameterType("id", ValueType_Integer64); + args.SetIntegerValue("id", internalId); + + statement->Execute(args); + } + + bool PostgreSQLIndex::IsProtectedPatient(DatabaseManager& manager, + int64_t internalId) + { + std::string value; + int64_t revision; + + if (LookupMetadata(value, revision, manager, internalId, 18)) // 18 = IsProtected + { + return value == "true"; + } + + return false; + } + + bool PostgreSQLIndex::SelectPatientToRecycle(int64_t& internalId /*out*/, + DatabaseManager& manager) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "SELECT r.internalId " + "FROM Resources r " + "JOIN Metadata m ON r.internalId = m.id AND m.type = 19 " // 19 = PatientRecyclingOrder + "WHERE r.resourceType = 0 " + " AND NOT EXISTS " + " (SELECT 1 FROM Metadata m " + " WHERE m.id = r.internalId AND m.type = 18 AND m.value = 'true') " // 18 = IsProtected + " ORDER BY CAST(m.value AS INTEGER) ASC LIMIT 1;"); + + statement.SetReadOnly(true); + statement.Execute(); + + if (statement.IsDone()) + { + return false; + } + else + { + internalId = statement.ReadInteger64(0); + return true; + } + } + + + bool PostgreSQLIndex::SelectPatientToRecycle(int64_t& internalId /*out*/, + DatabaseManager& manager, + int64_t patientIdToAvoid) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "SELECT r.internalId " + "FROM Resources r " + "JOIN Metadata m ON r.internalId = m.id AND m.type = 19 " // 19 = PatientRecyclingOrder + "WHERE r.resourceType = 0 " + " AND r.internalId != ${id} " + " AND NOT EXISTS " + " (SELECT 1 FROM Metadata m " + " WHERE m.id = r.internalId AND m.type = 18 AND m.value = 'true') " // 18 = IsProtected + " ORDER BY CAST(m.value AS INTEGER) ASC LIMIT 1;"); + + statement.SetReadOnly(true); + statement.SetParameterType("id", ValueType_Integer64); + + Dictionary args; + args.SetIntegerValue("id", patientIdToAvoid); + + statement.Execute(args); + + if (statement.IsDone()) + { + return false; + } + else + { + internalId = statement.ReadInteger64(0); + return true; + } + } + + bool PostgreSQLIndex::HasPerformDbHousekeeping() { return !IsReadOnly(); // Don't start HK on ReadOnly databases !
--- a/PostgreSQL/Plugins/PostgreSQLIndex.h Wed Apr 23 17:39:48 2025 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.h Tue Jun 03 17:55:51 2025 +0200 @@ -112,9 +112,24 @@ virtual int64_t GetLastChangeIndex(DatabaseManager& manager) ORTHANC_OVERRIDE; + // This is now obsolete virtual void TagMostRecentPatient(DatabaseManager& manager, int64_t patient) ORTHANC_OVERRIDE; + virtual void SetProtectedPatient(DatabaseManager& manager, + int64_t internalId, + bool isProtected) ORTHANC_OVERRIDE; + + virtual bool IsProtectedPatient(DatabaseManager& manager, + int64_t internalId) ORTHANC_OVERRIDE; + + virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, + DatabaseManager& manager) ORTHANC_OVERRIDE; + + virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, + DatabaseManager& manager, + int64_t patientIdToAvoid) ORTHANC_OVERRIDE; + // New primitive since Orthanc 1.12.0 virtual bool HasLabelsSupport() const ORTHANC_OVERRIDE {
--- a/PostgreSQL/Plugins/SQL/Downgrades/Rev5ToRev4.sql Wed Apr 23 17:39:48 2025 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ --- This file contains an SQL procedure to downgrade from schema Rev5 to Rev4 (version = 6). - -- It re-installs the old CreateInstance method - - ------------ - -CREATE OR REPLACE FUNCTION CreateInstance( - IN patient_public_id TEXT, - IN study_public_id TEXT, - IN series_public_id TEXT, - IN instance_public_id TEXT, - OUT is_new_patient BIGINT, - OUT is_new_study BIGINT, - OUT is_new_series BIGINT, - OUT is_new_instance BIGINT, - OUT patient_internal_id BIGINT, - OUT study_internal_id BIGINT, - OUT series_internal_id BIGINT, - OUT instance_internal_id BIGINT) AS $body$ - -BEGIN - is_new_patient := 1; - is_new_study := 1; - is_new_series := 1; - is_new_instance := 1; - - BEGIN - INSERT INTO "resources" VALUES (DEFAULT, 0, patient_public_id, NULL, 0) RETURNING internalid INTO patient_internal_id; - EXCEPTION - WHEN unique_violation THEN - is_new_patient := 0; - SELECT internalid INTO patient_internal_id FROM "resources" WHERE publicId = patient_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction - END; - - BEGIN - INSERT INTO "resources" VALUES (DEFAULT, 1, study_public_id, patient_internal_id, 0) RETURNING internalid INTO study_internal_id; - EXCEPTION - WHEN unique_violation THEN - is_new_study := 0; - SELECT internalid INTO study_internal_id FROM "resources" WHERE publicId = study_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction END; - END; - - BEGIN - INSERT INTO "resources" VALUES (DEFAULT, 2, series_public_id, study_internal_id, 0) RETURNING internalid INTO series_internal_id; - EXCEPTION - WHEN unique_violation THEN - is_new_series := 0; - SELECT internalid INTO series_internal_id FROM "resources" WHERE publicId = series_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction END; - END; - - BEGIN - INSERT INTO "resources" VALUES (DEFAULT, 3, instance_public_id, series_internal_id, 0) RETURNING internalid INTO instance_internal_id; - EXCEPTION - WHEN unique_violation THEN - is_new_instance := 0; - SELECT internalid INTO instance_internal_id FROM "resources" WHERE publicId = instance_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction - END; - - IF is_new_instance > 0 THEN - -- Move the patient to the end of the recycling order. - PERFORM PatientAddedOrUpdated(patient_internal_id, 1); - END IF; -END; -$body$ LANGUAGE plpgsql; - ----------- - --- set the global properties that actually documents the DB version, revision and some of the capabilities --- modify only the ones that have changed -DELETE FROM GlobalProperties WHERE property IN (4, 11); -INSERT INTO GlobalProperties VALUES (4, 4); -- GlobalProperty_DatabasePatchLevel
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PostgreSQL/Plugins/SQL/Downgrades/Rev99ToRev4.sql Tue Jun 03 17:55:51 2025 +0200 @@ -0,0 +1,149 @@ +-- This file contains an SQL procedure to downgrade from schema Rev99 to Rev4 (version = 6). + + +-- Re-installs the old PatientRecycling +----------- + +CREATE TABLE IF NOT EXISTS PatientRecyclingOrder( + seq BIGSERIAL NOT NULL PRIMARY KEY, + patientId BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE, + CONSTRAINT UniquePatientId UNIQUE (patientId) + ); + +CREATE INDEX IF NOT EXISTS PatientRecyclingIndex ON PatientRecyclingOrder(patientId); + +DROP TRIGGER IF EXISTS PatientAdded ON Resources; + +CREATE OR REPLACE FUNCTION PatientAddedOrUpdated( + IN patient_id BIGINT, + IN is_update BIGINT + ) +RETURNS VOID AS $body$ +BEGIN + DECLARE + newSeq BIGINT; + BEGIN + IF is_update > 0 THEN + -- Note: Protected patients are not listed in this table ! So, they won't be updated + WITH deleted_rows AS ( + DELETE FROM PatientRecyclingOrder + WHERE PatientRecyclingOrder.patientId = patient_id + RETURNING patientId + ) + INSERT INTO PatientRecyclingOrder (patientId) + SELECT patientID FROM deleted_rows + WHERE EXISTS(SELECT 1 FROM deleted_rows); + ELSE + INSERT INTO PatientRecyclingOrder VALUES (DEFAULT, patient_id); + END IF; + END; +END; +$body$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION PatientAddedFunc() +RETURNS TRIGGER AS $body$ +BEGIN + -- The "0" corresponds to "OrthancPluginResourceType_Patient" + IF new.resourceType = 0 THEN + PERFORM PatientAddedOrUpdated(new.internalId, 0); + END IF; + RETURN NULL; +END; +$body$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS PatientAdded on Resources; +CREATE TRIGGER PatientAdded +AFTER INSERT ON Resources +FOR EACH ROW +EXECUTE PROCEDURE PatientAddedFunc(); + +DROP FUNCTION IF EXISTS ProtectPatient(patient_id BIGINT); + +DROP FUNCTION IF EXISTS UnprotectPatient; + +-- repopulate the PatientRecyclingOrderTable +WITH UnprotectedPatients AS (SELECT r.internalId + FROM Resources r + RIGHT JOIN Metadata m ON r.internalId = m.id AND m.type = 19 -- 19 = PatientRecyclingOrder + WHERE r.resourceType = 0 + AND NOT EXISTS (SELECT 1 FROM Metadata m + WHERE m.id = r.internalId AND m.type = 18 AND m.value = 'true') -- 18 = IsProtected + ORDER BY CAST(m.value AS INTEGER) ASC) + +INSERT INTO PatientRecyclingOrder (patientId) +SELECT internalId +FROM UnprotectedPatients; + +DROP SEQUENCE IF EXISTS PatientRecyclingOrderSequence; + +-- remove the IsProtected and PatientRecyclingOrder metadata +DELETE FROM Metadata WHERE type IN (18, 19); + +-- Re-installs the old CreateInstance method +----------- + +CREATE OR REPLACE FUNCTION CreateInstance( + IN patient_public_id TEXT, + IN study_public_id TEXT, + IN series_public_id TEXT, + IN instance_public_id TEXT, + OUT is_new_patient BIGINT, + OUT is_new_study BIGINT, + OUT is_new_series BIGINT, + OUT is_new_instance BIGINT, + OUT patient_internal_id BIGINT, + OUT study_internal_id BIGINT, + OUT series_internal_id BIGINT, + OUT instance_internal_id BIGINT) AS $body$ + +BEGIN + is_new_patient := 1; + is_new_study := 1; + is_new_series := 1; + is_new_instance := 1; + + BEGIN + INSERT INTO "resources" VALUES (DEFAULT, 0, patient_public_id, NULL, 0) RETURNING internalid INTO patient_internal_id; + EXCEPTION + WHEN unique_violation THEN + is_new_patient := 0; + SELECT internalid INTO patient_internal_id FROM "resources" WHERE publicId = patient_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction + END; + + BEGIN + INSERT INTO "resources" VALUES (DEFAULT, 1, study_public_id, patient_internal_id, 0) RETURNING internalid INTO study_internal_id; + EXCEPTION + WHEN unique_violation THEN + is_new_study := 0; + SELECT internalid INTO study_internal_id FROM "resources" WHERE publicId = study_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction END; + END; + + BEGIN + INSERT INTO "resources" VALUES (DEFAULT, 2, series_public_id, study_internal_id, 0) RETURNING internalid INTO series_internal_id; + EXCEPTION + WHEN unique_violation THEN + is_new_series := 0; + SELECT internalid INTO series_internal_id FROM "resources" WHERE publicId = series_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction END; + END; + + BEGIN + INSERT INTO "resources" VALUES (DEFAULT, 3, instance_public_id, series_internal_id, 0) RETURNING internalid INTO instance_internal_id; + EXCEPTION + WHEN unique_violation THEN + is_new_instance := 0; + SELECT internalid INTO instance_internal_id FROM "resources" WHERE publicId = instance_public_id FOR UPDATE; -- also locks the resource and its parent to prevent from deletion while we complete this transaction + END; + + IF is_new_instance > 0 THEN + -- Move the patient to the end of the recycling order. + PERFORM PatientAddedOrUpdated(patient_internal_id, 1); + END IF; +END; +$body$ LANGUAGE plpgsql; + +---------- + +-- set the global properties that actually documents the DB version, revision and some of the capabilities +-- modify only the ones that have changed +DELETE FROM GlobalProperties WHERE property IN (4, 11); +INSERT INTO GlobalProperties VALUES (4, 4); -- GlobalProperty_DatabasePatchLevel
--- a/PostgreSQL/Plugins/SQL/PrepareIndex.sql Wed Apr 23 17:39:48 2025 +0200 +++ b/PostgreSQL/Plugins/SQL/PrepareIndex.sql Tue Jun 03 17:55:51 2025 +0200 @@ -76,12 +76,6 @@ date VARCHAR(64) ); -CREATE TABLE IF NOT EXISTS PatientRecyclingOrder( - seq BIGSERIAL NOT NULL PRIMARY KEY, - patientId BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE, - CONSTRAINT UniquePatientId UNIQUE (patientId) - ); - CREATE TABLE IF NOT EXISTS Labels( id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE, label TEXT, @@ -126,7 +120,6 @@ CREATE INDEX IF NOT EXISTS PublicIndex ON Resources(publicId); CREATE INDEX IF NOT EXISTS ResourceTypeIndex ON Resources(resourceType); -CREATE INDEX IF NOT EXISTS PatientRecyclingIndex ON PatientRecyclingOrder(patientId); CREATE INDEX IF NOT EXISTS MainDicomTagsIndex ON MainDicomTags(id); CREATE INDEX IF NOT EXISTS DicomIdentifiersIndex1 ON DicomIdentifiers(id); @@ -158,62 +151,24 @@ END $body$; -------------------- PatientAdded trigger & PatientRecyclingOrder ------------------- -DROP TRIGGER IF EXISTS PatientAdded ON Resources; - +--------------------- PatientRecyclingOrder ------------------- +-- from rev 99, we always maintain a PatientRecyclingOrder metadata, no matter if the patient is protected or not CREATE OR REPLACE FUNCTION PatientAddedOrUpdated( - IN patient_id BIGINT, - IN is_update BIGINT + IN patient_id BIGINT ) RETURNS VOID AS $body$ BEGIN DECLARE newSeq BIGINT; BEGIN - IF is_update > 0 THEN - -- Note: Protected patients are not listed in this table ! So, they won't be updated - WITH deleted_rows AS ( - DELETE FROM PatientRecyclingOrder - WHERE PatientRecyclingOrder.patientId = patient_id - RETURNING patientId - ) - INSERT INTO PatientRecyclingOrder (patientId) - SELECT patientID FROM deleted_rows - WHERE EXISTS(SELECT 1 FROM deleted_rows); - ELSE - INSERT INTO PatientRecyclingOrder VALUES (DEFAULT, patient_id); - END IF; + INSERT INTO Metadata (id, type, value, revision) + VALUES (patient_id, 19, nextval('PatientRecyclingOrderSequence')::TEXT, 0) + ON CONFLICT (id, type) + DO UPDATE SET value = EXCLUDED.value, revision = EXCLUDED.revision; END; END; $body$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION PatientAddedFunc() -RETURNS TRIGGER AS $body$ -BEGIN - -- The "0" corresponds to "OrthancPluginResourceType_Patient" - IF new.resourceType = 0 THEN - PERFORM PatientAddedOrUpdated(new.internalId, 0); - END IF; - RETURN NULL; -END; -$body$ LANGUAGE plpgsql; - -DROP TRIGGER IF EXISTS PatientAdded on Resources; -CREATE TRIGGER PatientAdded -AFTER INSERT ON Resources -FOR EACH ROW -EXECUTE PROCEDURE PatientAddedFunc(); - --- initial population of -SELECT setval('patientrecyclingorder_seq_seq', MAX(seq)) FROM PatientRecyclingOrder; -DELETE FROM GlobalIntegers WHERE key = 7; - -- UPDATE GlobalIntegers SET value = value + 1 WHERE key = 7 RETURNING value INTO newSeq; - --- INSERT INTO GlobalIntegers --- SELECT 7, CAST(COALESCE(MAX(seq), 0) AS BIGINT) FROM PatientRecyclingOrder --- ON CONFLICT DO NOTHING; - - ------------------- ResourceDeleted trigger ------------------- DROP TRIGGER IF EXISTS ResourceDeleted ON Resources; @@ -534,7 +489,8 @@ OUT instance_internal_id BIGINT) AS $body$ BEGIN - -- assume the parents already exists + -- Assume the parent series already exists to minimize exceptions. + -- Most of the instances are not the first of their series - especially when we need high performances. is_new_patient := 1; is_new_study := 1; @@ -601,10 +557,10 @@ END IF; - -- IF is_new_instance > 0 THEN - -- -- Move the patient to the end of the recycling order. - -- PERFORM PatientAddedOrUpdated(patient_internal_id, 1); - -- END IF; + IF is_new_instance > 0 THEN + -- Move the patient to the end of the recycling order. + PERFORM PatientAddedOrUpdated(patient_internal_id); + END IF; END; $body$ LANGUAGE plpgsql; @@ -748,11 +704,39 @@ EXECUTE PROCEDURE UpdateChildCount(); +-- new in rev 99 + +CREATE SEQUENCE IF NOT EXISTS PatientRecyclingOrderSequence INCREMENT 1 START 1; + +CREATE OR REPLACE FUNCTION ProtectPatient(patient_id BIGINT) +RETURNS VOID AS $$ +BEGIN + INSERT INTO Metadata (id, type, value, revision) -- 18 = IsProtected + VALUES (patient_id, 18, 'true', 0) + ON CONFLICT (id, type) + DO UPDATE SET value = EXCLUDED.value, revision = EXCLUDED.revision; +END; +$$ LANGUAGE plpgsql; + +-- remove IsProtected and update PatientRecyclingOrder +CREATE OR REPLACE FUNCTION UnprotectPatient(patient_id BIGINT) +RETURNS VOID AS $$ +BEGIN + DELETE FROM Metadata WHERE id = patient_id AND type = 18; -- 18 = IsProtected + + INSERT INTO Metadata (id, type, value, revision) + VALUES (patient_id, 19, nextval('PatientRecyclingOrderSequence')::TEXT, 0) + ON CONFLICT (id, type) + DO UPDATE SET value = EXCLUDED.value, revision = EXCLUDED.revision; +END; +$$ LANGUAGE plpgsql; + + -- set the global properties that actually documents the DB version, revision and some of the capabilities DELETE FROM GlobalProperties WHERE property IN (1, 4, 6, 10, 11, 12, 13, 14); INSERT INTO GlobalProperties VALUES (1, 6); -- GlobalProperty_DatabaseSchemaVersion -INSERT INTO GlobalProperties VALUES (4, 5); -- GlobalProperty_DatabasePatchLevel +INSERT INTO GlobalProperties VALUES (4, 99); -- GlobalProperty_DatabasePatchLevel INSERT INTO GlobalProperties VALUES (6, 1); -- GlobalProperty_GetTotalSizeIsFast INSERT INTO GlobalProperties VALUES (10, 1); -- GlobalProperty_HasTrigramIndex INSERT INTO GlobalProperties VALUES (11, 3); -- GlobalProperty_HasCreateInstance -- this is actually the 3rd version of HasCreateInstance
--- a/PostgreSQL/Plugins/SQL/Upgrades/Rev4ToRev5.sql Wed Apr 23 17:39:48 2025 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ --- everything is performed in PrepareIndex.sql -SELECT 1; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PostgreSQL/Plugins/SQL/Upgrades/Rev4ToRev99.sql Tue Jun 03 17:55:51 2025 +0200 @@ -0,0 +1,47 @@ +CREATE SEQUENCE IF NOT EXISTS PatientRecyclingOrderSequence INCREMENT 1 START 1; + +-- the protection mechanisms changed in rev 99. We now use a metadata (18: IsProtected) +-- while, in the past, patients where protected by not appearing in the PatientRecyclingOrder + +-- Step 1: Identify all patients that are not in PatientRecyclingOrder (those are the protected patients) +-- The "0" corresponds to "OrthancPluginResourceType_Patient" +WITH ProtectedPatients AS ( + SELECT r.internalId AS internalId + FROM Resources r + LEFT JOIN PatientRecyclingOrder pro ON r.internalId = pro.patientId + WHERE r.resourceType = 0 + AND pro.patientId IS NULL +) +, UnprotectedPatients AS ( + SELECT patientId AS internalId + FROM PatientRecyclingOrder + ORDER BY seq ASC +) + +-- Step 2: Prepare the data for both metadata types +, MetadataToInsert AS ( + -- mark protected patient in 18: IsProtected + SELECT internalId, 18 AS metadataType, 'true' AS metadataValue + FROM ProtectedPatients + + UNION ALL + + -- copy previous recycling order in 19: RecyclingOrder + SELECT internalId, 19 AS metadataType, nextval('PatientRecyclingOrderSequence')::TEXT AS metadataValue + FROM UnprotectedPatients +) + +-- Step 3: Insert the Metadata (since the metadata are new, there should not be any conflicts) +INSERT INTO Metadata (id, type, value, revision) +SELECT internalId, metadataType, metadataValue, 0 +FROM MetadataToInsert +ON CONFLICT (id, type) +DO UPDATE SET value = EXCLUDED.value, revision = EXCLUDED.revision; + +-- The PatientRecyclingOrder table can now be removed + +DROP TABLE PatientRecyclingOrder; + +DROP TRIGGER IF EXISTS PatientAdded on Resources; +DROP FUNCTION IF EXISTS PatientAddedFunc; +DROP FUNCTION IF EXISTS PatientAddedOrUpdated; \ No newline at end of file