Mercurial > hg > orthanc-databases
changeset 710:2f2036e0f352 sql-opti tip
renamed revision 599 into 6
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 22 Jul 2025 10:22:34 +0200 (2 days ago) |
parents | a88a2776fea4 |
children | |
files | Framework/Plugins/IDatabaseBackend.h Framework/Plugins/IndexBackend.cpp PostgreSQL/CMakeLists.txt PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/SQL/Downgrades/Rev599ToRev5.sql PostgreSQL/Plugins/SQL/Downgrades/Rev6ToRev5.sql PostgreSQL/Plugins/SQL/PrepareIndex.sql PostgreSQL/Plugins/SQL/Upgrades/Rev5ToRev599.sql PostgreSQL/Plugins/SQL/Upgrades/Rev5ToRev6.sql |
diffstat | 9 files changed, 321 insertions(+), 321 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Plugins/IDatabaseBackend.h Thu Jul 17 12:35:36 2025 +0200 +++ b/Framework/Plugins/IDatabaseBackend.h Tue Jul 22 10:22:34 2025 +0200 @@ -39,14 +39,14 @@ public: struct AuditLog { - uint64_t timeStamp; + std::string timeStamp; std::string userId; OrthancPluginResourceType resourceType; std::string resourceId; std::string action; std::string logData; - AuditLog(uint64_t timeStamp, + AuditLog(const std::string& timeStamp, const std::string& userId, OrthancPluginResourceType resourceType, const std::string& resourceId,
--- a/Framework/Plugins/IndexBackend.cpp Thu Jul 17 12:35:36 2025 +0200 +++ b/Framework/Plugins/IndexBackend.cpp Tue Jul 22 10:22:34 2025 +0200 @@ -4736,7 +4736,7 @@ LookupFormatter formatter(manager.GetDialect()); std::vector<std::string> filters; - std::string sql = "SELECT ts, userId, resourceType, resourceId, action, logData FROM AuditLogs "; + std::string sql = "SELECT to_char(ts, 'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"'), userId, resourceType, resourceId, action, logData FROM AuditLogs "; if (!userIdFilter.empty()) { @@ -4789,7 +4789,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } - statement.SetResultFieldType(0, ValueType_Integer64); + statement.SetResultFieldType(0, ValueType_Utf8String); statement.SetResultFieldType(1, ValueType_Utf8String); statement.SetResultFieldType(2, ValueType_Integer64); statement.SetResultFieldType(3, ValueType_Utf8String); @@ -4798,7 +4798,7 @@ while (!statement.IsDone()) { - logs.push_back(AuditLog(statement.ReadInteger64(0), + logs.push_back(AuditLog(statement.ReadString(0), statement.ReadString(1), static_cast<OrthancPluginResourceType>(statement.ReadInteger64(2)), statement.ReadString(3),
--- a/PostgreSQL/CMakeLists.txt Thu Jul 17 12:35:36 2025 +0200 +++ b/PostgreSQL/CMakeLists.txt Tue Jul 22 10:22:34 2025 +0200 @@ -95,7 +95,7 @@ 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_REV5_TO_REV599 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev5ToRev599.sql + POSTGRESQL_UPGRADE_REV5_TO_REV6 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev5ToRev6.sql )
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Thu Jul 17 12:35:36 2025 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Tue Jul 22 10:22:34 2025 +0200 @@ -49,7 +49,7 @@ static const GlobalProperty GlobalProperty_HasComputeStatisticsReadOnly = GlobalProperty_DatabaseInternal4; } -#define CURRENT_DB_REVISION 599 +#define CURRENT_DB_REVISION 6 namespace OrthancDatabases { @@ -250,15 +250,15 @@ if (currentRevision == 5) { - LOG(WARNING) << "Upgrading DB schema from revision 5 to revision 599"; + LOG(WARNING) << "Upgrading DB schema from revision 5 to revision 6"; std::string query; Orthanc::EmbeddedResources::GetFileResource - (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV5_TO_REV599); + (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV5_TO_REV6); t.GetDatabaseTransaction().ExecuteMultiLines(query); hasAppliedAnUpgrade = true; - currentRevision = 599; + currentRevision = 6; } if (hasAppliedAnUpgrade)
--- a/PostgreSQL/Plugins/SQL/Downgrades/Rev599ToRev5.sql Thu Jul 17 12:35:36 2025 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ --- This file contains an SQL procedure to downgrade from schema Rev599 to Rev5 (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; - --- Restore the DeleteResource function that has been optimized - -------------------- DeleteResource function ------------------- - -CREATE OR REPLACE FUNCTION DeleteResource( - IN id BIGINT, - OUT remaining_ancestor_resource_type INTEGER, - OUT remaining_anncestor_public_id TEXT) AS $body$ - -DECLARE - deleted_row RECORD; - locked_row RECORD; - -BEGIN - - SET client_min_messages = warning; -- suppress NOTICE: relation "deletedresources" already exists, skipping - - -- note: temporary tables are created at session (connection) level -> they are likely to exist - -- these tables are used by the triggers - CREATE TEMPORARY TABLE IF NOT EXISTS DeletedResources( - resourceType INTEGER NOT NULL, - publicId VARCHAR(64) NOT NULL - ); - - RESET client_min_messages; - - -- clear the temporary table in case it has been created earlier in the session - DELETE FROM DeletedResources; - - -- create/clear the DeletedFiles temporary table - PERFORM CreateDeletedFilesTemporaryTable(); - - -- Before deleting an object, we need to lock its parent until the end of the transaction to avoid that - -- 2 threads deletes the last 2 instances of a series at the same time -> none of them would realize - -- that they are deleting the last instance and the parent resources would not be deleted. - -- Locking only the immediate parent is sufficient to prevent from this. - SELECT * INTO locked_row FROM resources WHERE internalid = (SELECT parentid FROM resources WHERE internalid = id) FOR UPDATE; - - -- delete the resource itself - DELETE FROM Resources WHERE internalId=id RETURNING * INTO deleted_row; - -- note: there is a ResourceDeletedFunc trigger that will execute here and delete the parents if there are no remaining children + - - -- If this resource still has siblings, keep track of the remaining parent - -- (a parent that must not be deleted but whose LastUpdate must be updated) - SELECT resourceType, publicId INTO remaining_ancestor_resource_type, remaining_anncestor_public_id - FROM Resources - WHERE internalId = deleted_row.parentId - AND EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_row.parentId); - -END; - -$body$ LANGUAGE plpgsql; - - --- restore the DeletedResource trigger - -------------------- ResourceDeleted trigger ------------------- -DROP TRIGGER IF EXISTS ResourceDeleted ON Resources; - --- The following trigger combines 2 triggers from SQLite: --- ResourceDeleted + ResourceDeletedParentCleaning -CREATE OR REPLACE FUNCTION ResourceDeletedFunc() -RETURNS TRIGGER AS $body$ -BEGIN - -- RAISE NOTICE 'ResourceDeletedFunc %', old.publicId; - INSERT INTO DeletedResources VALUES (old.resourceType, old.publicId); - - -- If this resource is the latest child, delete the parent - DELETE FROM Resources WHERE internalId = old.parentId - AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = old.parentId); - RETURN NULL; -END; -$body$ LANGUAGE plpgsql; - -DROP TRIGGER IF EXISTS ResourceDeleted on Resources; -CREATE TRIGGER ResourceDeleted -AFTER DELETE ON Resources -FOR EACH ROW -EXECUTE PROCEDURE ResourceDeletedFunc(); - - --- remove the new DeleteAttachment function - -DROP FUNCTION IF EXISTS DeleteAttachment; - --- Restore the ON DELETE CASCADE on the Resources.parentId --- Drop the existing foreign key constraint and add a new one without ON DELETE CASCADE in a single command -ALTER TABLE Resources -DROP CONSTRAINT IF EXISTS resources_parentid_fkey, -ADD CONSTRAINT resources_parentid_fkey FOREIGN KEY (parentId) REFERENCES Resources(internalId) ON DELETE CASCADE; - - --- Remove the AuditLogs table ------------ - -DROP INDEX IF EXISTS AuditLogsUserId; -DROP INDEX IF EXISTS AuditLogsResourceId; -DROP INDEX IF EXISTS AuditLogsAction; -DROP TABLE IF EXISTS AuditLogs; - - ----------- - --- 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, 5); -- GlobalProperty_DatabasePatchLevel
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PostgreSQL/Plugins/SQL/Downgrades/Rev6ToRev5.sql Tue Jul 22 10:22:34 2025 +0200 @@ -0,0 +1,250 @@ +-- This file contains an SQL procedure to downgrade from schema 6 to Rev5 (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; + +-- Restore the DeleteResource function that has been optimized + +------------------- DeleteResource function ------------------- + +CREATE OR REPLACE FUNCTION DeleteResource( + IN id BIGINT, + OUT remaining_ancestor_resource_type INTEGER, + OUT remaining_anncestor_public_id TEXT) AS $body$ + +DECLARE + deleted_row RECORD; + locked_row RECORD; + +BEGIN + + SET client_min_messages = warning; -- suppress NOTICE: relation "deletedresources" already exists, skipping + + -- note: temporary tables are created at session (connection) level -> they are likely to exist + -- these tables are used by the triggers + CREATE TEMPORARY TABLE IF NOT EXISTS DeletedResources( + resourceType INTEGER NOT NULL, + publicId VARCHAR(64) NOT NULL + ); + + RESET client_min_messages; + + -- clear the temporary table in case it has been created earlier in the session + DELETE FROM DeletedResources; + + -- create/clear the DeletedFiles temporary table + PERFORM CreateDeletedFilesTemporaryTable(); + + -- Before deleting an object, we need to lock its parent until the end of the transaction to avoid that + -- 2 threads deletes the last 2 instances of a series at the same time -> none of them would realize + -- that they are deleting the last instance and the parent resources would not be deleted. + -- Locking only the immediate parent is sufficient to prevent from this. + SELECT * INTO locked_row FROM resources WHERE internalid = (SELECT parentid FROM resources WHERE internalid = id) FOR UPDATE; + + -- delete the resource itself + DELETE FROM Resources WHERE internalId=id RETURNING * INTO deleted_row; + -- note: there is a ResourceDeletedFunc trigger that will execute here and delete the parents if there are no remaining children + + + -- If this resource still has siblings, keep track of the remaining parent + -- (a parent that must not be deleted but whose LastUpdate must be updated) + SELECT resourceType, publicId INTO remaining_ancestor_resource_type, remaining_anncestor_public_id + FROM Resources + WHERE internalId = deleted_row.parentId + AND EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_row.parentId); + +END; + +$body$ LANGUAGE plpgsql; + + +-- restore the DeletedResource trigger + +------------------- ResourceDeleted trigger ------------------- +DROP TRIGGER IF EXISTS ResourceDeleted ON Resources; + +-- The following trigger combines 2 triggers from SQLite: +-- ResourceDeleted + ResourceDeletedParentCleaning +CREATE OR REPLACE FUNCTION ResourceDeletedFunc() +RETURNS TRIGGER AS $body$ +BEGIN + -- RAISE NOTICE 'ResourceDeletedFunc %', old.publicId; + INSERT INTO DeletedResources VALUES (old.resourceType, old.publicId); + + -- If this resource is the latest child, delete the parent + DELETE FROM Resources WHERE internalId = old.parentId + AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = old.parentId); + RETURN NULL; +END; +$body$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS ResourceDeleted on Resources; +CREATE TRIGGER ResourceDeleted +AFTER DELETE ON Resources +FOR EACH ROW +EXECUTE PROCEDURE ResourceDeletedFunc(); + + +-- remove the new DeleteAttachment function + +DROP FUNCTION IF EXISTS DeleteAttachment; + +-- Restore the ON DELETE CASCADE on the Resources.parentId +-- Drop the existing foreign key constraint and add a new one without ON DELETE CASCADE in a single command +ALTER TABLE Resources +DROP CONSTRAINT IF EXISTS resources_parentid_fkey, +ADD CONSTRAINT resources_parentid_fkey FOREIGN KEY (parentId) REFERENCES Resources(internalId) ON DELETE CASCADE; + + +-- Remove the AuditLogs table +----------- + +DROP INDEX IF EXISTS AuditLogsUserId; +DROP INDEX IF EXISTS AuditLogsResourceId; +DROP INDEX IF EXISTS AuditLogsAction; +DROP TABLE IF EXISTS AuditLogs; + + +---------- + +-- 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, 5); -- GlobalProperty_DatabasePatchLevel
--- a/PostgreSQL/Plugins/SQL/PrepareIndex.sql Thu Jul 17 12:35:36 2025 +0200 +++ b/PostgreSQL/Plugins/SQL/PrepareIndex.sql Tue Jul 22 10:22:34 2025 +0200 @@ -797,7 +797,7 @@ CREATE INDEX IF NOT EXISTS QueuesIndex ON Queues (queueId, id); --- new in rev 599 +-- new in rev 6 CREATE SEQUENCE IF NOT EXISTS PatientRecyclingOrderSequence INCREMENT 1 START 1; @@ -842,7 +842,7 @@ -- 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, 599); -- GlobalProperty_DatabasePatchLevel +INSERT INTO GlobalProperties VALUES (4, 6); -- 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/Rev5ToRev599.sql Thu Jul 17 12:35:36 2025 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -CREATE SEQUENCE IF NOT EXISTS PatientRecyclingOrderSequence INCREMENT 1 START 1; - --- the protection mechanisms changed in rev 499. 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; - --- The DeletedResources trigger is not used anymore - -DROP TRIGGER IF EXISTS ResourceDeleted ON Resources; -DROP FUNCTION IF EXISTS ResourceDeletedFunc(); - --- The ON DELETE CASCADE on the Resources.parentId has been removed since this is now implemented --- in the DeleteResource function --- Drop the existing foreign key constraint and add a new one without ON DELETE CASCADE in a single command -ALTER TABLE Resources -DROP CONSTRAINT IF EXISTS resources_parentid_fkey, -ADD CONSTRAINT resources_parentid_fkey FOREIGN KEY (parentId) REFERENCES Resources(internalId); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PostgreSQL/Plugins/SQL/Upgrades/Rev5ToRev6.sql Tue Jul 22 10:22:34 2025 +0200 @@ -0,0 +1,59 @@ +CREATE SEQUENCE IF NOT EXISTS PatientRecyclingOrderSequence INCREMENT 1 START 1; + +-- the protection mechanisms changed in rev 499. 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; + +-- The DeletedResources trigger is not used anymore + +DROP TRIGGER IF EXISTS ResourceDeleted ON Resources; +DROP FUNCTION IF EXISTS ResourceDeletedFunc(); + +-- The ON DELETE CASCADE on the Resources.parentId has been removed since this is now implemented +-- in the DeleteResource function +-- Drop the existing foreign key constraint and add a new one without ON DELETE CASCADE in a single command +ALTER TABLE Resources +DROP CONSTRAINT IF EXISTS resources_parentid_fkey, +ADD CONSTRAINT resources_parentid_fkey FOREIGN KEY (parentId) REFERENCES Resources(internalId); \ No newline at end of file