Mercurial > hg > orthanc-databases
changeset 849:6177e8e6be50
merged pg-next-1099 -> default
| author | Alain Mazy <am@orthanc.team> |
|---|---|
| date | Fri, 18 Sep 2026 14:41:29 +0200 |
| parents | 2ef3d2ce4905 (diff) 3e4d8da97292 (current diff) |
| children | d8d0b7b0cc75 8e0c16a006c8 |
| files | PostgreSQL/CMakeLists.txt PostgreSQL/NEWS PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/SQL/Downgrades/Rev1099ToRev10.sql PostgreSQL/Plugins/SQL/Downgrades/Rev11ToRev10.sql PostgreSQL/Plugins/SQL/PrepareIndex.sql PostgreSQL/Plugins/SQL/Upgrades/Rev10ToRev1099.sql PostgreSQL/Plugins/SQL/Upgrades/Rev10ToRev11.sql |
| diffstat | 8 files changed, 128 insertions(+), 133 deletions(-) [+] |
line wrap: on
line diff
--- a/PostgreSQL/CMakeLists.txt Thu Sep 17 16:36:20 2026 +0200 +++ b/PostgreSQL/CMakeLists.txt Fri Sep 18 14:41:29 2026 +0200 @@ -97,7 +97,7 @@ POSTGRESQL_UPGRADE_REV4_TO_REV5 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev4ToRev5.sql POSTGRESQL_UPGRADE_REV5_TO_REV6 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev5ToRev6.sql POSTGRESQL_UPGRADE_REV6_TO_REV10 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev6ToRev10.sql - POSTGRESQL_UPGRADE_REV10_TO_REV1099 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev10ToRev1099.sql + POSTGRESQL_UPGRADE_REV10_TO_REV11 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev10ToRev11.sql )
--- a/PostgreSQL/NEWS Thu Sep 17 16:36:20 2026 +0200 +++ b/PostgreSQL/NEWS Fri Sep 18 14:41:29 2026 +0200 @@ -3,15 +3,10 @@ Changes: * Added an index on AttachedFiles uuids to speed-up the SDK calls - SetAttachmentCustomData and GetAttachmentCustomData + SetAttachmentCustomData and GetAttachmentCustomData that are used by, + notably, the advanced-storage plugin. * Small update of the "DeleteResource" function to avoid a warning when multiple clients are trying to delete the same resource at the same time. - Warning: - test_concurrent_uploads_same_study (Concurrency.test_concurrency.TestConcurrency.test_concurrent_uploads_same_study) ... 2025-12-05 08:04:24.133 UTC [73] ERROR: null value in column "resourcetype" of relation "deletedresources" violates not-null constraint - 2025-12-05 08:04:24.133 UTC [73] DETAIL: Failing row contains (null, null). - 2025-12-05 08:04:24.133 UTC [73] CONTEXT: SQL statement "INSERT INTO DeletedResources VALUES (deleted_resource_row.resourceType, deleted_resource_row.publicId)" - PL/pgSQL function deleteresource(bigint) line 85 at SQL statement - 2025-12-05 08:04:24.133 UTC [73] STATEMENT: SELECT * FROM DeleteResource($1) Release 10.3 (2026-08-19)
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Thu Sep 17 16:36:20 2026 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Fri Sep 18 14:41:29 2026 +0200 @@ -49,7 +49,7 @@ static const GlobalProperty GlobalProperty_HasComputeStatisticsReadOnly = GlobalProperty_DatabaseInternal4; } -#define CURRENT_DB_REVISION 1099 +#define CURRENT_DB_REVISION 11 namespace OrthancDatabases { @@ -285,15 +285,15 @@ if (currentRevision == 10) { - LOG(WARNING) << "Upgrading DB schema from revision 10 to revision 1099"; + LOG(WARNING) << "Upgrading DB schema from revision 10 to revision 11"; std::string query; Orthanc::EmbeddedResources::GetFileResource - (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV10_TO_REV1099); + (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV10_TO_REV11); t.GetDatabaseTransaction().ExecuteMultiLines(query); hasAppliedAnUpgrade = true; - currentRevision = 1099; + currentRevision = 11; } if (hasAppliedAnUpgrade)
--- a/PostgreSQL/Plugins/SQL/Downgrades/Rev1099ToRev10.sql Thu Sep 17 16:36:20 2026 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ --- restore the old 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_resource_row RECORD; - deleted_parent_row RECORD; - deleted_grand_parent_row RECORD; - deleted_grand_grand_parent_row RECORD; - locked_parent_row RECORD; - locked_resource_row RECORD; -BEGIN - SET client_min_messages = warning; -- suppress NOTICE: relation "deletedresources" already exists, skipping - -- note: temporary tables are created at 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 connection - 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_parent_row FROM resources WHERE internalid = (SELECT parentid FROM resources WHERE internalid = id) FOR UPDATE; - -- Before deleting the resource itself, we lock it to retrieve the resourceType and to make sure not 2 connections try to - -- delete it at the same time - SELECT * INTO locked_resource_row FROM resources WHERE internalid = id FOR UPDATE; - -- before delete the resource itself, we must delete its grand-grand-children, the grand-children and its children no to violate - -- the parentId referencing an existing primary key constrain. This is actually implementing the ON DELETE CASCADE that was on the parentId in previous revisions. - - -- If this resource has grand-grand-children, delete them - if locked_resource_row.resourceType < 1 THEN - WITH grand_grand_children_to_delete AS (SELECT grandGrandChildLevel.internalId, grandGrandChildLevel.resourceType, grandGrandChildLevel.publicId - FROM Resources childLevel - INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId - INNER JOIN Resources grandGrandChildLevel ON grandChildLevel.internalId = grandGrandChildLevel.parentId - WHERE childLevel.parentId = id), - - deleted_grand_grand_children_rows AS (DELETE FROM Resources WHERE internalId IN (SELECT internalId FROM grand_grand_children_to_delete) - RETURNING resourceType, publicId) - INSERT INTO DeletedResources SELECT resourceType, publicId FROM deleted_grand_grand_children_rows; - END IF; - -- If this resource has grand-children, delete them - if locked_resource_row.resourceType < 2 THEN - WITH grand_children_to_delete AS (SELECT grandChildLevel.internalId, grandChildLevel.resourceType, grandChildLevel.publicId - FROM Resources childLevel - INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId - WHERE childLevel.parentId = id), - - deleted_grand_children_rows AS (DELETE FROM Resources WHERE internalId IN (SELECT internalId FROM grand_children_to_delete) - RETURNING resourceType, publicId) - INSERT INTO DeletedResources SELECT resourceType, publicId FROM deleted_grand_children_rows; - END IF; - -- If this resource has children, delete them - if locked_resource_row.resourceType < 3 THEN - WITH deleted_children AS (DELETE FROM Resources - WHERE parentId = id - RETURNING resourceType, publicId) - INSERT INTO DeletedResources SELECT resourceType, publicId FROM deleted_children; - END IF; - -- delete the resource itself - DELETE FROM Resources WHERE internalId=id RETURNING * INTO deleted_resource_row; - - -- keep track of the deleted resources for C++ code - INSERT INTO DeletedResources VALUES (deleted_resource_row.resourceType, deleted_resource_row.publicId); - - -- 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_resource_row.parentId - AND EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_resource_row.parentId); - IF deleted_resource_row.resourceType > 0 THEN - -- If this resource is the latest child, delete the parent - DELETE FROM Resources WHERE internalId = deleted_resource_row.parentId - AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_resource_row.parentId) - RETURNING * INTO deleted_parent_row; - IF FOUND THEN - INSERT INTO DeletedResources VALUES (deleted_parent_row.resourceType, deleted_parent_row.publicId); - IF deleted_parent_row.resourceType > 0 THEN - -- If this resource is the latest child, delete the parent - DELETE FROM Resources WHERE internalId = deleted_parent_row.parentId - AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_parent_row.parentId) - RETURNING * INTO deleted_grand_parent_row; - IF FOUND THEN - INSERT INTO DeletedResources VALUES (deleted_grand_parent_row.resourceType, deleted_grand_parent_row.publicId); - IF deleted_grand_parent_row.resourceType > 0 THEN - -- If this resource is the latest child, delete the parent - DELETE FROM Resources WHERE internalId = deleted_grand_parent_row.parentId - AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_grand_parent_row.parentId) - RETURNING * INTO deleted_grand_parent_row; - IF FOUND THEN - INSERT INTO DeletedResources VALUES (deleted_grand_parent_row.resourceType, deleted_grand_parent_row.publicId); - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; -END; - -DROP INDEX IF EXISTS AttachedFilesUuid; - --- 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); -INSERT INTO GlobalProperties VALUES (4, 10); -- GlobalProperty_DatabasePatchLevel
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PostgreSQL/Plugins/SQL/Downgrades/Rev11ToRev10.sql Fri Sep 18 14:41:29 2026 +0200 @@ -0,0 +1,114 @@ +-- restore the old 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_resource_row RECORD; + deleted_parent_row RECORD; + deleted_grand_parent_row RECORD; + deleted_grand_grand_parent_row RECORD; + locked_parent_row RECORD; + locked_resource_row RECORD; +BEGIN + SET client_min_messages = warning; -- suppress NOTICE: relation "deletedresources" already exists, skipping + -- note: temporary tables are created at 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 connection + 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_parent_row FROM resources WHERE internalid = (SELECT parentid FROM resources WHERE internalid = id) FOR UPDATE; + -- Before deleting the resource itself, we lock it to retrieve the resourceType and to make sure not 2 connections try to + -- delete it at the same time + SELECT * INTO locked_resource_row FROM resources WHERE internalid = id FOR UPDATE; + -- before delete the resource itself, we must delete its grand-grand-children, the grand-children and its children no to violate + -- the parentId referencing an existing primary key constrain. This is actually implementing the ON DELETE CASCADE that was on the parentId in previous revisions. + + -- If this resource has grand-grand-children, delete them + if locked_resource_row.resourceType < 1 THEN + WITH grand_grand_children_to_delete AS (SELECT grandGrandChildLevel.internalId, grandGrandChildLevel.resourceType, grandGrandChildLevel.publicId + FROM Resources childLevel + INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId + INNER JOIN Resources grandGrandChildLevel ON grandChildLevel.internalId = grandGrandChildLevel.parentId + WHERE childLevel.parentId = id), + + deleted_grand_grand_children_rows AS (DELETE FROM Resources WHERE internalId IN (SELECT internalId FROM grand_grand_children_to_delete) + RETURNING resourceType, publicId) + INSERT INTO DeletedResources SELECT resourceType, publicId FROM deleted_grand_grand_children_rows; + END IF; + -- If this resource has grand-children, delete them + if locked_resource_row.resourceType < 2 THEN + WITH grand_children_to_delete AS (SELECT grandChildLevel.internalId, grandChildLevel.resourceType, grandChildLevel.publicId + FROM Resources childLevel + INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId + WHERE childLevel.parentId = id), + + deleted_grand_children_rows AS (DELETE FROM Resources WHERE internalId IN (SELECT internalId FROM grand_children_to_delete) + RETURNING resourceType, publicId) + INSERT INTO DeletedResources SELECT resourceType, publicId FROM deleted_grand_children_rows; + END IF; + -- If this resource has children, delete them + if locked_resource_row.resourceType < 3 THEN + WITH deleted_children AS (DELETE FROM Resources + WHERE parentId = id + RETURNING resourceType, publicId) + INSERT INTO DeletedResources SELECT resourceType, publicId FROM deleted_children; + END IF; + -- delete the resource itself + DELETE FROM Resources WHERE internalId=id RETURNING * INTO deleted_resource_row; + + -- keep track of the deleted resources for C++ code + INSERT INTO DeletedResources VALUES (deleted_resource_row.resourceType, deleted_resource_row.publicId); + + -- 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_resource_row.parentId + AND EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_resource_row.parentId); + IF deleted_resource_row.resourceType > 0 THEN + -- If this resource is the latest child, delete the parent + DELETE FROM Resources WHERE internalId = deleted_resource_row.parentId + AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_resource_row.parentId) + RETURNING * INTO deleted_parent_row; + IF FOUND THEN + INSERT INTO DeletedResources VALUES (deleted_parent_row.resourceType, deleted_parent_row.publicId); + IF deleted_parent_row.resourceType > 0 THEN + -- If this resource is the latest child, delete the parent + DELETE FROM Resources WHERE internalId = deleted_parent_row.parentId + AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_parent_row.parentId) + RETURNING * INTO deleted_grand_parent_row; + IF FOUND THEN + INSERT INTO DeletedResources VALUES (deleted_grand_parent_row.resourceType, deleted_grand_parent_row.publicId); + IF deleted_grand_parent_row.resourceType > 0 THEN + -- If this resource is the latest child, delete the parent + DELETE FROM Resources WHERE internalId = deleted_grand_parent_row.parentId + AND NOT EXISTS (SELECT 1 FROM Resources WHERE parentId = deleted_grand_parent_row.parentId) + RETURNING * INTO deleted_grand_parent_row; + IF FOUND THEN + INSERT INTO DeletedResources VALUES (deleted_grand_parent_row.resourceType, deleted_grand_parent_row.publicId); + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; +END; + +DROP INDEX IF EXISTS AttachedFilesUuid; + +-- 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); +INSERT INTO GlobalProperties VALUES (4, 10); -- GlobalProperty_DatabasePatchLevel
--- a/PostgreSQL/Plugins/SQL/PrepareIndex.sql Thu Sep 17 16:36:20 2026 +0200 +++ b/PostgreSQL/Plugins/SQL/PrepareIndex.sql Fri Sep 18 14:41:29 2026 +0200 @@ -854,7 +854,7 @@ CREATE INDEX IF NOT EXISTS InvalidChildCountsId ON InvalidChildCounts (id); -- see https://discourse.orthanc-server.org/t/increase-in-cpu-usage-of-database-after-update-to-orthanc-1-12-7/6057/6 --- new in rev 1099 +-- new in rev 11 CREATE INDEX IF NOT EXISTS AttachedFilesUuid ON AttachedFiles (uuid); @@ -863,7 +863,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, 1099); -- GlobalProperty_DatabasePatchLevel +INSERT INTO GlobalProperties VALUES (4, 11); -- 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/Rev10ToRev1099.sql Thu Sep 17 16:36:20 2026 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ --- Update from Rev 10 to Rev 1099 - --- the DeleteResource function is updated in PrepareIndex.sql --- the AttachedFilesUuid index is created 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/Rev10ToRev11.sql Fri Sep 18 14:41:29 2026 +0200 @@ -0,0 +1,5 @@ +-- Update from Rev 10 to Rev 11 + +-- the DeleteResource function is updated in PrepareIndex.sql +-- the AttachedFilesUuid index is created in PrepareIndex.sql +SELECT 1; \ No newline at end of file
