From f1e9672e26b300c88ab72c2b12c3b047c389842f Mon Sep 17 00:00:00 2001
From: Jorge Camarero Vera <jorge.camarero@cern.ch>
Date: Mon, 23 Jan 2023 17:40:34 +0100
Subject: [PATCH] Refactor Catalogue and RdbmsCatalogue Classes

---
 catalogue/12.0/oracle_catalogue_schema.sql    |     4 +-
 catalogue/12.0/postgres_catalogue_schema.sql  |     4 +-
 catalogue/12.0/sqlite_catalogue_schema.sql    |     4 +-
 catalogue/AllCatalogueSchema.hpp              |    12 +-
 catalogue/CMakeLists.txt                      |    56 +-
 catalogue/Catalogue.hpp                       |  1440 +-
 ...alogueTest.cpp => CatalogueExceptions.hpp} |    16 +-
 catalogue/CatalogueTest.hpp                   |   143 -
 catalogue/CatalogueUtils.cpp                  |    43 +
 .../{Catalogue.cpp => CatalogueUtils.hpp}     |    26 +-
 catalogue/CreateAdminUserCmd.cpp              |     2 +-
 catalogue/DriveConfig.cpp                     |    16 +-
 catalogue/DriveConfig.hpp                     |     1 +
 catalogue/DummyCatalogue.cpp                  |   446 -
 catalogue/DummyCatalogue.hpp                  |   269 -
 catalogue/Group.hpp                           |    64 +
 catalogue/InMemoryCatalogueFactory.cpp        |     8 +-
 catalogue/InMemoryVersionOfCatalogueTest.cpp  |    48 -
 catalogue/OracleCatalogue.cpp                 |  1247 --
 catalogue/OracleCatalogue.hpp                 |   302 -
 catalogue/OracleCatalogueFactory.cpp          |    14 +-
 catalogue/OracleCatalogueFactory.hpp          |     5 +
 catalogue/PostgresCatalogue.cpp               |  1204 --
 catalogue/PostgresCatalogue.hpp               |   318 -
 catalogue/PostgresqlCatalogueFactory.cpp      |     8 +-
 catalogue/PostgresqlCatalogueFactory.hpp      |     9 +
 catalogue/RdbmsCatalogue.cpp                  | 12101 ----------------
 catalogue/RdbmsCatalogue.hpp                  |  2460 ----
 catalogue/SchemaCreatingSqliteCatalogue.cpp   |     6 +-
 catalogue/SchemaCreatingSqliteCatalogue.hpp   |     2 +-
 catalogue/SqliteCatalogue.cpp                 |   780 -
 catalogue/SqliteCatalogue.hpp                 |   269 -
 catalogue/TapeDrivesCatalogueState.cpp        |    23 +-
 catalogue/User.hpp                            |    64 +
 catalogue/common_catalogue_schema.sql         |     4 +-
 catalogue/dummy/DummyAdminUserCatalogue.cpp   |    47 +
 catalogue/dummy/DummyAdminUserCatalogue.hpp   |    43 +
 catalogue/dummy/DummyArchiveFileCatalogue.cpp |    93 +
 catalogue/dummy/DummyArchiveFileCatalogue.hpp |    69 +
 .../dummy/DummyArchiveRouteCatalogue.cpp      |    53 +
 .../dummy/DummyArchiveRouteCatalogue.hpp      |    48 +
 catalogue/dummy/DummyCatalogue.cpp            |   152 +
 catalogue/dummy/DummyCatalogue.hpp            |    86 +
 .../dummy/DummyDiskInstanceCatalogue.cpp      |    48 +
 .../dummy/DummyDiskInstanceCatalogue.hpp      |    51 +
 .../dummy/DummyDiskInstanceSpaceCatalogue.cpp |    72 +
 .../dummy/DummyDiskInstanceSpaceCatalogue.hpp |    70 +
 catalogue/dummy/DummyDiskSystemCatalogue.cpp  |    82 +
 catalogue/dummy/DummyDiskSystemCatalogue.hpp  |    67 +
 catalogue/dummy/DummyDriveConfig.hpp          |    53 +
 catalogue/dummy/DummyDriveConfigCatalogue.cpp |    51 +
 catalogue/dummy/DummyDriveConfigCatalogue.hpp |    54 +
 catalogue/dummy/DummyDriveStateCatalogue.cpp  |   171 +
 catalogue/dummy/DummyDriveStateCatalogue.hpp  |    74 +
 .../dummy/DummyFileRecycleLogCatalogue.cpp    |    41 +
 .../dummy/DummyFileRecycleLogCatalogue.hpp    |    40 +
 .../dummy/DummyLogicalLibraryCatalogue.cpp    |    60 +
 .../dummy/DummyLogicalLibraryCatalogue.hpp    |    51 +
 catalogue/dummy/DummyMediaTypeCatalogue.cpp   |    93 +
 catalogue/dummy/DummyMediaTypeCatalogue.hpp   |    67 +
 catalogue/dummy/DummyMountPolicyCatalogue.cpp |   110 +
 catalogue/dummy/DummyMountPolicyCatalogue.hpp |    59 +
 ...mmyRequesterActivityMountRuleCatalogue.cpp |    58 +
 ...mmyRequesterActivityMountRuleCatalogue.hpp |    49 +
 .../DummyRequesterGroupMountRuleCatalogue.cpp |    58 +
 .../DummyRequesterGroupMountRuleCatalogue.hpp |    48 +
 .../DummyRequesterMountRuleCatalogue.cpp      |    56 +
 .../DummyRequesterMountRuleCatalogue.hpp      |    46 +
 catalogue/dummy/DummySchemaCatalogue.cpp      |    38 +
 catalogue/dummy/DummySchemaCatalogue.hpp      |    38 +
 .../dummy/DummyStorageClassCatalogue.cpp      |    64 +
 .../dummy/DummyStorageClassCatalogue.hpp      |    56 +
 catalogue/dummy/DummyTapeCatalogue.cpp        |   206 +
 catalogue/dummy/DummyTapeCatalogue.hpp        |   121 +
 catalogue/dummy/DummyTapeFileCatalogue.cpp    |    48 +
 catalogue/dummy/DummyTapeFileCatalogue.hpp    |    41 +
 catalogue/dummy/DummyTapePoolCatalogue.cpp    |    82 +
 catalogue/dummy/DummyTapePoolCatalogue.hpp    |    62 +
 .../DummyVirtualOrganizationCatalogue.cpp     |    84 +
 .../DummyVirtualOrganizationCatalogue.hpp     |    67 +
 catalogue/interfaces/AdminUserCatalogue.hpp   |    64 +
 catalogue/interfaces/ArchiveFileCatalogue.hpp |   230 +
 .../interfaces/ArchiveRouteCatalogue.hpp      |    97 +
 .../interfaces/DiskInstanceCatalogue.hpp      |    56 +
 .../interfaces/DiskInstanceSpaceCatalogue.hpp |    89 +
 catalogue/interfaces/DiskSystemCatalogue.hpp  |   106 +
 catalogue/interfaces/DriveConfigCatalogue.hpp |    99 +
 catalogue/interfaces/DriveStateCatalogue.hpp  |   119 +
 .../interfaces/FileRecycleLogCatalogue.hpp    |    80 +
 .../interfaces/LogicalLibraryCatalogue.hpp    |    73 +
 catalogue/interfaces/MediaTypeCatalogue.hpp   |   171 +
 catalogue/interfaces/MountPolicyCatalogue.hpp |    92 +
 .../RequesterActivityMountRuleCatalogue.hpp   |    90 +
 .../RequesterGroupMountRuleCatalogue.hpp      |    89 +
 .../RequesterMountRuleCatalogue.hpp           |    86 +
 catalogue/interfaces/SchemaCatalogue.hpp      |    58 +
 .../interfaces/StorageClassCatalogue.hpp      |    88 +
 catalogue/interfaces/TapeCatalogue.hpp        |   286 +
 catalogue/interfaces/TapeFileCatalogue.hpp    |    96 +
 catalogue/interfaces/TapePoolCatalogue.hpp    |    87 +
 .../VirtualOrganizationCatalogue.hpp          |   135 +
 catalogue/rdbms/CommonExceptions.hpp          |    31 +
 catalogue/rdbms/RdbmsAdminUserCatalogue.cpp   |   287 +
 catalogue/rdbms/RdbmsAdminUserCatalogue.hpp   |    90 +
 catalogue/rdbms/RdbmsArchiveFileCatalogue.cpp |  1209 ++
 catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp |   334 +
 .../rdbms/RdbmsArchiveRouteCatalogue.cpp      |   411 +
 .../rdbms/RdbmsArchiveRouteCatalogue.hpp      |    77 +
 catalogue/rdbms/RdbmsCatalogue.cpp            |   164 +
 catalogue/rdbms/RdbmsCatalogue.hpp            |   182 +
 ...sCatalogueGetArchiveFilesForRepackItor.cpp |     2 +-
 ...sCatalogueGetArchiveFilesForRepackItor.hpp |     2 +-
 .../RdbmsCatalogueGetArchiveFilesItor.cpp     |     2 +-
 .../RdbmsCatalogueGetArchiveFilesItor.hpp     |     2 +-
 .../RdbmsCatalogueGetFileRecycleLogItor.cpp   |     2 +-
 .../RdbmsCatalogueGetFileRecycleLogItor.hpp   |     4 +-
 .../RdbmsCatalogueTapeContentsItor.cpp        |     2 +-
 .../RdbmsCatalogueTapeContentsItor.hpp        |     2 +-
 catalogue/rdbms/RdbmsCatalogueUtils.cpp       |   508 +
 catalogue/rdbms/RdbmsCatalogueUtils.hpp       |    87 +
 .../rdbms/RdbmsDiskInstanceCatalogue.cpp      |   219 +
 .../rdbms/RdbmsDiskInstanceCatalogue.hpp      |    57 +
 .../rdbms/RdbmsDiskInstanceSpaceCatalogue.cpp |   405 +
 .../rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp |    78 +
 catalogue/rdbms/RdbmsDiskSystemCatalogue.cpp  |   492 +
 catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp  |    79 +
 catalogue/rdbms/RdbmsDriveConfigCatalogue.cpp |   248 +
 catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp |    65 +
 catalogue/rdbms/RdbmsDriveStateCatalogue.cpp  |  1057 ++
 catalogue/rdbms/RdbmsDriveStateCatalogue.hpp  |    92 +
 .../rdbms/RdbmsFileRecycleLogCatalogue.cpp    |   389 +
 .../rdbms/RdbmsFileRecycleLogCatalogue.hpp    |   178 +
 .../rdbms/RdbmsLogicalLibraryCatalogue.cpp    |   361 +
 .../rdbms/RdbmsLogicalLibraryCatalogue.hpp    |    93 +
 catalogue/rdbms/RdbmsMediaTypeCatalogue.cpp   |   624 +
 catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp   |    91 +
 catalogue/rdbms/RdbmsMountPolicyCatalogue.cpp |   861 ++
 catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp |   153 +
 ...bmsRequesterActivityMountRuleCatalogue.cpp |   282 +
 ...bmsRequesterActivityMountRuleCatalogue.hpp |    66 +
 .../RdbmsRequesterGroupMountRuleCatalogue.cpp |   279 +
 .../RdbmsRequesterGroupMountRuleCatalogue.hpp |    65 +
 .../RdbmsRequesterMountRuleCatalogue.cpp      |   273 +
 .../RdbmsRequesterMountRuleCatalogue.hpp      |    63 +
 catalogue/rdbms/RdbmsSchemaCatalogue.cpp      |   124 +
 catalogue/rdbms/RdbmsSchemaCatalogue.hpp      |    60 +
 .../rdbms/RdbmsStorageClassCatalogue.cpp      |   502 +
 .../rdbms/RdbmsStorageClassCatalogue.hpp      |   145 +
 catalogue/rdbms/RdbmsTapeCatalogue.cpp        |  1879 +++
 catalogue/rdbms/RdbmsTapeCatalogue.hpp        |   186 +
 catalogue/rdbms/RdbmsTapeFileCatalogue.cpp    |   343 +
 catalogue/rdbms/RdbmsTapeFileCatalogue.hpp    |   131 +
 catalogue/rdbms/RdbmsTapePoolCatalogue.cpp    |   762 +
 catalogue/rdbms/RdbmsTapePoolCatalogue.hpp    |   122 +
 .../RdbmsVirtualOrganizationCatalogue.cpp     |   591 +
 .../RdbmsVirtualOrganizationCatalogue.hpp     |   110 +
 .../oracle/OracleArchiveFileCatalogue.cpp     |   344 +
 .../oracle/OracleArchiveFileCatalogue.hpp     |    71 +
 catalogue/rdbms/oracle/OracleCatalogue.cpp    |    85 +
 catalogue/rdbms/oracle/OracleCatalogue.hpp    |    73 +
 .../oracle/OracleFileRecycleLogCatalogue.cpp  |   117 +
 .../oracle/OracleFileRecycleLogCatalogue.hpp  |    61 +
 .../oracle/OracleLogicalLibraryCatalogue.cpp  |    54 +
 .../oracle/OracleLogicalLibraryCatalogue.hpp  |    40 +
 .../rdbms/oracle/OracleMediaTypeCatalogue.cpp |    54 +
 .../rdbms/oracle/OracleMediaTypeCatalogue.hpp |    41 +
 .../oracle/OracleStorageClassCatalogue.cpp    |    54 +
 .../oracle/OracleStorageClassCatalogue.hpp    |    42 +
 .../rdbms/oracle/OracleTapeCatalogue.cpp      |    58 +
 .../rdbms/oracle/OracleTapeCatalogue.hpp      |    41 +
 .../rdbms/oracle/OracleTapeFileCatalogue.cpp  |   619 +
 .../rdbms/oracle/OracleTapeFileCatalogue.hpp  |    88 +
 .../rdbms/oracle/OracleTapePoolCatalogue.cpp  |    55 +
 .../rdbms/oracle/OracleTapePoolCatalogue.hpp  |    41 +
 .../OracleVirtualOrganizationCatalogue.cpp    |    56 +
 .../OracleVirtualOrganizationCatalogue.hpp    |    39 +
 .../postgres/PostgresArchiveFileCatalogue.cpp |   338 +
 .../postgres/PostgresArchiveFileCatalogue.hpp |    72 +
 .../rdbms/postgres/PostgresCatalogue.cpp      |    91 +
 .../rdbms/postgres/PostgresCatalogue.hpp      |    69 +
 .../PostgresFileRecycleLogCatalogue.cpp       |   110 +
 .../PostgresFileRecycleLogCatalogue.hpp       |    62 +
 .../PostgresLogicalLibraryCatalogue.cpp       |    50 +
 .../PostgresLogicalLibraryCatalogue.hpp       |    41 +
 .../postgres/PostgresMediaTypeCatalogue.cpp   |    49 +
 .../postgres/PostgresMediaTypeCatalogue.hpp   |    41 +
 .../PostgresStorageClassCatalogue.cpp         |    51 +
 .../PostgresStorageClassCatalogue.hpp         |    41 +
 .../rdbms/postgres/PostgresTapeCatalogue.cpp  |    58 +
 .../rdbms/postgres/PostgresTapeCatalogue.hpp  |    41 +
 .../postgres/PostgresTapeFileCatalogue.cpp    |   589 +
 .../postgres/PostgresTapeFileCatalogue.hpp    |   101 +
 .../postgres/PostgresTapePoolCatalogue.cpp    |    51 +
 .../postgres/PostgresTapePoolCatalogue.hpp    |    41 +
 .../PostgresVirtualOrganizationCatalogue.cpp  |    50 +
 .../PostgresVirtualOrganizationCatalogue.hpp  |    41 +
 .../sqlite/SqliteArchiveFileCatalogue.cpp     |   240 +
 .../sqlite/SqliteArchiveFileCatalogue.hpp     |    47 +
 catalogue/rdbms/sqlite/SqliteCatalogue.cpp    |    86 +
 catalogue/rdbms/sqlite/SqliteCatalogue.hpp    |    68 +
 .../sqlite/SqliteFileRecycleLogCatalogue.cpp  |   118 +
 .../sqlite/SqliteFileRecycleLogCatalogue.hpp  |    62 +
 .../sqlite/SqliteLogicalLibraryCatalogue.cpp  |    59 +
 .../sqlite/SqliteLogicalLibraryCatalogue.hpp  |    41 +
 .../rdbms/sqlite/SqliteMediaTypeCatalogue.cpp |    59 +
 .../rdbms/sqlite/SqliteMediaTypeCatalogue.hpp |    39 +
 .../sqlite/SqliteStorageClassCatalogue.cpp    |    59 +
 .../sqlite/SqliteStorageClassCatalogue.hpp    |    41 +
 .../rdbms/sqlite/SqliteTapeCatalogue.cpp      |    56 +
 .../rdbms/sqlite/SqliteTapeCatalogue.hpp      |    42 +
 .../rdbms/sqlite/SqliteTapeFileCatalogue.cpp  |   202 +
 .../rdbms/sqlite/SqliteTapeFileCatalogue.hpp  |    55 +
 .../rdbms/sqlite/SqliteTapePoolCatalogue.cpp  |    60 +
 .../rdbms/sqlite/SqliteTapePoolCatalogue.hpp  |    41 +
 .../SqliteVirtualOrganizationCatalogue.cpp    |    59 +
 .../SqliteVirtualOrganizationCatalogue.hpp    |    41 +
 .../AdminUserCatalogueRetryWrapper.cpp        |    59 +
 .../AdminUserCatalogueRetryWrapper.hpp        |    59 +
 .../ArchiveFileCatalogueRetryWrapper.cpp      |   127 +
 .../ArchiveFileCatalogueRetryWrapper.hpp      |    84 +
 .../ArchiveRouteCatalogueRetryWrapper.cpp     |    70 +
 .../ArchiveRouteCatalogueRetryWrapper.hpp     |    63 +
 .../retrywrappers/CatalogueRetryWrapper.cpp   |   163 +
 .../retrywrappers/CatalogueRetryWrapper.hpp   |   116 +
 .../DiskInstanceCatalogueRetryWrapper.cpp     |    61 +
 .../DiskInstanceCatalogueRetryWrapper.hpp     |    57 +
 ...DiskInstanceSpaceCatalogueRetryWrapper.cpp |    88 +
 ...DiskInstanceSpaceCatalogueRetryWrapper.hpp |    70 +
 .../DiskSystemCatalogueRetryWrapper.cpp       |    93 +
 .../DiskSystemCatalogueRetryWrapper.hpp       |    75 +
 .../DriveConfigCatalogueRetryWrapper.cpp      |    71 +
 .../DriveConfigCatalogueRetryWrapper.hpp      |    62 +
 .../DriveStateCatalogueRetryWrapper.cpp       |   104 +
 .../DriveStateCatalogueRetryWrapper.hpp       |    78 +
 .../FileRecycleLogCatalogueRetryWrapper.cpp   |    54 +
 .../FileRecycleLogCatalogueRetryWrapper.hpp   |    58 +
 .../LogicalLibraryCatalogueRetryWrapper.cpp   |    75 +
 .../LogicalLibraryCatalogueRetryWrapper.hpp   |    75 +
 .../MediaTypeCatalogueRetryWrapper.cpp        |   111 +
 .../MediaTypeCatalogueRetryWrapper.hpp        |    82 +
 .../MountPolicyCatalogueRetryWrapper.cpp      |    96 +
 .../MountPolicyCatalogueRetryWrapper.hpp      |    74 +
 ...ActivityMountRuleCatalogueRetryWrapper.cpp |    79 +
 ...ActivityMountRuleCatalogueRetryWrapper.hpp |    64 +
 ...terGroupMountRuleCatalogueRetryWrapper.cpp |    74 +
 ...terGroupMountRuleCatalogueRetryWrapper.hpp |    63 +
 ...equesterMountRuleCatalogueRetryWrapper.cpp |    73 +
 ...equesterMountRuleCatalogueRetryWrapper.hpp |    61 +
 .../SchemaCatalogueRetryWrapper.cpp           |    46 +
 .../SchemaCatalogueRetryWrapper.hpp           |    49 +
 .../StorageClassCatalogueRetryWrapper.cpp     |    84 +
 .../StorageClassCatalogueRetryWrapper.hpp     |    67 +
 .../TapeCatalogueRetryWrapper.cpp             |   191 +
 .../TapeCatalogueRetryWrapper.hpp             |   118 +
 .../TapeFileCatalogueRetryWrapper.cpp         |    55 +
 .../TapeFileCatalogueRetryWrapper.hpp         |    51 +
 .../TapePoolCatalogueRetryWrapper.hpp         |    75 +
 .../TapePoolRetryCatalogueWrapper.cpp         |    96 +
 ...rtualOrganizationCatalogueRetryWrapper.cpp |    98 +
 ...rtualOrganizationCatalogueRetryWrapper.hpp |    73 +
 .../retryOnLostConnection.hpp                 |    14 +-
 catalogue/tests/CatalogueTestUtils.cpp        |   511 +
 catalogue/tests/CatalogueTestUtils.hpp        |   106 +
 .../tests/DbConfigVersionOfCatalogueTest.cpp  |    65 +
 catalogue/tests/DriveConfigCatalogueTest.cpp  |   287 +
 catalogue/tests/DriveConfigCatalogueTest.hpp  |    42 +
 catalogue/tests/DriveStateCatalogueTest.cpp   |  1547 ++
 catalogue/tests/DriveStateCatalogueTest.hpp   |    45 +
 .../{ => tests}/InMemoryCatalogueTest.cpp     |     3 +-
 .../tests/InMemoryVersionOfCatalogueTest.cpp  |    88 +
 .../tests/modules/AdminUserCatalogueTest.cpp  |   214 +
 .../tests/modules/AdminUserCatalogueTest.hpp  |    46 +
 .../modules/ArchiveFileCatalogueTest.cpp      |  4897 +++++++
 .../modules/ArchiveFileCatalogueTest.hpp      |    57 +
 .../modules/ArchiveRouteCatalogueTest.cpp     |   483 +
 .../modules/ArchiveRouteCatalogueTest.hpp     |    53 +
 .../modules/DiskInstanceCatalogueTest.cpp     |   230 +
 .../modules/DiskInstanceCatalogueTest.hpp     |    45 +
 .../DiskInstanceSpaceCatalogueTest.cpp        |   620 +
 .../DiskInstanceSpaceCatalogueTest.hpp        |    45 +
 .../tests/modules/DiskSystemCatalogueTest.cpp |   714 +
 .../tests/modules/DiskSystemCatalogueTest.hpp |    45 +
 .../modules/DriveConfigCatalogueTest.cpp      |   266 +
 .../modules/DriveConfigCatalogueTest.hpp      |    42 +
 .../tests/modules/DriveStateCatalogueTest.cpp |  1526 ++
 .../tests/modules/DriveStateCatalogueTest.hpp |    45 +
 .../modules/FileRecycleLogCatalogueTest.cpp   |  1122 ++
 .../modules/FileRecycleLogCatalogueTest.hpp   |    56 +
 .../modules/LogicalLibraryCatalogueTest.cpp   |   751 +
 .../modules/LogicalLibraryCatalogueTest.hpp   |    56 +
 .../tests/modules/MediaTypeCatalogueTest.cpp  |   788 +
 .../tests/modules/MediaTypeCatalogueTest.hpp  |    65 +
 .../modules/MountPolicyCatalogueTest.cpp      |   318 +
 .../modules/MountPolicyCatalogueTest.hpp      |    45 +
 .../RequesterActivityMountRuleTest.cpp        |   282 +
 .../RequesterActivityMountRuleTest.hpp        |    47 +
 .../RequesterGroupMountRuleCatalogueTest.cpp  |   288 +
 .../RequesterGroupMountRuleCatalogueTest.hpp  |    47 +
 .../tests/modules/RequesterMountRuleTest.cpp  |   267 +
 .../tests/modules/RequesterMountRuleTest.hpp  |    47 +
 .../tests/modules/SchemaCatalogueTest.cpp     |    55 +
 .../tests/modules/SchemaCatalogueTest.hpp     |    41 +
 .../modules/StorageClassCatalogueTest.cpp     |   337 +
 .../modules/StorageClassCatalogueTest.hpp     |    50 +
 catalogue/tests/modules/TapeCatalogueTest.cpp |  4185 ++++++
 catalogue/tests/modules/TapeCatalogueTest.hpp |    62 +
 .../tests/modules/TapeFileCatalogueTest.cpp   |  1614 +++
 .../tests/modules/TapeFileCatalogueTest.hpp   |    57 +
 .../tests/modules/TapePoolCatalogueTest.cpp   |  1526 ++
 .../tests/modules/TapePoolCatalogueTest.hpp   |    56 +
 .../VirtualOrganizationCatalogueTest.cpp      |   335 +
 .../VirtualOrganizationCatalogueTest.hpp      |    52 +
 disk/DiskSystem.cpp                           |     3 +-
 disk/DiskSystemTest.cpp                       |    22 +-
 frontend-grpc/FrontendGRpcSvc.cpp             |     2 +-
 frontend-grpc/Main.cpp                        |     5 +-
 frontend/common/FrontendService.cpp           |     2 +-
 objectstore/AlgorithmsTest.cpp                |     2 +-
 objectstore/GarbageCollectorTest.cpp          |     9 +-
 objectstore/Helpers.cpp                       |     4 +-
 objectstore/QueueCleanupRunner.cpp            |    13 +-
 .../QueueCleanupRunnerConcurrentTest.cpp      |    12 +-
 objectstore/QueueCleanupRunnerTest.cpp        |    14 +-
 objectstore/SorterTest.cpp                    |     2 +-
 scheduler/ArchiveMount.cpp                    |     6 +-
 scheduler/OStoreDB/OStoreDB.cpp               |    24 +-
 scheduler/OStoreDB/OStoreDBTest.cpp           |     2 +-
 scheduler/RetrieveMount.cpp                   |     2 +-
 scheduler/Scheduler.cpp                       |    69 +-
 scheduler/SchedulerDatabaseTest.cpp           |    30 +-
 scheduler/SchedulerTest.cpp                   |   501 +-
 scheduler/testingMocks/MockArchiveJob.hpp     |     6 +-
 scheduler/testingMocks/MockArchiveMount.hpp   |     2 +-
 .../RAO/FilePositionEstimatorFactory.cpp      |     2 +-
 .../tape/tapeserver/daemon/CleanerSession.cpp |     8 +-
 .../daemon/DataTransferSessionTest.cpp        |   221 +-
 .../tapeserver/daemon/DiskWriteTaskTest.cpp   |     2 +-
 .../daemon/DiskWriteThreadPoolTest.cpp        |     2 +-
 .../daemon/MigrationReportPackerTest.cpp      |    30 +-
 .../daemon/RecallReportPackerTest.cpp         |     2 +-
 .../daemon/RecallTaskInjectorTest.cpp         |     2 +-
 .../tape/tapeserver/file/OsmReaderTest.cpp    |    20 +-
 tapeserver/daemon/DriveHandler.cpp            |     2 +-
 tapeserver/readtp/ReadtpCmd.cpp               |     6 +-
 tapeserver/tapelabel/TapeLabelCmd.cpp         |     4 +-
 xroot_plugins/XrdCtaActivityMountRuleLs.hpp   |     2 +-
 xroot_plugins/XrdCtaAdminLs.hpp               |     2 +-
 xroot_plugins/XrdCtaArchiveRouteLs.hpp        |     2 +-
 xroot_plugins/XrdCtaChangeStorageClass.hpp    |     4 +-
 xroot_plugins/XrdCtaDiskInstanceLs.hpp        |     2 +-
 xroot_plugins/XrdCtaDiskInstanceSpaceLs.hpp   |     2 +-
 xroot_plugins/XrdCtaDiskSystemLs.hpp          |     2 +-
 xroot_plugins/XrdCtaDriveLs.hpp               |    15 +-
 xroot_plugins/XrdCtaGroupMountRuleLs.hpp      |     2 +-
 xroot_plugins/XrdCtaLogicalLibraryLs.hpp      |     2 +-
 xroot_plugins/XrdCtaMediaTypeLs.hpp           |     2 +-
 xroot_plugins/XrdCtaMountPolicyLs.hpp         |     2 +-
 xroot_plugins/XrdCtaRecycleTapeFileLs.hpp     |     4 +-
 xroot_plugins/XrdCtaRepackLs.hpp              |     2 +-
 xroot_plugins/XrdCtaRequesterMountRuleLs.hpp  |     2 +-
 xroot_plugins/XrdCtaStorageClassLs.hpp        |     4 +-
 xroot_plugins/XrdCtaTapeFileLs.hpp            |     8 +-
 xroot_plugins/XrdCtaTapeLs.hpp                |     2 +-
 xroot_plugins/XrdCtaTapePoolLs.hpp            |     4 +-
 xroot_plugins/XrdCtaVersion.hpp               |     4 +-
 xroot_plugins/XrdCtaVirtualOrganizationLs.hpp |     2 +-
 xroot_plugins/XrdSsiCtaRequestMessage.cpp     |   211 +-
 367 files changed, 54974 insertions(+), 21722 deletions(-)
 rename catalogue/{DbConfigVersionOfCatalogueTest.cpp => CatalogueExceptions.hpp} (75%)
 delete mode 100644 catalogue/CatalogueTest.hpp
 create mode 100644 catalogue/CatalogueUtils.cpp
 rename catalogue/{Catalogue.cpp => CatalogueUtils.hpp} (74%)
 delete mode 100644 catalogue/DummyCatalogue.cpp
 delete mode 100644 catalogue/DummyCatalogue.hpp
 create mode 100644 catalogue/Group.hpp
 delete mode 100644 catalogue/InMemoryVersionOfCatalogueTest.cpp
 delete mode 100644 catalogue/OracleCatalogue.cpp
 delete mode 100644 catalogue/OracleCatalogue.hpp
 delete mode 100644 catalogue/PostgresCatalogue.cpp
 delete mode 100644 catalogue/PostgresCatalogue.hpp
 delete mode 100644 catalogue/RdbmsCatalogue.cpp
 delete mode 100644 catalogue/RdbmsCatalogue.hpp
 delete mode 100644 catalogue/SqliteCatalogue.cpp
 delete mode 100644 catalogue/SqliteCatalogue.hpp
 create mode 100644 catalogue/User.hpp
 create mode 100644 catalogue/dummy/DummyAdminUserCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyAdminUserCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyArchiveFileCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyArchiveFileCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyArchiveRouteCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyArchiveRouteCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyDiskInstanceCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyDiskInstanceCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyDiskInstanceSpaceCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyDiskSystemCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyDiskSystemCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyDriveConfig.hpp
 create mode 100644 catalogue/dummy/DummyDriveConfigCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyDriveConfigCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyDriveStateCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyDriveStateCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyFileRecycleLogCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyFileRecycleLogCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyLogicalLibraryCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyLogicalLibraryCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyMediaTypeCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyMediaTypeCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyMountPolicyCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyMountPolicyCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyRequesterMountRuleCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyRequesterMountRuleCatalogue.hpp
 create mode 100644 catalogue/dummy/DummySchemaCatalogue.cpp
 create mode 100644 catalogue/dummy/DummySchemaCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyStorageClassCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyStorageClassCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyTapeCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyTapeCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyTapeFileCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyTapeFileCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyTapePoolCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyTapePoolCatalogue.hpp
 create mode 100644 catalogue/dummy/DummyVirtualOrganizationCatalogue.cpp
 create mode 100644 catalogue/dummy/DummyVirtualOrganizationCatalogue.hpp
 create mode 100644 catalogue/interfaces/AdminUserCatalogue.hpp
 create mode 100644 catalogue/interfaces/ArchiveFileCatalogue.hpp
 create mode 100644 catalogue/interfaces/ArchiveRouteCatalogue.hpp
 create mode 100644 catalogue/interfaces/DiskInstanceCatalogue.hpp
 create mode 100644 catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp
 create mode 100644 catalogue/interfaces/DiskSystemCatalogue.hpp
 create mode 100644 catalogue/interfaces/DriveConfigCatalogue.hpp
 create mode 100644 catalogue/interfaces/DriveStateCatalogue.hpp
 create mode 100644 catalogue/interfaces/FileRecycleLogCatalogue.hpp
 create mode 100644 catalogue/interfaces/LogicalLibraryCatalogue.hpp
 create mode 100644 catalogue/interfaces/MediaTypeCatalogue.hpp
 create mode 100644 catalogue/interfaces/MountPolicyCatalogue.hpp
 create mode 100644 catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp
 create mode 100644 catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp
 create mode 100644 catalogue/interfaces/RequesterMountRuleCatalogue.hpp
 create mode 100644 catalogue/interfaces/SchemaCatalogue.hpp
 create mode 100644 catalogue/interfaces/StorageClassCatalogue.hpp
 create mode 100644 catalogue/interfaces/TapeCatalogue.hpp
 create mode 100644 catalogue/interfaces/TapeFileCatalogue.hpp
 create mode 100644 catalogue/interfaces/TapePoolCatalogue.hpp
 create mode 100644 catalogue/interfaces/VirtualOrganizationCatalogue.hpp
 create mode 100644 catalogue/rdbms/CommonExceptions.hpp
 create mode 100644 catalogue/rdbms/RdbmsAdminUserCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsAdminUserCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsArchiveFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsArchiveRouteCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsArchiveRouteCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsCatalogue.hpp
 rename catalogue/{ => rdbms}/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp (99%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp (98%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueGetArchiveFilesItor.cpp (99%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueGetArchiveFilesItor.hpp (98%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueGetFileRecycleLogItor.cpp (99%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueGetFileRecycleLogItor.hpp (96%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueTapeContentsItor.cpp (99%)
 rename catalogue/{ => rdbms}/RdbmsCatalogueTapeContentsItor.hpp (97%)
 create mode 100644 catalogue/rdbms/RdbmsCatalogueUtils.cpp
 create mode 100644 catalogue/rdbms/RdbmsCatalogueUtils.hpp
 create mode 100644 catalogue/rdbms/RdbmsDiskInstanceCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsDiskSystemCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsDriveConfigCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsDriveStateCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsDriveStateCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsFileRecycleLogCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsLogicalLibraryCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsMediaTypeCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsMountPolicyCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsSchemaCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsSchemaCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsStorageClassCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsStorageClassCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsTapeCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsTapeCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsTapeFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsTapeFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsTapePoolCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsTapePoolCatalogue.hpp
 create mode 100644 catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.cpp
 create mode 100644 catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleArchiveFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleMediaTypeCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleMediaTypeCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleStorageClassCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleStorageClassCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleTapeCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleTapeCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleTapeFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleTapeFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleTapePoolCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleTapePoolCatalogue.hpp
 create mode 100644 catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.cpp
 create mode 100644 catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresStorageClassCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresStorageClassCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresTapeCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresTapeCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresTapeFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresTapeFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresTapePoolCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresTapePoolCatalogue.hpp
 create mode 100644 catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.cpp
 create mode 100644 catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteTapeCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.hpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.cpp
 create mode 100644 catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.hpp
 create mode 100644 catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/CatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/CatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/SchemaCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/SchemaCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/TapeCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/TapeCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/TapePoolCatalogueRetryWrapper.hpp
 create mode 100644 catalogue/retrywrappers/TapePoolRetryCatalogueWrapper.cpp
 create mode 100644 catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.cpp
 create mode 100644 catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.hpp
 rename catalogue/{ => retrywrappers}/retryOnLostConnection.hpp (94%)
 create mode 100644 catalogue/tests/CatalogueTestUtils.cpp
 create mode 100644 catalogue/tests/CatalogueTestUtils.hpp
 create mode 100644 catalogue/tests/DbConfigVersionOfCatalogueTest.cpp
 create mode 100644 catalogue/tests/DriveConfigCatalogueTest.cpp
 create mode 100644 catalogue/tests/DriveConfigCatalogueTest.hpp
 create mode 100644 catalogue/tests/DriveStateCatalogueTest.cpp
 create mode 100644 catalogue/tests/DriveStateCatalogueTest.hpp
 rename catalogue/{ => tests}/InMemoryCatalogueTest.cpp (94%)
 create mode 100644 catalogue/tests/InMemoryVersionOfCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/AdminUserCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/AdminUserCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/ArchiveFileCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/ArchiveFileCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/ArchiveRouteCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/DiskInstanceCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/DiskInstanceCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/DiskSystemCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/DiskSystemCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/DriveConfigCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/DriveConfigCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/DriveStateCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/DriveStateCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/FileRecycleLogCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/LogicalLibraryCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/MediaTypeCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/MediaTypeCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/MountPolicyCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/MountPolicyCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/RequesterActivityMountRuleTest.cpp
 create mode 100644 catalogue/tests/modules/RequesterActivityMountRuleTest.hpp
 create mode 100644 catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/RequesterMountRuleTest.cpp
 create mode 100644 catalogue/tests/modules/RequesterMountRuleTest.hpp
 create mode 100644 catalogue/tests/modules/SchemaCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/SchemaCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/StorageClassCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/StorageClassCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/TapeCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/TapeCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/TapeFileCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/TapeFileCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/TapePoolCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/TapePoolCatalogueTest.hpp
 create mode 100644 catalogue/tests/modules/VirtualOrganizationCatalogueTest.cpp
 create mode 100644 catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp

diff --git a/catalogue/12.0/oracle_catalogue_schema.sql b/catalogue/12.0/oracle_catalogue_schema.sql
index e835a40c13..a2cbda1b88 100644
--- a/catalogue/12.0/oracle_catalogue_schema.sql
+++ b/catalogue/12.0/oracle_catalogue_schema.sql
@@ -120,8 +120,8 @@ CREATE UNIQUE INDEX DISK_INSTANCE_SPACE_DISN_UN_IDX ON DISK_INSTANCE_SPACE(DISK_
 
 CREATE TABLE DISK_SYSTEM(
   DISK_SYSTEM_NAME        VARCHAR2(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,
-  DISK_INSTANCE_NAME       VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   
-  DISK_INSTANCE_SPACE_NAME VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   
+  DISK_INSTANCE_NAME       VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,
+  DISK_INSTANCE_SPACE_NAME VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,
   FILE_REGEXP             VARCHAR2(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,
   TARGETED_FREE_SPACE     NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,
   SLEEP_TIME              NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,
diff --git a/catalogue/12.0/postgres_catalogue_schema.sql b/catalogue/12.0/postgres_catalogue_schema.sql
index 6b150277c8..d6aeaa54e9 100644
--- a/catalogue/12.0/postgres_catalogue_schema.sql
+++ b/catalogue/12.0/postgres_catalogue_schema.sql
@@ -102,8 +102,8 @@ CREATE UNIQUE INDEX DISK_INSTANCE_SPACE_DISN_UN_IDX ON DISK_INSTANCE_SPACE(DISK_
 
 CREATE TABLE DISK_SYSTEM(
   DISK_SYSTEM_NAME        VARCHAR(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,
-  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   
-  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   
+  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,
+  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,
   FILE_REGEXP             VARCHAR(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,
   TARGETED_FREE_SPACE     NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,
   SLEEP_TIME              NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,
diff --git a/catalogue/12.0/sqlite_catalogue_schema.sql b/catalogue/12.0/sqlite_catalogue_schema.sql
index c0afd68d4e..2406f4f4f6 100644
--- a/catalogue/12.0/sqlite_catalogue_schema.sql
+++ b/catalogue/12.0/sqlite_catalogue_schema.sql
@@ -74,8 +74,8 @@ CREATE UNIQUE INDEX DISK_INSTANCE_SPACE_DISN_UN_IDX ON DISK_INSTANCE_SPACE(DISK_
 
 CREATE TABLE DISK_SYSTEM(
   DISK_SYSTEM_NAME        VARCHAR(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,
-  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   
-  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   
+  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,
+  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,
   FILE_REGEXP             VARCHAR(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,
   TARGETED_FREE_SPACE     INTEGER      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,
   SLEEP_TIME              INTEGER      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,
diff --git a/catalogue/AllCatalogueSchema.hpp b/catalogue/AllCatalogueSchema.hpp
index f0d9a1458a..6b0aa7fb73 100644
--- a/catalogue/AllCatalogueSchema.hpp
+++ b/catalogue/AllCatalogueSchema.hpp
@@ -5020,8 +5020,8 @@ namespace catalogue{
   ""
   "CREATE TABLE DISK_SYSTEM("
   "  DISK_SYSTEM_NAME        VARCHAR2(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,"
-  "  DISK_INSTANCE_NAME       VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   "
-  "  DISK_INSTANCE_SPACE_NAME VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   "
+  "  DISK_INSTANCE_NAME       VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,"
+  "  DISK_INSTANCE_SPACE_NAME VARCHAR2(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,"
   "  FILE_REGEXP             VARCHAR2(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,"
   "  TARGETED_FREE_SPACE     NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,"
   "  SLEEP_TIME              NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,"
@@ -5505,8 +5505,8 @@ namespace catalogue{
   ""
   "CREATE TABLE DISK_SYSTEM("
   "  DISK_SYSTEM_NAME        VARCHAR(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,"
-  "  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   "
-  "  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   "
+  "  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,"
+  "  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,"
   "  FILE_REGEXP             VARCHAR(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,"
   "  TARGETED_FREE_SPACE     INTEGER      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,"
   "  SLEEP_TIME              INTEGER      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,"
@@ -6015,8 +6015,8 @@ namespace catalogue{
   ""
   "CREATE TABLE DISK_SYSTEM("
   "  DISK_SYSTEM_NAME        VARCHAR(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,"
-  "  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   "
-  "  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   "
+  "  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,"
+  "  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,"
   "  FILE_REGEXP             VARCHAR(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,"
   "  TARGETED_FREE_SPACE     NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,"
   "  SLEEP_TIME              NUMERIC(20, 0)      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,"
diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt
index 50fb86782f..77945af0f3 100644
--- a/catalogue/CMakeLists.txt
+++ b/catalogue/CMakeLists.txt
@@ -33,31 +33,24 @@ include_directories(${CMAKE_BINARY_DIR}/eos_cta ${PROTOBUF3_INCLUDE_DIRS})
 
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow")
 
-set (CATALOGUE_LIB_SRC_FILES
+file (GLOB CATALOGUE_LIB_SRC_FILES
   ArchiveFileBuilder.cpp
   ArchiveFileRow.cpp
   ArchiveFileRowWithoutTimestamps.cpp
-  Catalogue.cpp
   CatalogueFactory.cpp
   CatalogueFactoryFactory.cpp
-  CatalogueRetryWrapper.cpp
   CatalogueSchema.cpp
+  CatalogueUtils.cpp
   CmdLineTool.cpp
   DriveConfig.cpp
-  DummyCatalogue.cpp
+  dummy/*.cpp
   InMemoryCatalogue.cpp
   InMemoryCatalogueFactory.cpp
-  PostgresCatalogue.cpp
   PostgresqlCatalogueFactory.cpp
-  RdbmsCatalogue.cpp
-  RdbmsCatalogueGetArchiveFilesForRepackItor.cpp
-  RdbmsCatalogueGetArchiveFilesItor.cpp
-  RdbmsCatalogueGetFileRecycleLogItor.cpp
-  RdbmsCatalogueTapeContentsItor.cpp
-  SchemaCreatingSqliteCatalogue.cpp
-  SchemaVersion.cpp
-  SqliteCatalogue.cpp
-  SqliteCatalogueSchema.cpp
+  rdbms/*.cpp
+  rdbms/postgres/*.cpp
+  rdbms/sqlite/*.cpp
+  retrywrappers/*.cpp
   TapeDrivesCatalogueState.cpp
   TapeFileWritten.cpp
   TapeForWriting.cpp
@@ -66,12 +59,21 @@ set (CATALOGUE_LIB_SRC_FILES
 )
 
 if (OCCI_SUPPORT)
-  set (CATALOGUE_LIB_SRC_FILES
+  file (GLOB CATALOGUE_LIB_SRC_FILES
     ${CATALOGUE_LIB_SRC_FILES}
-    OracleCatalogue.cpp
-    OracleCatalogueFactory.cpp)
+    OracleCatalogueFactory.cpp
+    rdbms/oracle/*.cpp
+  )
 endif()
 
+# It use set because it was giving linking error
+set (CATALOGUE_LIB_SRC_FILES
+  ${CATALOGUE_LIB_SRC_FILES}
+  SchemaCreatingSqliteCatalogue.cpp
+  SchemaVersion.cpp
+  SqliteCatalogueSchema.cpp
+)
+
 add_library (ctacatalogue SHARED
    ${CATALOGUE_LIB_SRC_FILES})
 
@@ -145,11 +147,17 @@ add_custom_command(OUTPUT PostgresCatalogueSchema.cpp
   COMMAND sed -e '/CTA_SQL_SCHEMA/r postgres_catalogue_schema.cpp' ${CMAKE_CURRENT_SOURCE_DIR}/PostgresCatalogueSchema.before_SQL.cpp > PostgresCatalogueSchema.cpp
   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/PostgresCatalogueSchema.before_SQL.cpp postgres_catalogue_schema.cpp)
 
+file (GLOB CATALOGUE_MODULES_TESTS_SRC_FILES
+  tests/modules/*.cpp
+)
+
 set(IN_MEMORY_CATALOGUE_UNIT_TESTS_LIB_SRC_FILES
-  CatalogueTest.cpp
-  InMemoryCatalogueTest.cpp
-  InMemoryVersionOfCatalogueTest.cpp
-  TapeItemWrittenPointerTest.cpp)
+  ${CATALOGUE_MODULES_TESTS_SRC_FILES}
+  TapeItemWrittenPointerTest.cpp
+  tests/CatalogueTestUtils.cpp
+  tests/InMemoryCatalogueTest.cpp
+  tests/InMemoryVersionOfCatalogueTest.cpp
+)
 
 add_library (ctainmemorycatalogueunittests SHARED
   ${IN_MEMORY_CATALOGUE_UNIT_TESTS_LIB_SRC_FILES})
@@ -165,8 +173,10 @@ target_link_libraries (ctainmemorycatalogueunittests
 install (TARGETS ctainmemorycatalogueunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
 
 set (DBCONFIG_CATALOGUE_UNIT_TESTS_LIB_SRC_FILES
-  CatalogueTest.cpp
-  DbConfigVersionOfCatalogueTest.cpp)
+  ${CATALOGUE_MODULES_TESTS_SRC_FILES}
+  tests/CatalogueTestUtils.cpp
+  tests/DbConfigVersionOfCatalogueTest.cpp
+)
 
 add_library (ctadbconfigcatalogueunittests SHARED
   ${DBCONFIG_CATALOGUE_UNIT_TESTS_LIB_SRC_FILES})
diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index 30c1cc2719..dd0e009039 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -17,125 +17,35 @@
 
 #pragma once
 
-#include <list>
-#include <map>
 #include <memory>
-#include <optional>
-#include <set>
-#include <string>
-#include <tuple>
-#include <utility>
 
-#include "catalogue/RecyleTapeFileSearchCriteria.hpp"
-#include "catalogue/TapeFileSearchCriteria.hpp"
-#include "catalogue/TapePoolSearchCriteria.hpp"
-#include "catalogue/TapeSearchCriteria.hpp"
-#include "common/dataStructures/DiskSpaceReservationRequest.hpp"
-#include "common/dataStructures/VidToTapeMap.hpp"
-#include "common/exception/UserError.hpp"
+#include "catalogue/interfaces/AdminUserCatalogue.hpp"
+#include "catalogue/interfaces/ArchiveFileCatalogue.hpp"
+#include "catalogue/interfaces/ArchiveRouteCatalogue.hpp"
+#include "catalogue/interfaces/DiskInstanceCatalogue.hpp"
+#include "catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp"
+#include "catalogue/interfaces/DiskSystemCatalogue.hpp"
+#include "catalogue/interfaces/DriveConfigCatalogue.hpp"
+#include "catalogue/interfaces/DriveStateCatalogue.hpp"
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/interfaces/LogicalLibraryCatalogue.hpp"
+#include "catalogue/interfaces/MediaTypeCatalogue.hpp"
+#include "catalogue/interfaces/MountPolicyCatalogue.hpp"
+#include "catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp"
+#include "catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp"
+#include "catalogue/interfaces/RequesterMountRuleCatalogue.hpp"
+#include "catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp"
+#include "catalogue/interfaces/RequesterMountRuleCatalogue.hpp"
+#include "catalogue/interfaces/SchemaCatalogue.hpp"
+#include "catalogue/interfaces/StorageClassCatalogue.hpp"
+#include "catalogue/interfaces/TapeCatalogue.hpp"
+#include "catalogue/interfaces/TapeFileCatalogue.hpp"
+#include "catalogue/interfaces/TapePoolCatalogue.hpp"
+#include "catalogue/interfaces/VirtualOrganizationCatalogue.hpp"
 
 namespace cta {
-namespace common {
-namespace dataStructures {
-struct AdminUser;
-struct ArchiveFile;
-struct ArchiveFileQueueCriteria;
-struct ArchiveFileSummary;
-struct ArchiveRoute;
-struct DeleteArchiveRequest;
-struct DesiredDriveState;
-struct DiskInstance;
-struct DiskInstanceSpace;
-struct DriveState;
-struct FileRecycleLog;
-struct LogicalLibrary;
-struct MountPolicy;
-struct RequesterActivityMountRule;
-struct RequesterGroupMountRule;
-struct RequesterIdentity;
-struct RequesterMountRule;
-struct RetrieveFileQueueCriteria;
-struct SecurityIdentity;
-struct StorageClass;
-struct Tape;
-struct TapeDrive;
-struct TapeDriveStatistics;
-struct TapeFile;
-struct VirtualOrganization;
-}  // namespace dataStructures
-}  // namespace common
-
-namespace disk {
-class DiskSystem;
-class DiskSystemList;
-}
-
-namespace log {
-class LogContext;
-}
-
 namespace catalogue {
 
-template <typename Item>
-class CatalogueItor;
-
-struct CreateMountPolicyAttributes;
-struct CreateTapeAttributes;
-struct MediaType;
-struct MediaTypeWithLogs;
-struct SchemaVersion;
-struct TapeForWriting;
-struct TapeItemWrittenPointer;
-struct TapePool;
-
-CTA_GENERATE_EXCEPTION_CLASS(CommentOrReasonWithMoreSizeThanMaximunAllowed);
-CTA_GENERATE_EXCEPTION_CLASS(NegativeDiskSpaceReservationReached);
-CTA_GENERATE_EXCEPTION_CLASS(WrongSchemaVersionException);
-
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringCartridge);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringComment);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskInstanceName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskInstanceSpaceName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskSystemName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringFileRegexp);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringFreeSpaceQueryURL);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringLogicalLibraryName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringMediaType);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringMediaTypeName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringStorageClassName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringTapePoolName);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringUsername);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringVendor);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringVid);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringVo);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyTapePool);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyDiskInstanceAfterDelete);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyDiskInstanceSpaceAfterDelete);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyDiskSystemAfterDelete);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyLogicalLibrary);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyTape);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentArchiveRoute);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentDiskInstance);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentDiskInstanceSpace);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentDiskSystem);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentLogicalLibrary);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTape);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTapePool);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTapeState);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentVirtualOrganization);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroCapacity);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroCopyNb);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroRefreshInterval);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroSleepTime);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroTargetedFreeSpace);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedExistingDeletedFileCopy);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedMediaTypeUsedByTapes);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedStorageClassUsedByArchiveFiles);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedStorageClassUsedByArchiveRoutes);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedStorageClassUsedByFileRecycleLogs);
-CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedTapePoolUsedInAnArchiveRoute);
-
 /**
  * Abstract class defining the interface to the CTA catalogue responsible for
  * storing critical information about archive files, tapes and tape files.
@@ -145,1286 +55,30 @@ public:
   /**
    * Destructor.
    */
-  virtual ~Catalogue() = 0;
-
-  //////////////////////////////////////////////////////////////////
-  // START OF METHODS DIRECTLY INVOLVED DATA TRANSFER AND SCHEDULING
-  //////////////////////////////////////////////////////////////////
-
-  /**
-   * Notifies the catalogue that the specified tape was labelled.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param drive The name of tape drive that was used to label the tape.
-   */
-  virtual void tapeLabelled(const std::string &vid, const std::string &drive) = 0;
-
-  /**
-   * Checks the specified archival could take place and returns a new and
-   * unique archive file identifier that can be used by a new archive file
-   * within the catalogue.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * storage class belongs.
-   * @param storageClassName The name of the storage class of the file to be
-   * archived.  The storage class name is only guaranteed to be unique within
-   * its disk instance.  The storage class name will be used by the Catalogue
-   * to determine the destination tape pool for each tape copy.
-   * @param user The user for whom the file is to be archived.  This will be
-   * used by the Catalogue to determine the mount policy to be used when
-   * archiving the file.
-   * @return The new archive file identifier.
-   * @throw UserErrorWithCacheInfo if there was a user error.
-   */
-  virtual uint64_t checkAndGetNextArchiveFileId(
-    const std::string &diskInstanceName,
-    const std::string &storageClassName,
-    const common::dataStructures::RequesterIdentity &user) = 0;
-
-  /**
-   * Returns the information required to queue an archive request.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * storage class belongs.
-   * @param storageClassName The name of the storage class of the file to be
-   * archived.  The storage class name is only guaranteed to be unique within
-   * its disk instance.  The storage class name will be used by the Catalogue
-   * to determine the destination tape pool for each tape copy.
-   * @param user The user for whom the file is to be archived.  This will be
-   * used by the Catalogue to determine the mount policy to be used when
-   * archiving the file.
-   * @return The information required to queue an archive request.
-   */
-  virtual common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(
-    const std::string &diskInstanceName,
-    const std::string &storageClassName,
-    const common::dataStructures::RequesterIdentity &user) = 0;
-
-  /**
-   * Returns the list of tapes that can be written to by a tape drive in the
-   * specified logical library, in other words tapes that are labelled, not
-   * disabled, not full, not read-only and are in the specified logical library.
-   *
-   * @param logicalLibraryName The name of the logical library.
-   * @return The list of tapes for writing.
-   */
-  virtual std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const = 0;
-
-  virtual common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const = 0;
-
-  /**
-   * Notifies the catalogue that the specified files have been written to tape.
-   *
-   * @param events The tape file written events.
-   * @throw TapeFseqMismatch If an unexpected tape file sequence number is encountered.
-   * @throw FileSizeMismatch If an unexpected tape file size is encountered.
-   */
-  virtual void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) = 0;
-
-  /**
-   * Notifies the CTA catalogue that the specified tape has been mounted in
-   * order to archive files.
-   *
-   * The purpose of this method is to keep track of which drive mounted a given
-   * tape for archiving files last.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param drive The name of the drive where the tape was mounted.
-   */
-  virtual void tapeMountedForArchive(const std::string &vid, const std::string &drive) = 0;  // internal function (noCLI)
-
-  /**
-   * Prepares for a file retrieval by returning the information required to
-   * queue the associated retrieve request(s).
-   *
-   * @param diskInstanceName The name of the instance from where the retrieval
-   * request originated
-   * @param archiveFileId The unique identifier of the archived file that is
-   * to be retrieved.
-   * @param user The user for whom the file is to be retrieved.  This will be
-   * used by the Catalogue to determine the mount policy to be used when
-   * retrieving the file.
-   * @param activity The activity under which the user wants to start the retrieve
-   * The call will fail if the activity is set and unknown.
-   * @param lc The log context.
-   *
-   * @return The information required to queue the associated retrieve request(s).
-   */
-  virtual common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(
-    const std::string &diskInstanceName,
-    const uint64_t archiveFileId,
-    const common::dataStructures::RequesterIdentity &user,
-    const std::optional<std::string> & activity,
-    log::LogContext &lc,
-    const std::optional<std::string> &mountPolicyName =  std::nullopt) = 0;
-
-  /**
-   * Notifies the CTA catalogue that the specified tape has been mounted in
-   * order to retrieve files.
-   *
-   * The purpose of this method is to keep track of which drive mounted a given
-   * tape for retrieving files last.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param drive The name of the drive where the tape was mounted.
-   */
-  virtual void tapeMountedForRetrieve(const std::string &vid, const std::string &drive) = 0;  // internal function (noCLI)
-
-  /**
-   * This method notifies the CTA catalogue that there is no more free space on
-   * the specified tape.
-   *
-   * @param vid The volume identifier of the tape.
-   */
-  virtual void noSpaceLeftOnTape(const std::string &vid) = 0;
-
-  ////////////////////////////////////////////////////////////////
-  // END OF METHODS DIRECTLY INVOLVED DATA TRANSFER AND SCHEDULING
-  ////////////////////////////////////////////////////////////////
-
-  virtual void createAdminUser(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) = 0;
-  virtual void deleteAdminUser(const std::string &username) = 0;
-  virtual std::list<common::dataStructures::AdminUser> getAdminUsers() const = 0;
-  virtual void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) = 0;
-
-  /**
-   * Creates the specified Virtual Organization
-   * @param admin The administrator.
-   * @param vo the Virtual Organization
-   */
-  virtual void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) = 0;
-
-  /**
-   * Deletes the specified Virtual Organization
-   * @param voName the name of the VirtualOrganization to delete
-   */
-  virtual void deleteVirtualOrganization(const std::string &voName) = 0;
-
-  /**
-   * Get all the Virtual Organizations from the Catalogue
-   * @return the list of all the Virtual Organizations
-   */
-  virtual std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const = 0;
-
-  /**
-   * Get the virtual organization corresponding to the tapepool passed in parameter
-   * @param tapepoolName the name of the tapepool which we want the virtual organization
-   * @return the VirtualOrganization associated to the tapepool passed in parameter
-   */
-  virtual common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(const std::string & tapepoolName) const = 0;
-
-  /**
-   * Get, from the cache, the virtual organization corresponding to the tapepool passed in parameter
-   * @param tapepoolName the name of the tapepool which we want the virtual organization
-   * @return the VirtualOrganization associated to the tapepool passed in parameter
-   */
-  virtual common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(const std::string & tapepoolName) const = 0;
-
-  /**
-   * Modifies the name of the specified Virtual Organization.
-   *
-   * @param currentVoName The current name of the Virtual Organization.
-   * @param newVoName The new name of the Virtual Organization.
-   */
-  virtual void modifyVirtualOrganizationName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName, const std::string &newVoName) = 0;
-
-  /**
-   * Modifies the max number of allocated drives for read for the specified Virtual Organization
-   *
-   * @param voName the VO name
-   * @param readMaxDrives the new max number of allocated drives for read for the specified Virtual Organization
-   */
-  virtual void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) = 0;
-
-  /**
-   * Modifies the max number of allocated drives for write for the specified Virtual Organization
-   *
-   * @param voName the VO name
-   * @param writeMaxDrives the new max number of allocated drives for write for the specified Virtual Organization
-   */
-  virtual void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) = 0;
-
-  /**
-   * Modifies the max size of files  for the specified Virtual Organization
-   *
-   * @param voName the VO name
-   * @param maxFileSize the new max file size for the specified Virtual Organization
-   */
-  virtual void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) = 0;
-
-  /**
-   * Modifies the comment of the specified Virtual Organization
-   *
-   * @param voName The name of the Virtual Organization.
-   * @param comment The new comment of the Virtual Organization.
-   */
-  virtual void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) = 0;
-
-  /**
-   * Modifies the comment of the specified Virtual Organization
-   *
-   * @param voName The name of the Virtual Organization.
-   * @param diskInstance The new disk instance of the Virtual Organization.
-   */
-  virtual void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) = 0;
-
-  /**
-   * Creates the specified storage class.
-   *
-   * @param admin The administrator.
-   * @param storageClass The storage class.
-   */
-  virtual void createStorageClass(
-    const common::dataStructures::SecurityIdentity &admin,
-    const common::dataStructures::StorageClass &storageClass) = 0;
-
-  /**
-   * Deletes the specified storage class.
-   *
-   * @param storageClassName The name of the storage class which is only
-   * guaranteed to be unique within its disk instance.
-   */
-  virtual void deleteStorageClass(const std::string &storageClassName) = 0;
-
-  virtual std::list<common::dataStructures::StorageClass> getStorageClasses() const = 0;
-  virtual common::dataStructures::StorageClass getStorageClass(const std::string &name) const = 0;
-
-  virtual void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbCopies) = 0;
-  virtual void modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) = 0;
-  virtual void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) = 0;
-
-  /**
-   * Modifies the name of the specified storage class.
-   *
-   * @param currentName The current name of the storage class.
-   * @param newName The new name of the storage class.
-   */
-  virtual void modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) = 0;
-
-  /**
-   * Creates a tape media type.
-   *
-   * @param admin The administrator.
-   * @param mediaType The tape media type.
-   */
-  virtual void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) = 0;
-
-  /**
-   * Deletes the specified tape media type.
-   *
-   * @param name The name of the tape media type.
-   */
-  virtual void deleteMediaType(const std::string &name) = 0;
-
-  /**
-   * Returns all tape media types.
-   *
-   * @return All tape media types.
-   */
-  virtual std::list<MediaTypeWithLogs> getMediaTypes() const = 0;
-
-  /**
-   * Return the media type associated to the tape corresponding to the
-   * vid passed in parameter
-   * @param vid the vid of the tape to return its media type
-   * @return the media type associated to the tape corresponding to the vid passed in parameter
-   */
-  virtual MediaType getMediaTypeByVid(const std::string & vid) const = 0;
-
-  /**
-   * Modifies the name of the specified tape media type.
-   *
-   * @param admin The administrator.
-   * @param currentName The current name of the tape media type.
-   * @param newName The new name of the tape media type.
-   */
-  virtual void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) = 0;
-
-  /**
-   * Modifies the cartidge of the specified tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param cartridge The new cartidge.
-   */
-  virtual void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &cartridge) = 0;
-
-  /**
-   * Modify the capacity in bytes of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param capacityInBytes The new capacity in bytes.
-   */
-  virtual void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t capacityInBytes) = 0;
-
-  /**
-   * Modify the SCSI primary density code of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param primaryDensityCode The new SCSI primary density code.
-   */
-  virtual void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t primaryDensityCode) = 0;
-
-  /**
-   * Modify the SCSI secondary density code of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param secondaryDensityCode The new SCSI secondary density code.
-   */
-  virtual void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t secondaryDensityCode) = 0;
-
-  /**
-   * Modify the number of tape wraps of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param nbWraps The new number of tape wraps.
-   */
-  virtual void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint32_t> &nbWraps) = 0;
-
-  /**
-   * Modify the minimum longitudinal tape position of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param minLPos The new minimum longitudinal tape position.
-   */
-  virtual void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &minLPos) = 0;
-
-  /**
-   * Modify the maximum longitudinal tape position of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param maxLPos The new maximum longitudinal tape position.
-   */
-  virtual void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &maxLPos) = 0;
-
-  /**
-   * Modify the comment of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param comment The new comment.
-   */
-  virtual void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) = 0;
-
-  virtual void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue, const std::optional<std::string> &supply, const std::string &comment) = 0;
-
- /**
-  * Deletes the specified tape pool.
-
-  * @name The name of th epatpe pool.
-  * @throw UserSpecifiedTapePoolUsedInAnArchiveRoute If the specified tape pool
-  *  is used in an archive route.
-  */
-  virtual void deleteTapePool(const std::string &name) = 0;
-
-  virtual std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria = TapePoolSearchCriteria()) const = 0;
-
-  /**
-   * @return The tape pool with the specified name.
-   * @param tapePoolName The name of the tape pool.
-   */
-  virtual std::optional<TapePool> getTapePool(const std::string &tapePoolName) const = 0;
-
-  virtual void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) = 0;
-  virtual void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbPartialTapes) = 0;
-  virtual void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) = 0;
-  virtual void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool encryptionValue) = 0;
-  virtual void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &supply) = 0;
-
-  /**
-   * Modifies the name of the specified tape pool.
-   *
-   * @param admin The administrator.
-   * @param currentName The current name of the tape pool.
-   * @param newName The new name of the tape pool.
-   */
-  virtual void modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) = 0;
-
-  virtual void createArchiveRoute(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &storageClassName,
-    const uint32_t copyNb,
-    const std::string &tapePoolName,
-    const std::string &comment) = 0;
-
-  /**
-   * Deletes the specified archive route.
-   *
-   * @param storageClassName The name of the storage class.
-   * @param copyNb The copy number of the tape file.
-   */
-  virtual void deleteArchiveRoute(
-    const std::string &storageClassName,
-    const uint32_t copyNb) = 0;
-
-  virtual std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const = 0;
-
-  /**
-   * @return the archive routes of the given storage class and destination tape
-   * pool.
-   *
-   * Under normal circumstances this method should return either 0 or 1 route.
-   * For a given storage class there should be no more than one route to any
-   * given tape pool.
-   *
-   * @param storageClassName The name of the storage class.
-   * @param tapePoolName The name of the tape pool.
-   */
-  virtual std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(
-    const std::string &storageClassName,
-    const std::string &tapePoolName) const = 0;
-
-  /**
-   * Modifies the tape pool of the specified archive route.
-   *
-   * @param admin The administrator.
-   * @param storageClassName The name of the storage class.
-   * @param copyNb The copy number.
-   * @param tapePoolName The name of the tape pool.
-   * @throw UserSpecifiedANonExistentTapePool if the user specified a
-   * non-existent tape pool.
-   */
-  virtual void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) = 0;
-
-  virtual void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) = 0;
-
-  virtual void createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool isDisabled, const std::string &comment) = 0;
-  virtual void deleteLogicalLibrary(const std::string &name) = 0;
-  virtual std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const = 0;
-
-  /**
-   * Modifies the name of the specified logical library.
-   *
-   * @param admin The administrator.
-   * @param currentName The current name of the logical library.
-   * @param newName The new name of the logical library.
-   */
-  virtual void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) = 0;
-
-  virtual void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) = 0;
-  virtual void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &disabledReason) = 0;
-  virtual void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool disabledValue) = 0;
-
-  /**
-   * Creates a tape which is assumed to have isFromCastor disabled.
-   *
-   * @param admin The administrator.
-   * @param tape The attributes of the tape to be created.
-   */
-  virtual void createTape(
-    const common::dataStructures::SecurityIdentity &admin,
-    const CreateTapeAttributes &tape) = 0;
-
-  virtual void deleteTape(const std::string &vid) = 0;
-
-  /**
-   * Returns the list of tapes that meet the specified search criteria.
-   *
-   * @param searchCriteria The search criteria.
-   * @return The list of tapes.
-   * @throw UserSpecifiedANonExistentTapePool if the user specified a
-   * non-existent tape pool.
-   */
-  virtual std::list<common::dataStructures::Tape> getTapes(
-    const TapeSearchCriteria &searchCriteria = TapeSearchCriteria()) const = 0;
-
-  /**
-   * Returns the tape with the specified volume identifier.
-   *
-   * This method will throw an exception if it cannot find the specified tape.
-   *
-   * @param vids The tape volume identifier (VID).
-   * @return Map from tape volume identifier to tape.
-   */
-  virtual common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const = 0;
-
-  /**
-   * Returns the tapes with the specified volume identifiers.
-   *
-   * This method will throw an exception if it cannot find ALL of the specified
-   * tapes.
-   *
-   * @param vids The tape volume identifiers (VIDs).
-   * @return Map from tape volume identifier to tape.
-   */
-  virtual common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string> &vids) const = 0;
-
-  /**
-   * Returns map from VID to logical library name for specified set of VIDs.
-   *
-   * @param vids The tape volume identifiers (VIDs).
-   * @return map from VID to logical library name.
-   */
-  virtual std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const = 0;
-
-  /**
-   * Reclaims the specified tape.
-   *
-   * This method will throw an exception if the specified tape does not exist.
-   *
-   * This method will throw an exception if the specified tape is not FULL.
-   *
-   * This method will throw an exception if there is still at least one tape
-   * file recorded in the catalogue as being on the specified tape.
-   *
-   * @param admin The administrator.
-   * @param vid The volume identifier of the tape to be reclaimed.
-   * @param lc the logContext
-   */
-  virtual void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, cta::log::LogContext & lc) = 0;
-
-  /**
-   * Checks the specified tape for the tape label command.
-   *
-   * This method checks if the tape is safe to be labeled and will throw an
-   * exception if the specified tape does not ready to be labeled.
-   *
-   * @param vid The volume identifier of the tape to be checked.
-   */
-  virtual void checkTapeForLabel(const std::string &vid) = 0;
-
-  /**
-   * Returns the number of any files contained in the tape identified by its vid
-   * @param vid the vid in which we will the number of files
-   * @return the number of files on the tape
-   */
-  virtual uint64_t getNbFilesOnTape(const std::string &vid) const = 0;
-
-  virtual void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &mediaType) = 0;
-  virtual void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &vendor) = 0;
-  virtual void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &logicalLibraryName) = 0;
-  virtual void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &tapePoolName) = 0;
-  virtual void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &encryptionKeyName) = 0;
-  virtual void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &verificationStatus) = 0;
-
-  /**
-   * Modify the state of the specified tape
-   * @param admin, the person or the system who modified the state of the tape
-   * @param vid the VID of the tape to change the state
-   * @param state the new state
-   * @param prev_state the previous state, if value is not std::nullopt then it will be used to validate that the state transition proceeds as expected
-   * @param stateReason the reason why the state changes, if the state is ACTIVE and the stateReason is std::nullopt, the state will be reset to null
-   */
-  virtual void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const std::optional<common::dataStructures::Tape::State> & prev_state, const std::optional<std::string> & stateReason) = 0;
-
-  /**
-   * Sets the full status of the specified tape.
-   *
-   * Please note that this method is to be called by the CTA front-end in
-   * response to a command from the CTA command-line interface (CLI).
-   *
-   * @param admin The administrator.
-   * @param vid The volume identifier of the tape to be marked as full.
-   * @param fullValue Set to true if the tape is full.
-   */
-  virtual void setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const bool fullValue) = 0;
-
-  /**
-   * Sets the dirty status of the specified tape.
-   *
-   * Please note that this method is to be called by the CTA front-end in
-   * response to a command from the CTA command-line interface (CLI).
-   *
-   * @param admin The administrator.
-   * @param vid The volume identifier of the tape to be marked as full.
-   * @param dirty Set to true if the tape is dirty.
-   */
-  virtual void setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const bool dirty) = 0;
-
-  /**
-   * This method notifies the CTA catalogue to set the specified tape is from CASTOR.
-   * This method only for unitTests and MUST never be called in CTA!!!
-   *
-   * @param vid The volume identifier of the tape.
-   */
-  virtual void setTapeIsFromCastorInUnitTests(const std::string &vid) = 0;
-
-  virtual void setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string & reason) = 0;
-  virtual void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string & reason) = 0;
-
-  virtual void setTapeDirty(const std::string & vid) = 0;
-
-  /**
-   * Modifies the tape comment
-   * If the comment == std::nullopt, it will delete the comment from the tape table
-   * @param admin the admin who removes the comment
-   * @param vid the vid of the tape to remove the comment
-   * @param comment the new comment. If comment == std::nullopt, the comment will be deleted.
-   */
-  virtual void modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::optional<std::string> &comment) = 0;
-
-  virtual void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex, const std::string &mountPolicy) = 0;
-  virtual void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex, const std::string &comment) = 0;
-  virtual void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) = 0;
-  virtual void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &comment) = 0;
-  virtual void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) = 0;
-  virtual void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) = 0;
-
-  virtual void createMountPolicy(const common::dataStructures::SecurityIdentity &admin, const CreateMountPolicyAttributes & mountPolicy) = 0;
-
-  /**
-   * Returns the list of all existing mount policies.
-   *
-   * @return the list of all existing mount policies.
-   */
-  virtual std::list<common::dataStructures::MountPolicy> getMountPolicies() const = 0;
-
-  /**
-   * Returns the mount policy with the specified name.
-   *
-   * @return the specified mount policy
-   */
-  virtual std::optional<common::dataStructures::MountPolicy> getMountPolicy(const std::string &mountPolicyName) const = 0;
-
-
-  /**
-   * Returns the cached list of all existing mount policies.
-   *
-   * @return the list of all existing mount policies.
-   */
-  virtual std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const = 0;
-
-  /**
-   * Deletes the specified mount policy.
-   *
-   * @param name The name of the mount policy.
-   */
-  virtual void deleteMountPolicy(const std::string &name) = 0;
-
-  /**
-   * Creates the rule that the specified mount policy will be used for the
-   * specified requester+matching activities.
-   *
-   * Please note that requester-activity mount-rules overrule requester
-   * mount-rules.
-   *
-   * @param admin The administrator.
-   * @param mountPolicyName The name of the mount policy.
-   * @param diskInstance The name of the disk instance to which the requester
-   * belongs.
-   * @param activityRegex The regex to match request activities
-   * @param requesterName The name of the requester which is only guarantted to
-   * be unique within its disk instance.
-   * @param comment Comment.
-   */
-  virtual void createRequesterActivityMountRule(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &mountPolicyName,
-    const std::string &diskInstance,
-    const std::string &requesterName,
-    const std::string &activityRegex,
-    const std::string &comment) = 0;
-
-  /**
-   * Returns the rules that specify which mount policy is be used for which
-   * requester + activity.
-   *
-   * @return the rules that specify which mount policy is be used for which
-   * requester + activity.
-   */
-  virtual std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const = 0;
-
-    /**
-   * Deletes the specified mount rule.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester belongs.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   * @param activityRegex The regex to match request activities
-   */
-  virtual void deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName, const std::string &activityRegex) = 0;
-
-  /**
-   * Creates the rule that the specified mount policy will be used for the
-   * specified requester.
-   *
-   * Please note that requester mount-rules overrule requester-group
-   * mount-rules.
-   *
-   * @param admin The administrator.
-   * @param mountPolicyName The name of the mount policy.
-   * @param diskInstance The name of the disk instance to which the requester
-   * belongs.
-   * @param requesterName The name of the requester which is only guarantted to
-   * be unique within its disk instance.
-   * @param comment Comment.
-   */
-  virtual void createRequesterMountRule(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &mountPolicyName,
-    const std::string &diskInstance,
-    const std::string &requesterName,
-    const std::string &comment) = 0;
-
-  /**
-   * Returns the rules that specify which mount policy is be used for which
-   * requester.
-   *
-   * @return the rules that specify which mount policy is be used for which
-   * requester.
-   */
-  virtual std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const = 0;
-
-  /**
-   * Deletes the specified mount rule.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester belongs.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   */
-  virtual void deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) = 0;
-
-  /**
-   * Creates the rule that the specified mount policy will be used for the
-   * specified requester group.
-   *
-   * Please note that requester mount-rules overrule requester-group
-   * mount-rules.
-   *
-   * @param admin The administrator.
-   * @param mountPolicyName The name of the mount policy.
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester group belongs.
-   * @param requesterGroupName The name of the requester group which is only
-   * guarantted to be unique within its disk instance.
-   * @param comment Comment.
-   */
-  virtual void createRequesterGroupMountRule(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &mountPolicyName,
-    const std::string &diskInstanceName,
-    const std::string &requesterGroupName,
-    const std::string &comment) = 0;
-
-  /**
-   * Returns the rules that specify which mount policy is be used for which
-   * requester group.
-   *
-   * @return the rules that specify which mount policy is be used for which
-   * requester group.
-   */
-  virtual std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const = 0;
-
-  /**
-   * Deletes the specified mount rule.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester group belongs.
-   * @param requesterGroupName The name of the requester group which is only
-   * guaranteed to be unique within its disk instance.
-   */
-  virtual void deleteRequesterGroupMountRule(
-    const std::string &diskInstanceName,
-    const std::string &requesterGroupName) = 0;
-
-  virtual void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t archivePriority) = 0;
-  virtual void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t minArchiveRequestAge) = 0;
-  virtual void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t retrievePriority) = 0;
-  virtual void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t minRetrieveRequestAge) = 0;
-  virtual void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) = 0;
-
-  /**
-   * Returns all the disk systems within the CTA catalogue.
-   *
-   * @return The disk systems.
-   * requester group.
-   */
-  virtual disk::DiskSystemList getAllDiskSystems() const = 0;
-
-  /**
-   * Creates a disk system.
-   *
-   * @param admin The administrator.
-   * @param name The name of the disk system.
-   * @param fileRegexp The regular expression allowing matching destination URLs
-   * for this disk system.
-   * @param freeSpaceQueryURL The query URL that describes a method to query the
-   * free space from the disk system.
-   * @param refreshInterval The refresh interval (seconds) defining how long do
-   * we use a free space value.
-   * @param targetedFreeSpace The targeted free space (margin) based on the free
-   * space update latency (inherent to the file system and induced by the refresh
-   * interval), and the expected external bandwidth from sources external to CTA.
-   * @param comment Comment.
-   */
-  virtual void createDiskSystem(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name,
-    const std::string &diskInstanceName,
-    const std::string &diskInstanceSpaceName,
-    const std::string &fileRegexp,
-    const uint64_t targetedFreeSpace,
-    const time_t sleepTime,
-    const std::string &comment) = 0;
-
-  /**
-   * Deletes a disk system.
-   *
-   * @param name The name of the disk system.
-   */
-  virtual void deleteDiskSystem(const std::string &name) = 0;
-
-  virtual void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &fileRegexp) = 0;
-  virtual void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const uint64_t targetedFreeSpace) = 0;
-  virtual void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const uint64_t sleepTime) = 0;
-  virtual void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &comment) = 0;
-  virtual void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstanceName) = 0;
-  virtual void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstanceSpaceName) = 0;
-
-  /**
-   * Deletes a disk instance space.
-   *
-   * @param name The name of the disk instance.
-   * @param diskInstance The disk instance of the disk instance space.
-   */
-  virtual void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) = 0;
-
-  /**
-   * Creates the specified Disk Instance Space
-   * @param admin The administrator.
-   * @param name the name of the new disk instance space
-   * @param diskInstance the disk instance associated to the disk instance space
-   * @param freeSpaceQueryURL the URL to query to obtain the disk instance space free space
-   * @param refreshInterval the period to query for disk instance space free space
-   * @param comment the comment of the new disk instance space
-   */
-  virtual void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name,
-    const std::string &diskInstance,
-    const std::string &freeSpaceQueryURL,
-    const uint64_t refreshInterval,
-    const std::string &comment) = 0;
-
-  /**
-   * Returns all the disk instance spaces within the CTA catalogue.
-   *
-   * @return The disk instance spaces in the CTA catalogue.
-   */
-  virtual std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const = 0;
-
-  /**
-   * Deletes a disk instance.
-   *
-   * @param name The name of the disk instance.
-   */
-  virtual void deleteDiskInstance(const std::string &name) = 0;
-
-  virtual void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const std::string &comment) = 0;
-  virtual void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) = 0;
-  virtual void modifyDiskInstanceSpaceFreeSpace(const std::string &name,
-    const std::string &diskInstance, const uint64_t freeSpace) = 0;
-  virtual void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
-    const std::string &freeSpaceQueryURL) = 0;
-
-
-  /**
-   * Changes the comment of the specified disk instance
-   * @param admin The administrator.
-   * @param name the name of the disk instance
-   * @param comment the new comment of the disk instance
-   */
-  virtual void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &comment) = 0;
-
-  /**
-   * Creates the specified Disk Instance
-   * @param admin The administrator.
-   * @param name the name of the new disk instance
-   * @param comment the comment of the new disk instance
-   */
-  virtual void createDiskInstance(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name,
-    const std::string &comment) = 0;
-
-  /**
-   * Returns all the disk instances within the CTA catalogue.
-   *
-   * @return The disk instances in the CTA catalogue.
-   */
-  virtual std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const = 0;
-
-
-  typedef CatalogueItor<common::dataStructures::ArchiveFile> ArchiveFileItor;
-
-  /**
-   * Returns the specified archive files.  Please note that the list of files
-   * is ordered by archive file ID.
-   *
-   * @param searchCriteria The search criteria.
-   * @return The archive files.
-   */
-  virtual ArchiveFileItor getArchiveFilesItor(
-    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const = 0;
-
-  /**
-   * Returns the specified archive file. If the search criteria result in more than one tape file being returned
-   * an exception is thrown.
-   * @param searchCriteria The search criteria.
-   * @return The archive file.
-   */
-  virtual common::dataStructures::ArchiveFile getArchiveFileForDeletion(const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const = 0;
-
-  typedef CatalogueItor<common::dataStructures::FileRecycleLog> FileRecycleLogItor;
-
-  /**
-   * Returns all the currently deleted files by looking at the FILE_RECYCLE_LOG table
-   *
-   * @return The deleted archive files ordered by archive file ID.
-   */
-  virtual FileRecycleLogItor getFileRecycleLogItor(const RecycleTapeFileSearchCriteria & searchCriteria = RecycleTapeFileSearchCriteria()) const = 0;
-
-
-  /**
-   * Restores the deleted file in the Recycle log that match the criteria passed
-   *
-   * @param searchCriteria The search criteria
-   * @param newFid the new Fid of the archive file (if the archive file must be restored)
-   */
-  virtual void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria, const std::string &newFid) = 0;
-
-
-  /**
-   * Returns the specified files in tape file sequence order.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param startFSeq The file sequence number of the first file.  Please note
-   * that there might not be a file with this exact file sequence number.
-   * @param maxNbFiles The maximum number of files to be returned.
-   * @return The specified files in tape file sequence order.
-   */
-  virtual std::list<common::dataStructures::ArchiveFile> getFilesForRepack(
-    const std::string &vid,
-    const uint64_t startFSeq,
-    const uint64_t maxNbFiles) const = 0;
-
-  /**
-   * Returns all the tape copies (no matter their VIDs) of the archive files
-   * associated with the tape files on the specified tape in FSEQ order
-   * starting at the specified startFSeq.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param startFSeq The file sequence number of the first file.  Please note
-   * that there might not be a file with this exact file sequence number.
-   * @return The specified files in FSEQ order.
-   */
-  virtual ArchiveFileItor getArchiveFilesForRepackItor(
-    const std::string &vid,
-    const uint64_t startFSeq) const = 0;
-
-  /**
-   * Returns a summary of the tape files that meet the specified search
-   * criteria.
-   *
-   * @param searchCriteria The search criteria.
-   * @return The summary.
-   */
-  virtual common::dataStructures::ArchiveFileSummary getTapeFileSummary(
-    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const = 0;
-
-  /**
-  * Deletes a tape file copy
-  *
-  * @param file The tape file to delete
-  * @param reason The reason for deleting the tape file copy
-  */
-  virtual void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) = 0;
-
-  /**
-   * Returns the archive file with the specified unique identifier.
-   *
-   * This method assumes that the archive file being requested exists and will
-   * therefore throw an exception if it does not.
-   *
-   * Please note that an archive file with no associated tape files is
-   * considered not to exist by this method.
-   *
-   * @param id The unique identifier of the archive file.
-   * @return The archive file.
-   */
-  virtual common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const = 0;
-
-  /**
-   * !!!!!!!!!!!!!!!!!!! THIS METHOD SHOULD NOT BE USED !!!!!!!!!!!!!!!!!!!!!!!
-   * Deletes the specified archive file and its associated tape copies from the
-   * catalogue.
-   *
-   * Please note that the name of the disk instance is specified in order to
-   * prevent a disk instance deleting an archive file that belongs to another
-   * disk instance.
-   *
-   * Please note that this method is idempotent.  If the file to be deleted does
-   * not exist in the CTA catalogue then this method returns without error.
-   *
-   * @param instanceName The name of the instance from where the deletion request
-   * originated
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param lc The log context.
-   * @return The metadata of the deleted archive file including the metadata of
-   * the associated and also deleted tape copies.
-   */
-  virtual void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &instanceName, const uint64_t archiveFileId,
-    log::LogContext &lc) = 0;
-
-  /**
-   * Returns true if the specified user running the CTA command-line tool on
-   * the specified host has administrator privileges.
-   *
-   * @param admin The administrator.
-   * @return True if the specified user running the CTA command-line tool on
-   * the specified host has administrator privileges.
-   */
-  virtual bool isAdmin(const common::dataStructures::SecurityIdentity &admin) const = 0;
-
-  /**
-   * Checks that the most trivial query goes through. Throws an exception if not.
-   */
-  virtual void ping() = 0;
-
-  /**
-   * Checks that the online database schema MAJOR version number matches the schema MAJOR version number defined in version.h
-   */
-  virtual void verifySchemaVersion() = 0;
-
-  /**
-   * Returns the SchemaVersion object corresponding to the catalogue schema version:
-   * - SCHEMA_VERSION_MAJOR
-   * - SCHEMA_VERSION_MINOR
-   * - SCHEMA_VERSION_MAJOR_NEXT (future major version number of the schema in case of upgrade)
-   * - SCHEMA_VERSION_MINOR_NEXT (future minor version number of the schema in case of upgrade)
-   * - STATUS (UPGRADING or PRODUCTION)
-   *
-   * @return The SchemaVersion object corresponding to the catalogue schema version
-   */
-  virtual SchemaVersion getSchemaVersion() const = 0;
-
-  /**
-   * Returns true if the specified tape pool exists.
-   *
-   * @param tapePoolName The name of the tape pool.
-   * @return True if the tape pool exists.
-   */
-  virtual bool tapePoolExists(const std::string &tapePoolName) const = 0;
-
-  /**
-   * Returns true if the specified tape exists.
-   *
-   * @param vid The volume identifier of the tape.
-   * @return True if the tape exists.
-   */
-  virtual bool tapeExists(const std::string &vid) const = 0;
-
-  /**
-   * Returns true if the specified disk system exists.
-   *
-   * @param name The name identifier of the disk system.
-   * @return True if the tape exists.
-   */
-  virtual bool diskSystemExists(const std::string &name) const = 0;
-
-  /**
-   * Updates the disk file ID of the specified archive file.
-   *
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param diskInstance The instance name of the source disk system.
-   * @param diskFileId The identifier of the source disk file which is unique
-   * within it's host disk system.  Two files from different disk systems may
-   * have the same identifier.  The combination of diskInstance and diskFileId
-   * must be globally unique, in other words unique within the CTA catalogue.
-   */
-  virtual void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
-    const std::string &diskFileId) = 0;
-
-  /**
-   * Insert the ArchiveFile and all its tape files in the FILE_RECYCLE_LOG table.
-   * There will be one entry on the FILE_RECYCLE_LOG table per deleted tape file
-   *
-   * @param request the DeleteRequest object that holds information about the file to delete.
-   * @param lc the logContext
-   */
-  virtual void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
-  log::LogContext & lc) = 0;
-
-   /**
-   *
-   * Deletes the specified archive file and its associated tape copies from the
-   * recycle-bin
-   *
-   * Please note that this method is idempotent.  If the file to be deleted does
-   * not exist in the CTA catalogue then this method returns without error.
-   *
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param lc The log context.
-   */
-  virtual void deleteFileFromRecycleBin(const uint64_t archiveFileId, log::LogContext &lc) = 0;
-
-  /**
-   * Deletes all the log entries corresponding to the vid passed in parameter.
-   *
-   * Please note that this method is idempotent.  If there are no recycle log
-   * entries associated to the vid passed in parameter, the method will return
-   * without any error.
-   *
-   * @param vid, the vid of the files to be deleted
-   * @param lc, the logContext
-   */
-  virtual void deleteFilesFromRecycleLog(const std::string & vid, log::LogContext & lc) = 0;
-
-  /**
-   * Creates the specified Tape Drive
-   * @param tapeDrive Parameters of the Tape Drive.
-   */
-  virtual void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) = 0;
-
-  /**
-   * Gets the names of all stored Tape Drive
-   * @return List of tape drive names
-   */
-  virtual std::list<std::string> getTapeDriveNames() const = 0;
-
-  /**
-   * Gets the information of all Tape Drives
-   * @return Parameters of all Tape Drives.
-   */
-  virtual std::list<common::dataStructures::TapeDrive> getTapeDrives() const = 0;
-
-  /**
-   * Gets the information of the specified Tape Drive
-   * @param tapeDriveName The name of the tape drive.
-   * @return Parameters of the Tape Drive.
-   */
-  virtual std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const = 0;
-
-  /**
-   * Modifies the desired state parameters off the specified Tape Drive
-   * @param tapeDriveName Name of the Tape Drive.
-   * @param desiredState Desired state parameters of the Tape Drive.
-   */
-  virtual void setDesiredTapeDriveState(const std::string& tapeDriveName,
-      const common::dataStructures::DesiredDriveState &desiredState) = 0;
-
-  virtual void setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
-    const std::string &comment) = 0;
-
-  virtual void updateTapeDriveStatistics(const std::string& tapeDriveName,
-    const std::string& host, const std::string& logicalLibrary,
-    const common::dataStructures::TapeDriveStatistics& statistics) = 0;
-
-  virtual void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) = 0;
-
-  /**
-   * Deletes the entry of a Tape Drive
-   * @param tapeDriveName The name of the tape drive.
-   */
-  virtual void deleteTapeDrive(const std::string &tapeDriveName) = 0;
-
-  /*
-   * Struct with a drive configuration
-   */
-  struct DriveConfig {
-    std::string tapeDriveName;
-    std::string category;
-    std::string keyName;
-    std::string value;
-    std::string source;
-  };
-
-  /**
-   * Creates a specified parameter of the configuration for a certain Tape Drive
-   * @param tapeDriveName The name of the tape drive.
-   * @param category The category of the parameter.
-   * @param keyName The key of the parameter.
-   * @param value The value of the parameter.
-   * @param source The source from which the parameter was gotten.
-   */
-  virtual void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
-    const std::string &keyName, const std::string &value, const std::string &source) = 0;
-
-  /**
-   * Gets all Drive Configurations of all TapeDrives.
-   * @return Drive Configurations of all TapeDrives.
-   */
-  virtual std::list<DriveConfig> getTapeDriveConfigs() const = 0;
-
-  /**
-   * Gets the Key and Names of configurations of all TapeDrives
-   * @return Keys and Names of configurations.
-   */
-  virtual std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const = 0;
-
-  /**
-   * Modifies a specified parameter of the configuration for a certain Tape Drive
-   * @param tapeDriveName The name of the tape drive.
-   * @param category The category of the parameter.
-   * @param keyName The key of the parameter.
-   * @param value The value of the parameter.
-   * @param source The source from which the parameter was gotten.
-   */
-  virtual void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
-    const std::string &keyName, const std::string &value, const std::string &source) = 0;
-
-  /**
-   * Gets a specified parameter of the configuration for a certain Tape Drive
-   * @param tapeDriveName The name of the tape drive.
-   * @param keyName The key of the parameter.
-   * @return Returns the category, value and source of a parameter of the configuarion
-   */
-  virtual std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig(const std::string &tapeDriveName,
-    const std::string &keyName) const = 0;
-
-  /**
-   * Deletes the entry of a Drive Configuration
-   * @param tapeDriveName The name of the tape drive.
-   */
-  virtual void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) = 0;
-
-  /**
-   * Gets the disk space reservations for all disk systems
-   */
-  virtual std::map<std::string, uint64_t> getDiskSpaceReservations() const = 0;
-
-  /**
-   * Adds to the current disk space reservation
-   */
-  virtual void reserveDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) = 0;
-
-  /**
-   * Subtracts from the current disk space reservation.
-   *
-   * If the amount released exceeds the current reservation, the reservation will be reduced to zero.
-   */
-  virtual void releaseDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) = 0;
-
-  /**
-  * Changes the name of hte storage class
-  * @param archiveFileId Id for file found in ARCHIVE_FILE
-  * @param newStorageClassName The name of the storage class
-  */
-  virtual void modifyArchiveFileStorageClassId(const uint64_t archiveFileId, const std::string& newStorageClassName) const = 0;
-
-  /**
-  * Changes the fxid in for a archive file
-  * @param archiveId The archive file id
-  * @param fxId The eos fxid related to the archive file
-  * @param diskInstance Disk instace
-  */
-  virtual void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId, const std::string &diskInstance) const = 0;
-
+  virtual ~Catalogue() = default;
+
+  // Table Methods
+  virtual const std::unique_ptr<SchemaCatalogue>& Schema() = 0;
+  virtual const std::unique_ptr<AdminUserCatalogue>& AdminUser() = 0;
+  virtual const std::unique_ptr<DiskSystemCatalogue>& DiskSystem() = 0;
+  virtual const std::unique_ptr<DiskInstanceCatalogue>& DiskInstance() = 0;
+  virtual const std::unique_ptr<DiskInstanceSpaceCatalogue>& DiskInstanceSpace() = 0;
+  virtual const std::unique_ptr<VirtualOrganizationCatalogue>& VO() = 0;
+  virtual const std::unique_ptr<ArchiveRouteCatalogue>& ArchiveRoute() = 0;
+  virtual const std::unique_ptr<MediaTypeCatalogue>& MediaType() = 0;
+  virtual const std::unique_ptr<StorageClassCatalogue>& StorageClass() = 0;
+  virtual const std::unique_ptr<TapePoolCatalogue>& TapePool() = 0;
+  virtual const std::unique_ptr<TapeCatalogue>& Tape() = 0;
+  virtual const std::unique_ptr<MountPolicyCatalogue>& MountPolicy() = 0;
+  virtual const std::unique_ptr<RequesterActivityMountRuleCatalogue>& RequesterActivityMountRule() = 0;
+  virtual const std::unique_ptr<RequesterMountRuleCatalogue>& RequesterMountRule() = 0;
+  virtual const std::unique_ptr<RequesterGroupMountRuleCatalogue>& RequesterGroupMountRule() = 0;
+  virtual const std::unique_ptr<LogicalLibraryCatalogue>& LogicalLibrary() = 0;
+  virtual const std::unique_ptr<TapeFileCatalogue>& TapeFile() = 0;
+  virtual const std::unique_ptr<FileRecycleLogCatalogue>& FileRecycleLog() = 0;
+  virtual const std::unique_ptr<DriveConfigCatalogue>& DriveConfig() = 0;
+  virtual const std::unique_ptr<DriveStateCatalogue>& DriveState() = 0;
+  virtual const std::unique_ptr<ArchiveFileCatalogue>& ArchiveFile() = 0;
 }; // class Catalogue
 
 } // namespace catalogue
diff --git a/catalogue/DbConfigVersionOfCatalogueTest.cpp b/catalogue/CatalogueExceptions.hpp
similarity index 75%
rename from catalogue/DbConfigVersionOfCatalogueTest.cpp
rename to catalogue/CatalogueExceptions.hpp
index 54eabde720..24b3c86f92 100644
--- a/catalogue/DbConfigVersionOfCatalogueTest.cpp
+++ b/catalogue/CatalogueExceptions.hpp
@@ -1,6 +1,6 @@
 /*
  * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
+ * @copyright    Copyright © 2022 CERN
  * @license      This program is free software, distributed under the terms of the GNU General Public
  *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
  *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
@@ -15,11 +15,13 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "catalogue/CatalogueTest.hpp"
-#include "tests/GlobalCatalogueFactoryForUnitTests.hpp"
+#pragma once
 
-namespace unitTests {
+#include <common/exception/Exception.hpp>
 
-INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_CatalogueTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
-
-} // namespace unitTests
+namespace cta {
+namespace exception {
+CTA_GENERATE_EXCEPTION_CLASS(NegativeDiskSpaceReservationReached);
+CTA_GENERATE_EXCEPTION_CLASS(CommentOrReasonWithMoreSizeThanMaximunAllowed);
+}
+}
diff --git a/catalogue/CatalogueTest.hpp b/catalogue/CatalogueTest.hpp
deleted file mode 100644
index 675b367cac..0000000000
--- a/catalogue/CatalogueTest.hpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-#include <gtest/gtest.h>
-
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-#include "catalogue/Catalogue.hpp"
-#include "catalogue/CatalogueFactory.hpp"
-#include "catalogue/CatalogueItor.hpp"
-#include "catalogue/CreateTapeAttributes.hpp"
-#include "catalogue/MediaTypeWithLogs.hpp"
-#include "catalogue/TapePool.hpp"
-#include "common/dataStructures/AdminUser.hpp"
-#include "common/dataStructures/ArchiveFile.hpp"
-#include "common/dataStructures/DiskInstance.hpp"
-#include "common/dataStructures/LogicalLibrary.hpp"
-#include "common/dataStructures/SecurityIdentity.hpp"
-#include "common/dataStructures/StorageClass.hpp"
-#include "common/dataStructures/VirtualOrganization.hpp"
-#include "common/log/DummyLogger.hpp"
-
-namespace unitTests {
-
-class cta_catalogue_CatalogueTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
-public:
-  cta_catalogue_CatalogueTest();
-
-protected:
-  cta::log::DummyLogger m_dummyLog;
-  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
-  const cta::common::dataStructures::SecurityIdentity m_localAdmin;
-  const cta::common::dataStructures::SecurityIdentity m_admin;
-  const cta::common::dataStructures::VirtualOrganization m_vo;
-  const cta::common::dataStructures::VirtualOrganization m_anotherVo;
-  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
-  const cta::common::dataStructures::StorageClass m_anotherStorageClass;
-  const cta::common::dataStructures::StorageClass m_storageClassDualCopy;
-  const cta::common::dataStructures::StorageClass m_storageClassTripleCopy;
-  const cta::common::dataStructures::DiskInstance m_diskInstance;
-  const cta::catalogue::MediaType m_mediaType;
-  const cta::catalogue::CreateTapeAttributes m_tape1;
-  const cta::catalogue::CreateTapeAttributes m_tape2;
-  const cta::catalogue::CreateTapeAttributes m_tape3;
-
-  virtual void SetUp();
-
-  virtual void TearDown();
-
-  /**
-   * Creates a map from logical library name to logical library given the
-   * specified list of logical libraries.
-   *
-   * @param listOfLibs The list of logical libraries from which the map shall
-   * be created.
-   * @return The map from logical library name to logical library.
-   */
-  std::map<std::string, cta::common::dataStructures::LogicalLibrary> logicalLibraryListToMap(
-    const std::list<cta::common::dataStructures::LogicalLibrary> &listOfLibs);
-
-  /**
-   * Creates a map from VID to tape given the specified list of tapes.
-   *
-   * @param listOfTapes The list of tapes from which the map shall be created.
-   * @return The map from VID to tape.
-   */
-  std::map<std::string, cta::common::dataStructures::Tape> tapeListToMap(
-    const std::list<cta::common::dataStructures::Tape> &listOfTapes);
-
-  /**
-   * Creates a map from archive file ID to archive file from the specified
-   * iterator.
-   *
-   * @param itor Iterator over archive files.
-   * @return Map from archive file ID to archive file.
-   */
-  std::map<uint64_t, cta::common::dataStructures::ArchiveFile> archiveFileItorToMap(
-    cta::catalogue::Catalogue::ArchiveFileItor &itor);
-
-  /**
-   * Creates a map from archive file ID to archive file from the specified
-   * list of archive files.
-   *
-   * @param listOfArchiveFiles The list of archive files from which the map
-   * shall be created.
-   * @return Map from archive file ID to archive file.
-   */
-  std::map<uint64_t, cta::common::dataStructures::ArchiveFile> archiveFileListToMap(
-    const std::list<cta::common::dataStructures::ArchiveFile> &listOfArchiveFiles);
-
-  /**
-   * Creates a map from admin username to admin user from the specified list of
-   * admin users.
-   *
-   * @param listOfAdminUsers The list of admin users.
-   * @return Map from username to admin user.
-   */
-  std::map<std::string, cta::common::dataStructures::AdminUser> adminUserListToMap(
-    const std::list<cta::common::dataStructures::AdminUser> &listOfAdminUsers);
-
-  /**
-   * Creates a map from tape meida type name to tape media type from the
-   * specified list of tape media types.
-   *
-   * @param listOfMediaTypes The list of tape media types.
-   * @return Map from tape media type name to tape media type.
-   */
-  std::map<std::string, cta::catalogue::MediaTypeWithLogs> mediaTypeWithLogsListToMap(
-    const std::list<cta::catalogue::MediaTypeWithLogs> &listOfMediaTypes);
-
-  /**
-   * Creates a map from tape pool name to tape pool from the specified list of
-   * tape pools.
-   *
-   * @param listOfTapePools The list of tape pools.
-   * @return Map from tape pool name to tape pool.
-   */
-  std::map<std::string, cta::catalogue::TapePool> tapePoolListToMap(
-    const std::list<cta::catalogue::TapePool> &listOfTapePools);
-
-};  // cta_catalogue_CatalogueTest
-
-}  // namespace unitTests
diff --git a/catalogue/CatalogueUtils.cpp b/catalogue/CatalogueUtils.cpp
new file mode 100644
index 0000000000..d9a6ee2358
--- /dev/null
+++ b/catalogue/CatalogueUtils.cpp
@@ -0,0 +1,43 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "catalogue/CatalogueExceptions.hpp"
+#include "catalogue/CatalogueUtils.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void CatalogueUtils::checkCommentOrReasonMaxLength(const std::optional<std::string>& str, log::Logger &log) {
+  const size_t MAX_CHAR_COMMENT = 1000;
+  if (!str.has_value()) return;
+  if (str.value().length() > MAX_CHAR_COMMENT) {
+    log::LogContext lc(log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("Large_Message: ", str.value());
+    lc.log(log::ERR, "The reason or comment has more characters than the maximun allowed.");
+    throw exception::CommentOrReasonWithMoreSizeThanMaximunAllowed(
+      "The comment or reason string value has more than 1000 characters");
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/Catalogue.cpp b/catalogue/CatalogueUtils.hpp
similarity index 74%
rename from catalogue/Catalogue.cpp
rename to catalogue/CatalogueUtils.hpp
index f660aa83de..665733cc72 100644
--- a/catalogue/Catalogue.cpp
+++ b/catalogue/CatalogueUtils.hpp
@@ -1,6 +1,6 @@
 /*
  * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
+ * @copyright    Copyright © 2022 CERN
  * @license      This program is free software, distributed under the terms of the GNU General Public
  *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
  *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
@@ -15,16 +15,24 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "catalogue/Catalogue.hpp"
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <string>
 
 namespace cta {
-namespace catalogue {
 
-//------------------------------------------------------------------------------
-// destructor
-//------------------------------------------------------------------------------
-Catalogue::~Catalogue() {
+namespace log {
+class Logger;
 }
 
-} // namespace catalogue
-} // namespace cta
+namespace catalogue {
+
+class CatalogueUtils {
+public:
+static void checkCommentOrReasonMaxLength(const std::optional<std::string>& comment, log::Logger &log);
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/CreateAdminUserCmd.cpp b/catalogue/CreateAdminUserCmd.cpp
index fa813c4f19..91da64ed2b 100644
--- a/catalogue/CreateAdminUserCmd.cpp
+++ b/catalogue/CreateAdminUserCmd.cpp
@@ -62,7 +62,7 @@ int CreateAdminUserCmd::exceptionThrowingMain(const int argc, char *const *const
   auto catalogue = catalogueFactory->create();
   const common::dataStructures::SecurityIdentity adminRunningCommand(getUsername(), getHostname());
 
-  catalogue->createAdminUser(adminRunningCommand, cmdLineArgs.adminUsername, cmdLineArgs.comment);
+  catalogue->AdminUser()->createAdminUser(adminRunningCommand, cmdLineArgs.adminUsername, cmdLineArgs.comment);
   return 0;
 }
 
diff --git a/catalogue/DriveConfig.cpp b/catalogue/DriveConfig.cpp
index 8ce0e03293..3437162fd6 100644
--- a/catalogue/DriveConfig.cpp
+++ b/catalogue/DriveConfig.cpp
@@ -62,20 +62,20 @@ void DriveConfig::setTapedConfiguration(const cta::tape::daemon::TapedConfigurat
 
 void DriveConfig::checkConfigInDB(catalogue::Catalogue* catalogue, const std::string& tapeDriveName,
   const std::string& key) {
-  auto namesAndKeys = catalogue->getTapeDriveConfigNamesAndKeys();
+  auto namesAndKeys = catalogue->DriveConfig()->getTapeDriveConfigNamesAndKeys();
   auto it = std::find_if(namesAndKeys.begin(), namesAndKeys.end(),
     [&tapeDriveName, &key](const std::pair<std::string, std::string>& element) {
       return element.first == tapeDriveName && element.second == key;
     });
   if (it != namesAndKeys.end()) {
-    catalogue->deleteTapeDriveConfig(tapeDriveName, key);
+    catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, key);
   }
 }
 
 void DriveConfig::setConfigToDB(cta::SourcedParameter<std::string>* sourcedParameter,
   catalogue::Catalogue* catalogue, const std::string& tapeDriveName) {
   checkConfigInDB(catalogue, tapeDriveName, sourcedParameter->key());
-  catalogue->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
+  catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
     sourcedParameter->value(), sourcedParameter->source());
 }
 
@@ -85,32 +85,32 @@ void DriveConfig::setConfigToDB(cta::SourcedParameter<cta::tape::daemon::FetchRe
   cta::utils::searchAndReplace(key, "Bytes", "");
   cta::utils::searchAndReplace(key, "Files", "");
   checkConfigInDB(catalogue, tapeDriveName, key.append("Files"));
-  catalogue->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), key,
+  catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), key,
     std::to_string(sourcedParameter->value().maxFiles), sourcedParameter->source());
   cta::utils::searchAndReplace(key, "Files", "");
   checkConfigInDB(catalogue, tapeDriveName, key.append("Bytes"));
-  catalogue->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), key,
+  catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), key,
     std::to_string(sourcedParameter->value().maxBytes), sourcedParameter->source());
 }
 
 void DriveConfig::setConfigToDB(cta::SourcedParameter<uint32_t>* sourcedParameter,
   catalogue::Catalogue* catalogue, const std::string& tapeDriveName) {
   checkConfigInDB(catalogue, tapeDriveName, sourcedParameter->key());
-  catalogue->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
+  catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
     std::to_string(sourcedParameter->value()), sourcedParameter->source());
 }
 
 void DriveConfig::setConfigToDB(cta::SourcedParameter<uint64_t>* sourcedParameter,
   catalogue::Catalogue* catalogue, const std::string& tapeDriveName) {
   checkConfigInDB(catalogue, tapeDriveName, sourcedParameter->key());
-  catalogue->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
+  catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
     std::to_string(sourcedParameter->value()), sourcedParameter->source());
 }
 
 void DriveConfig::setConfigToDB(cta::SourcedParameter<time_t>* sourcedParameter,
   catalogue::Catalogue* catalogue, const std::string& tapeDriveName) {
   checkConfigInDB(catalogue, tapeDriveName, sourcedParameter->key());
-  catalogue->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
+  catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, sourcedParameter->category(), sourcedParameter->key(),
     std::to_string(sourcedParameter->value()), sourcedParameter->source());
 }
 
diff --git a/catalogue/DriveConfig.hpp b/catalogue/DriveConfig.hpp
index e98c686e4e..d1966e98e0 100644
--- a/catalogue/DriveConfig.hpp
+++ b/catalogue/DriveConfig.hpp
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <ctime>
 #include <memory>
 #include <string>
 
diff --git a/catalogue/DummyCatalogue.cpp b/catalogue/DummyCatalogue.cpp
deleted file mode 100644
index 1ff45728da..0000000000
--- a/catalogue/DummyCatalogue.cpp
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#include <list>
-#include <map>
-
-#include "catalogue/CatalogueItor.hpp"
-#include "catalogue/DummyCatalogue.hpp"
-#include "catalogue/MediaType.hpp"
-#include "catalogue/SchemaVersion.hpp"
-#include "catalogue/TapePool.hpp"
-#include "common/dataStructures/ArchiveFile.hpp"
-#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
-#include "common/dataStructures/ArchiveFileSummary.hpp"
-#include "common/dataStructures/DesiredDriveState.hpp"
-#include "common/dataStructures/DiskInstance.hpp"
-#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
-#include "common/dataStructures/StorageClass.hpp"
-#include "common/dataStructures/TapeDrive.hpp"
-#include "common/dataStructures/TapeDriveStatistics.hpp"
-#include "common/threading/MutexLocker.hpp"
-
-namespace cta {
-namespace catalogue {
-
-void DummyCatalogue::createAdminUser(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createArchiveRoute(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& tapePoolName, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createLogicalLibrary(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const bool isDisabled, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createMountPolicy(const common::dataStructures::SecurityIdentity& admin, const CreateMountPolicyAttributes & mountPolicy) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createRequesterGroupMountRule(const common::dataStructures::SecurityIdentity& admin, const std::string& mountPolicyName, const std::string& diskInstanceName, const std::string& requesterGroupName, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createRequesterMountRule(const common::dataStructures::SecurityIdentity& admin, const std::string& mountPolicyName, const std::string& diskInstance, const std::string& requesterName, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createRequesterActivityMountRule(const common::dataStructures::SecurityIdentity& admin, const std::string& mountPolicyName, const std::string& diskInstance, const std::string& requesterName, const std::string &activityRegex, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createStorageClass(const common::dataStructures::SecurityIdentity& admin, const common::dataStructures::StorageClass& storageClass) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createTape(const common::dataStructures::SecurityIdentity &admin, const CreateTapeAttributes & tape) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteMediaType(const std::string &name) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<MediaTypeWithLogs> DummyCatalogue::getMediaTypes() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-MediaType DummyCatalogue::getMediaTypeByVid(const std::string & vid) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &cartridge) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t capacityInBytes) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t primaryDensityCode) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t secondaryDensityCode) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint32_t> &nbWraps) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &minLPos) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &maxLPos) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createTapePool(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string & vo, const uint64_t nbPartialTapes, const bool encryptionValue, const std::optional<std::string> &supply, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteAdminUser(const std::string& username) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string& instanceName, const uint64_t archiveFileId, log::LogContext &lc) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteArchiveRoute(const std::string& storageClassName, const uint32_t copyNb) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteLogicalLibrary(const std::string& name) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteMountPolicy(const std::string& name) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteRequesterGroupMountRule(const std::string& diskInstanceName, const std::string& requesterGroupName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteRequesterMountRule(const std::string& diskInstanceName, const std::string& requesterName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteRequesterActivityMountRule(const std::string& diskInstanceName, const std::string& requesterName, const std::string &activityRegex) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteStorageClass(const std::string& storageClassName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteTape(const std::string& vid) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteTapePool(const std::string& name) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer>& event) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteDiskSystem(const std::string &name) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &fileRegexp) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t targetedFreeSpace) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t sleepTime) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceSpaceName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<common::dataStructures::DiskInstance> DummyCatalogue::getAllDiskInstances() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::deleteDiskInstance(const std::string &name) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<common::dataStructures::DiskInstanceSpace> DummyCatalogue::getAllDiskInstanceSpaces() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const std::string &comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<common::dataStructures::AdminUser> DummyCatalogue::getAdminUsers() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::ArchiveFile DummyCatalogue::getArchiveFileById(const uint64_t id) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-using ArchiveFileItor = CatalogueItor<common::dataStructures::ArchiveFile>;
-ArchiveFileItor DummyCatalogue::getArchiveFilesItor(const TapeFileSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-using FileRecycleLogItor = CatalogueItor<common::dataStructures::FileRecycleLog>;
-FileRecycleLogItor DummyCatalogue::getFileRecycleLogItor(const RecycleTapeFileSearchCriteria & searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria, const std::string &newFid) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteFileFromRecycleBin(const uint64_t archiveFileId, log::LogContext &lc) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::deleteFilesFromRecycleLog(const std::string & vid, log::LogContext & lc) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::deleteTapeDrive(const std::string &tapeDriveName) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category, const std::string &keyName, const std::string &value, const std::string &source) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<cta::catalogue::Catalogue::DriveConfig> DummyCatalogue::getTapeDriveConfigs() const {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<std::pair<std::string, std::string>> DummyCatalogue::getTapeDriveConfigNamesAndKeys() const {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category, const std::string &keyName, const std::string &value, const std::string &source) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::optional<std::tuple<std::string, std::string, std::string>> DummyCatalogue::getTapeDriveConfig( const std::string &tapeDriveName, const std::string &keyName) const {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<common::dataStructures::ArchiveFile> DummyCatalogue::getFilesForRepack(const std::string &vid, const uint64_t startFSeq, const uint64_t maxNbFiles) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-ArchiveFileItor DummyCatalogue::getArchiveFilesForRepackItor(const std::string &vid, const uint64_t startFSeq) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::ArchiveRoute> DummyCatalogue::getArchiveRoutes() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::ArchiveRoute> DummyCatalogue::getArchiveRoutes(const std::string &storageClassName, const std::string &tapePoolName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::LogicalLibrary> DummyCatalogue::getLogicalLibraries() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::RequesterGroupMountRule> DummyCatalogue::getRequesterGroupMountRules() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::RequesterMountRule> DummyCatalogue::getRequesterMountRules() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::RequesterActivityMountRule> DummyCatalogue::getRequesterActivityMountRules() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::StorageClass> DummyCatalogue::getStorageClasses() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::StorageClass DummyCatalogue::getStorageClass(const std::string &name) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::ArchiveFileSummary DummyCatalogue::getTapeFileSummary(const TapeFileSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::ArchiveFile DummyCatalogue::getArchiveFileForDeletion(const TapeFileSearchCriteria &searchCriteria) const {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-std::list<TapePool> DummyCatalogue::getTapePools(const TapePoolSearchCriteria &searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::optional<TapePool> DummyCatalogue::getTapePool(const std::string &tapePoolName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::optional<common::dataStructures::MountPolicy> DummyCatalogue::getMountPolicy(const std::string &mountPolicyName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::Tape> DummyCatalogue::getTapes(const TapeSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::map<std::string, std::string> DummyCatalogue::getVidToLogicalLibrary(const std::set<std::string> &vids) const { throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented"); }
-common::dataStructures::Label::Format DummyCatalogue::getTapeLabelFormat(const std::string& vid) const { throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented"); }
-std::list<TapeForWriting> DummyCatalogue::getTapesForWriting(const std::string& logicalLibraryName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-bool DummyCatalogue::isAdmin(const common::dataStructures::SecurityIdentity& admin) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::deleteVirtualOrganization(const std::string &voName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-std::list<common::dataStructures::VirtualOrganization> DummyCatalogue::getVirtualOrganizations() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::VirtualOrganization DummyCatalogue::getVirtualOrganizationOfTapepool(const std::string & tapepoolName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::VirtualOrganization DummyCatalogue::getCachedVirtualOrganizationOfTapepool(const std::string & tapepoolName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyVirtualOrganizationName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName, const std::string &newVoName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& tapePoolName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& disabledReason) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool disabledValue) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t minArchiveRequestAge) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t archivePriority) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMountPolicyComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t minRetrieveRequestAge) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t retrievePriority) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string &activityRegex, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterGroupName, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterGroupName, const std::string& mountPolicy) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string& mountPolicy) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string &activityRegex, const std::string& mountPolicy) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyStorageClassName(const common::dataStructures::SecurityIdentity& admin, const std::string& currentName, const std::string& newName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t nbCopies) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeComment(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::optional<std::string> &comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& encryptionKeyName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& verificationStatus) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeMediaType(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& mediaType) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeVendor(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& vendor) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& logicalLibraryName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapePoolComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t nbPartialTapes) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapePoolSupply(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& supply) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& currentName, const std::string& newName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& tapePoolName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::noSpaceLeftOnTape(const std::string& vid) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::ping() { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::verifySchemaVersion() { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-SchemaVersion DummyCatalogue::getSchemaVersion() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-uint64_t DummyCatalogue::checkAndGetNextArchiveFileId(const std::string &diskInstanceName, const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::ArchiveFileQueueCriteria DummyCatalogue::getArchiveFileQueueCriteria(const std::string &diskInstanceName,
-  const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-common::dataStructures::RetrieveFileQueueCriteria DummyCatalogue::prepareToRetrieveFile(const std::string& diskInstanceName, const uint64_t archiveFileId, const common::dataStructures::RequesterIdentity& user, const std::optional<std::string>& activity, log::LogContext& lc, const std::optional<std::string> &mountPolicyName) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::reclaimTape(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, cta::log::LogContext & lc) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::checkTapeForLabel(const std::string& vid) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-uint64_t DummyCatalogue::getNbFilesOnTape(const std::string& vid) const  { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapeDisabled(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string & reason) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string & reason) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapeFull(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const bool fullValue) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapeDirty(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const bool dirtyValue) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapeDirty(const std::string & vid) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapeIsFromCastorInUnitTests(const std::string &vid) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::setTapePoolEncryption(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const bool encryptionValue) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-bool DummyCatalogue::diskSystemExists(const std::string& name) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::tapeLabelled(const std::string& vid, const std::string& drive) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::tapeMountedForArchive(const std::string& vid, const std::string& drive) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::tapeMountedForRetrieve(const std::string& vid, const std::string& drive) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-bool DummyCatalogue::tapePoolExists(const std::string& tapePoolName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance, const std::string &diskFileId) { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
-  log::LogContext & lc) {throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");}
-void DummyCatalogue::modifyArchiveFileStorageClassId(const uint64_t archiveFileId, const std::string &newStorageClassName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-void DummyCatalogue::modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId, const std::string &diskInstance) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-
-
-// Special functions for unit tests.
-void DummyCatalogue::addEnabledTape(const std::string & vid) {
-  threading::MutexLocker lm(m_tapeEnablingMutex);
-  m_tapeEnabling[vid]=common::dataStructures::Tape::ACTIVE;
-}
-void DummyCatalogue::addDisabledTape(const std::string & vid) {
-  threading::MutexLocker lm(m_tapeEnablingMutex);
-  m_tapeEnabling[vid]=common::dataStructures::Tape::DISABLED;
-}
-void DummyCatalogue::addRepackingTape(const std::string & vid) {
-  threading::MutexLocker lm(m_tapeEnablingMutex);
-  m_tapeEnabling[vid]=common::dataStructures::Tape::REPACKING;
-}
-void DummyCatalogue::modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const std::optional<common::dataStructures::Tape::State> & prev_state, const std::optional<std::string> & stateReason) {
-  threading::MutexLocker lm(m_tapeEnablingMutex);
-  if (prev_state.has_value() && prev_state.value() != m_tapeEnabling[vid]) {
-    throw exception::Exception("Previous state mismatch");
-  }
-  m_tapeEnabling[vid]=state;
-}
-bool DummyCatalogue::tapeExists(const std::string& vid) const {
-  return m_tapeEnabling.find(vid) != m_tapeEnabling.end();
-}
-common::dataStructures::Tape::State DummyCatalogue::getTapeState(const std::string & vid) const {
-  return m_tapeEnabling.at(vid);
-}
-common::dataStructures::VidToTapeMap DummyCatalogue::getTapesByVid(const std::string& vid) const {
-  std::set<std::string> vids = {vid};
-  return getTapesByVid(vids);
-}
-common::dataStructures::VidToTapeMap DummyCatalogue::getTapesByVid(const std::set<std::string>& vids) const {
-  // Minimal implementation of VidToMap for retrieve request unit tests. We just support
-  // disabled status for the tapes.
-  // If the tape is not listed, it is listed as enabled in the return value.
-  threading::MutexLocker lm(m_tapeEnablingMutex);
-  common::dataStructures::VidToTapeMap ret;
-  for (const auto & v: vids) {
-    try {
-      ret[v].state = m_tapeEnabling.at(v);
-    } catch (std::out_of_range &) {
-      ret[v].state = common::dataStructures::Tape::ACTIVE;
-    }
-  }
-  return ret;
-}
-std::list<common::dataStructures::MountPolicy> DummyCatalogue::getMountPolicies() const {
-  std::list<common::dataStructures::MountPolicy> mountPolicies;
-  common::dataStructures::MountPolicy mp1;
-  mp1.name = "mountPolicy";
-  mp1.archivePriority = 1;
-  mp1.archiveMinRequestAge = 0;
-  mp1.retrievePriority = 1;
-  mp1.retrieveMinRequestAge = 0;
-  mountPolicies.push_back(mp1);
-
-  common::dataStructures::MountPolicy mp2;
-  mp2.name = "moreAdvantageous";
-  mp2.archivePriority = 2;
-  mp2.archiveMinRequestAge = 0;
-  mp2.retrievePriority = 2;
-  mp2.retrieveMinRequestAge = 0;
-  mountPolicies.push_back(mp1);
-  return mountPolicies;
-}
-
-std::list<common::dataStructures::MountPolicy> DummyCatalogue::getCachedMountPolicies() const {
-  std::list<common::dataStructures::MountPolicy> mountPolicies;
-  common::dataStructures::MountPolicy mp1;
-  mp1.name = "mountPolicy";
-  mp1.archivePriority = 1;
-  mp1.archiveMinRequestAge = 0;
-  mp1.retrievePriority = 1;
-  mp1.retrieveMinRequestAge = 0;
-  mountPolicies.push_back(mp1);
-
-  common::dataStructures::MountPolicy mp2;
-  mp2.name = "moreAdvantageous";
-  mp2.archivePriority = 2;
-  mp2.archiveMinRequestAge = 0;
-  mp2.retrievePriority = 2;
-  mp2.retrieveMinRequestAge = 0;
-  mountPolicies.push_back(mp1);
-  return mountPolicies;
-}
-
-std::list<std::string> DummyCatalogue::getTapeDriveNames() const {
-  return {m_tapeDriveStatus.driveName};
-}
-
-std::optional<common::dataStructures::TapeDrive> DummyCatalogue::getTapeDrive(const std::string &tapeDriveName) const {
-  if (m_tapeDriveStatus.driveName != "") return m_tapeDriveStatus;
-  common::dataStructures::TapeDrive tapeDriveStatus;
-  const time_t reportTime = time(nullptr);
-
-  tapeDriveStatus.driveName = tapeDriveName;
-  tapeDriveStatus.host = "Dummy_Host";
-  tapeDriveStatus.logicalLibrary = "Dummy_Library";
-
-  tapeDriveStatus.downOrUpStartTime = reportTime;
-
-  tapeDriveStatus.mountType = common::dataStructures::MountType::NoMount;
-  tapeDriveStatus.driveStatus = common::dataStructures::DriveStatus::Down;
-  tapeDriveStatus.desiredUp = false;
-  tapeDriveStatus.desiredForceDown = false;
-
-  tapeDriveStatus.diskSystemName = "Dummy_System";
-  tapeDriveStatus.reservedBytes = 0;
-  tapeDriveStatus.reservationSessionId = 0;
-
-
-  return tapeDriveStatus;
-}
-
-std::list<common::dataStructures::TapeDrive> DummyCatalogue::getTapeDrives() const {
-  std::list<common::dataStructures::TapeDrive> tapeDrives;
-  const auto tapeDrive = getTapeDrive(m_tapeDriveStatus.driveName);
-  if (tapeDrive.has_value()) tapeDrives.push_back(tapeDrive.value());
-  return tapeDrives;
-}
-
-void DummyCatalogue::setDesiredTapeDriveState(const std::string&,
-    const common::dataStructures::DesiredDriveState &desiredState) {
-  m_tapeDriveStatus.desiredUp = desiredState.up;
-  m_tapeDriveStatus.desiredForceDown = desiredState.forceDown;
-  m_tapeDriveStatus.reasonUpDown = desiredState.reason;
-}
-
-void DummyCatalogue::setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
-  const std::string &comment) {
-  m_tapeDriveStatus.userComment = comment;
-}
-
-void DummyCatalogue::updateTapeDriveStatistics(const std::string& tapeDriveName,
-  const std::string& host, const std::string& logicalLibrary,
-  const common::dataStructures::TapeDriveStatistics& statistics) {
-  m_tapeDriveStatus.driveName = tapeDriveName;
-  m_tapeDriveStatus.host = host;
-  m_tapeDriveStatus.logicalLibrary = logicalLibrary;
-  m_tapeDriveStatus.bytesTransferedInSession = statistics.bytesTransferedInSession;
-  m_tapeDriveStatus.filesTransferedInSession = statistics.filesTransferedInSession;
-  m_tapeDriveStatus.lastModificationLog = statistics.lastModificationLog;
-}
-
-void DummyCatalogue::updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) {
-  m_tapeDriveStatus = tapeDrive;
-}
-
-std::map<std::string, uint64_t> DummyCatalogue::getDiskSpaceReservations() const {
-  std::map<std::string, uint64_t> ret;
-  const auto tdNames = getTapeDriveNames();
-  for (const auto& driveName : tdNames) {
-    const auto tdStatus = getTapeDrive(driveName);
-    if (tdStatus.value().diskSystemName) {
-      // no need to check key, operator[] initializes missing values at zero for scalar types
-      ret[tdStatus.value().diskSystemName.value()] += tdStatus.value().reservedBytes.value();
-    }
-  }
-  return ret;
-}
-
-void DummyCatalogue::reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
-  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
-  if (diskSpaceReservation.empty()) return;
-
-  log::ScopedParamContainer params(lc);
-  params.add("driveName", driveName)
-        .add("diskSystem", diskSpaceReservation.begin()->first)
-        .add("reservationBytes", diskSpaceReservation.begin()->second)
-        .add("mountId", mountId);
-  lc.log(log::DEBUG, "In RetrieveMount::reserveDiskSpace(): reservation request.");
-
-  auto tdStatus = getTapeDrive(driveName);
-  if (!tdStatus) return;
-
-  if (!tdStatus.value().reservationSessionId) {
-    tdStatus.value().reservationSessionId = mountId;
-    tdStatus.value().reservedBytes = 0;
-  }
-
-  if (tdStatus.value().reservationSessionId != mountId) {
-    tdStatus.value().reservationSessionId = mountId;
-    tdStatus.value().reservedBytes = 0;
-  }
-
-  tdStatus.value().diskSystemName = diskSpaceReservation.begin()->first;
-  tdStatus.value().reservedBytes.value() += diskSpaceReservation.begin()->second;
-  updateTapeDriveStatus(tdStatus.value());
-}
-
-void DummyCatalogue::releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
-  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
-  if (diskSpaceReservation.empty()) return;
-
-  log::ScopedParamContainer params(lc);
-  params.add("driveName", driveName)
-        .add("diskSystem", diskSpaceReservation.begin()->first)
-        .add("reservationBytes", diskSpaceReservation.begin()->second)
-        .add("mountId", mountId);
-  lc.log(log::DEBUG, "In RetrieveMount::releaseDiskSpace(): reservation release request.");
-
-  auto tdStatus = getTapeDrive(driveName);
-
-  if (!tdStatus) return;
-  if (!tdStatus.value().reservationSessionId) {
-    return;
-  }
-  if (tdStatus.value().reservationSessionId != mountId) {
-    return;
-  }
-  auto& bytes = diskSpaceReservation.begin()->second;
-  if (bytes > tdStatus.value().reservedBytes) throw NegativeDiskSpaceReservationReached(
-    "In DriveState::subtractDiskSpaceReservation(): we would reach a negative reservation size.");
-  tdStatus.value().diskSystemName = diskSpaceReservation.begin()->first;
-  tdStatus.value().reservedBytes.value() -= bytes;
-  updateTapeDriveStatus(tdStatus.value());
-}
-
-
-/*
-  * Implemented for testing disk space reservation logic
-  */
-disk::DiskSystemList DummyCatalogue::getAllDiskSystems() const {
-  return m_diskSystemList;
-}
-
-void DummyCatalogue::createDiskInstance(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) {
-  m_diskInstances[name] = {name, comment, common::dataStructures::EntryLog(), common::dataStructures::EntryLog()};
-}
-
-void DummyCatalogue::createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL, const uint64_t refreshInterval, const std::string &comment) {
-  m_diskInstanceSpaces[name] = {name, diskInstance, freeSpaceQueryURL, refreshInterval, 0, 0, comment, common::dataStructures::EntryLog(), common::dataStructures::EntryLog()};
-}
-
-void DummyCatalogue::createDiskSystem(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceName, const std::string &diskInstanceSpaceName, const std::string &fileRegexp, const uint64_t targetedFreeSpace, const time_t sleepTime, const std::string &comment)  {
-  m_diskSystemList.push_back({name, m_diskInstanceSpaces.at(diskInstanceSpaceName), fileRegexp, targetedFreeSpace, sleepTime, common::dataStructures::EntryLog(), common::dataStructures::EntryLog{}, comment});
-}
-
-void DummyCatalogue::modifyDiskInstanceSpaceFreeSpace(const std::string &name, const std::string &diskInstance, const uint64_t freeSpace) {
-  m_diskInstanceSpaces[name].freeSpace = freeSpace;
-  m_diskInstanceSpaces[name].lastRefreshTime = time(nullptr);
-}
-
-}  // namespace catalogue
-}  // namespace cta
diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp
deleted file mode 100644
index 6e98eeb6ad..0000000000
--- a/catalogue/DummyCatalogue.hpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-#include <list>
-#include <map>
-#include <string>
-
-#include "catalogue/Catalogue.hpp"
-#include "common/dataStructures/ArchiveFileSummary.hpp"
-#include "common/dataStructures/DiskInstance.hpp"
-#include "common/dataStructures/TapeDrive.hpp"
-#include "common/threading/MutexLocker.hpp"
-#include "disk/DiskSystem.hpp"
-
-namespace cta {
-namespace catalogue {
-
-/**
- * An empty implementation of the Catalogue used to populate unit tests of the scheduler database
- * as they need a reference to a Catalogue, used in very few situations (requeueing of retrieve
- * requests).
- */
-class DummyCatalogue: public Catalogue {
-public:
-  DummyCatalogue() = default;
-  ~DummyCatalogue() override = default;
-
-  void createAdminUser(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) override;
-  void createArchiveRoute(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& tapePoolName, const std::string& comment) override;
-  void createLogicalLibrary(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const bool isDisabled, const std::string& comment) override;
-  void createMountPolicy(const common::dataStructures::SecurityIdentity& admin, const CreateMountPolicyAttributes & mountPolicy) override;
-  void createRequesterGroupMountRule(const common::dataStructures::SecurityIdentity& admin, const std::string& mountPolicyName, const std::string& diskInstanceName, const std::string& requesterGroupName, const std::string& comment) override;
-  void createRequesterMountRule(const common::dataStructures::SecurityIdentity& admin, const std::string& mountPolicyName, const std::string& diskInstance, const std::string& requesterName, const std::string& comment) override;
-  void createRequesterActivityMountRule(const common::dataStructures::SecurityIdentity& admin, const std::string& mountPolicyName, const std::string& diskInstance, const std::string& requesterName, const std::string &activityRegex, const std::string& comment) override;
-  void createStorageClass(const common::dataStructures::SecurityIdentity& admin, const common::dataStructures::StorageClass& storageClass) override;
-  void createTape(const common::dataStructures::SecurityIdentity &admin, const CreateTapeAttributes & tape) override;
-  void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) override;
-  void deleteMediaType(const std::string &name) override;
-  std::list<MediaTypeWithLogs> getMediaTypes() const override;
-  MediaType getMediaTypeByVid(const std::string & vid) const override;
-  void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override;
-  void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &cartridge) override;
-  void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t capacityInBytes) override;
-  void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t primaryDensityCode) override;
-  void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t secondaryDensityCode) override;
-  void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint32_t> &nbWraps) override;
-  void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &minLPos) override;
-  void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &maxLPos) override;
-  void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-  void createTapePool(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string & vo, const uint64_t nbPartialTapes, const bool encryptionValue, const std::optional<std::string> &supply, const std::string& comment) override;
-  void deleteAdminUser(const std::string& username) override;
-  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string& instanceName, const uint64_t archiveFileId, log::LogContext &lc) override;
-  void deleteArchiveRoute(const std::string& storageClassName, const uint32_t copyNb) override;
-  void deleteLogicalLibrary(const std::string& name) override;
-  void deleteMountPolicy(const std::string& name) override;
-  void deleteRequesterGroupMountRule(const std::string& diskInstanceName, const std::string& requesterGroupName) override;
-  void deleteRequesterMountRule(const std::string& diskInstanceName, const std::string& requesterName) override;
-  void deleteRequesterActivityMountRule(const std::string& diskInstanceName, const std::string& requesterName, const std::string &activityRegex) override;
-  void deleteStorageClass(const std::string& storageClassName) override;
-  void deleteTape(const std::string& vid) override;
-  void deleteTapePool(const std::string& name) override;
-  void filesWrittenToTape(const std::set<TapeItemWrittenPointer>& event) override;
-  void deleteDiskSystem(const std::string &name) override;
-  void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &fileRegexp) override;
-  void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t targetedFreeSpace) override;
-  void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t sleepTime) override;
-  void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-  void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceName) override;
-  void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceSpaceName) override;
-  std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const override;
-  void deleteDiskInstance(const std::string &name) override;
-  void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-  std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const override;
-  void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) override;
-  void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const std::string &comment) override;
-  void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) override;
-  void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) override;
-  std::list<common::dataStructures::AdminUser> getAdminUsers() const override;
-  common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const override;
-  ArchiveFileItor getArchiveFilesItor(const TapeFileSearchCriteria& searchCriteria) const;
-  FileRecycleLogItor getFileRecycleLogItor(const RecycleTapeFileSearchCriteria & searchCriteria) const;
-  void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria, const std::string &newFid);
-  void deleteFileFromRecycleBin(const uint64_t archiveFileId, log::LogContext &lc);
-  void deleteFilesFromRecycleLog(const std::string & vid, log::LogContext & lc);
-  void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive);
-  void deleteTapeDrive(const std::string &tapeDriveName);
-  void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category, const std::string &keyName, const std::string &value, const std::string &source);
-  std::list<cta::catalogue::Catalogue::DriveConfig> getTapeDriveConfigs() const;
-  std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const;
-  void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category, const std::string &keyName, const std::string &value, const std::string &source);
-  std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig( const std::string &tapeDriveName, const std::string &keyName) const;
-  void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName);
-  std::list<common::dataStructures::ArchiveFile> getFilesForRepack(const std::string &vid, const uint64_t startFSeq, const uint64_t maxNbFiles) const override;
-  ArchiveFileItor getArchiveFilesForRepackItor(const std::string &vid, const uint64_t startFSeq) const override;
-  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const;
-  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(const std::string &storageClassName, const std::string &tapePoolName) const override;
-  std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const;
-  std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const;
-  std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const;
-  std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const;
-  std::list<common::dataStructures::StorageClass> getStorageClasses() const;
-  common::dataStructures::StorageClass getStorageClass(const std::string &name) const;
-  common::dataStructures::ArchiveFileSummary getTapeFileSummary(const TapeFileSearchCriteria& searchCriteria) const;
-  common::dataStructures::ArchiveFile getArchiveFileForDeletion(const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
-  void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) override;
-  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const;
-  std::optional<TapePool> getTapePool(const std::string &tapePoolName) const override;
-  std::optional<common::dataStructures::MountPolicy> getMountPolicy(const std::string &mountPolicyName) const override;
-  std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria& searchCriteria) const;
- // getTapesByVid is implemented below (and works).
-  std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override;
-  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override;
-  std::list<TapeForWriting> getTapesForWriting(const std::string& logicalLibraryName) const;
-  bool isAdmin(const common::dataStructures::SecurityIdentity& admin) const;
-  void modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) override;
-  void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override;
-  void deleteVirtualOrganization(const std::string &voName) override;
-  std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const override;
-  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(const std::string & tapepoolName) const override;
-  common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(const std::string & tapepoolName) const override;
-  void modifyVirtualOrganizationName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName, const std::string &newVoName) override;
-  void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) override;
-  void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) override;
-  void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) override;
-  void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) override;
-  void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) override;
-  void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& comment) override;
-  void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& tapePoolName) override;
-  void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override;
-  void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) override;
-  void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& disabledReason) override;
-  void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool disabledValue) override;
-  void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t minArchiveRequestAge) override;
-  void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t archivePriority) override;
-  void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) override;
-  void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t minRetrieveRequestAge) override;
-  void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t retrievePriority) override;
-  void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string &activityRegex, const std::string& comment) override;
-  void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string& comment) override;
-  void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterGroupName, const std::string& comment) override;
-  void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterGroupName, const std::string& mountPolicy) override;
-  void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string& mountPolicy) override;
-  void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity& admin, const std::string& instanceName, const std::string& requesterName, const std::string &activityRegex, const std::string& mountPolicy) override;
-  void modifyStorageClassComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) override;
-  void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) override;
-  void modifyStorageClassName(const common::dataStructures::SecurityIdentity& admin, const std::string& currentName, const std::string& newName) override;
-  void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t nbCopies) override;
-  void modifyTapeComment(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::optional<std::string> &comment) override;
-  void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& encryptionKeyName) override;
-  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& verificationStatus) override;
-  void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const std::optional<common::dataStructures::Tape::State> & prev_state, const std::optional<std::string> & stateReason) override;
-  void modifyTapeMediaType(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& mediaType) override;
-  void modifyTapeVendor(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& vendor) override;
-  void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& logicalLibraryName) override;
-  void modifyTapePoolComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) override;
-  void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) override;
-  void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t nbPartialTapes) override;
-  void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& supply) override;
-  void modifyTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& currentName, const std::string& newName) override;
-  void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& tapePoolName) override;
-  void noSpaceLeftOnTape(const std::string& vid) override;
-  void ping() override;
-  void verifySchemaVersion() override;
-  SchemaVersion getSchemaVersion() const override;
-  uint64_t checkAndGetNextArchiveFileId(const std::string &diskInstanceName, const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) override;
-  common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(const std::string &diskInstanceName,
-    const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) override;
-  common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(const std::string& diskInstanceName, const uint64_t archiveFileId, const common::dataStructures::RequesterIdentity& user, const std::optional<std::string>& activity, log::LogContext& lc, const std::optional<std::string> &mountPolicyName) override;
-  void reclaimTape(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, cta::log::LogContext & lc) override;
-  void checkTapeForLabel(const std::string& vid) override;
-  uint64_t getNbFilesOnTape(const std::string& vid) const  override;
-  void setTapeDisabled(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string & reason) override;
-  void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string & reason) override;
-  void setTapeFull(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const bool fullValue) override;
-  void setTapeDirty(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const bool dirtyValue) override;
-  void setTapeDirty(const std::string & vid) override;
-  void setTapeIsFromCastorInUnitTests(const std::string &vid) override;
-  void setTapePoolEncryption(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const bool encryptionValue) override;
-  bool tapeExists(const std::string& vid) const;
-  bool diskSystemExists(const std::string& name) const;
-  void tapeLabelled(const std::string& vid, const std::string& drive) override;
-  void tapeMountedForArchive(const std::string& vid, const std::string& drive) override;
-  void tapeMountedForRetrieve(const std::string& vid, const std::string& drive) override;
-  bool tapePoolExists(const std::string& tapePoolName) const;
-  void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance, const std::string &diskFileId) override;
-  void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
-  void modifyArchiveFileStorageClassId(const uint64_t archiveFileId, const std::string &newStorageClassName) const override;
-  void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId, const std::string &diskInstance) const;
-
-  common::dataStructures::Tape::State getTapeState(const std::string & vid) const;
-
-  common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const override;
-
-  common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string>& vids) const override;
-
-  std::list<common::dataStructures::MountPolicy> getMountPolicies() const override;
-
-  std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const override;
-
-  std::list<std::string> getTapeDriveNames() const override;
-
-  std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const override;
-
-  std::list<common::dataStructures::TapeDrive> getTapeDrives() const override;
-
-  void setDesiredTapeDriveState(const std::string&, const common::dataStructures::DesiredDriveState &desiredState) override;
-
-  void setDesiredTapeDriveStateComment(const std::string& tapeDriveName, const std::string &comment) override;
-
-  void updateTapeDriveStatistics(const std::string& tapeDriveName,
-    const std::string& host, const std::string& logicalLibrary,
-    const common::dataStructures::TapeDriveStatistics& statistics) override;
-
-  void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) override;
-
-  std::map<std::string, uint64_t> getDiskSpaceReservations() const override;
-
-  void reserveDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
-
-  void releaseDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
-
-  /*
-   * Implemented for testing disk space reservation logic
-   */
-  disk::DiskSystemList getAllDiskSystems() const override;
-
-  void createDiskInstance(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-
-  void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL, const uint64_t refreshInterval, const std::string &comment) override;
-
-  void createDiskSystem(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceName, const std::string &diskInstanceSpaceName, const std::string &fileRegexp, const uint64_t targetedFreeSpace, const time_t sleepTime, const std::string &comment) override;
-
-  void modifyDiskInstanceSpaceFreeSpace(const std::string &name, const std::string &diskInstance, const uint64_t freeSpace) override;
-
-  // This special funcitons for unit tests should be put in private
-  void addEnabledTape(const std::string & vid);
-  void addDisabledTape(const std::string & vid);
-  void addRepackingTape(const std::string & vid);
-
-private:
-  mutable threading::Mutex m_tapeEnablingMutex;
-  std::map<std::string, common::dataStructures::Tape::State> m_tapeEnabling;
-
-  common::dataStructures::TapeDrive m_tapeDriveStatus;
-
-  disk::DiskSystemList m_diskSystemList;
-  std::map<std::string, common::dataStructures::DiskInstance> m_diskInstances;
-  std::map<std::string, common::dataStructures::DiskInstanceSpace> m_diskInstanceSpaces;
-};
-
-}  // namespace catalogue
-}  // namespace cta.
-
diff --git a/catalogue/Group.hpp b/catalogue/Group.hpp
new file mode 100644
index 0000000000..0ad7ce1c48
--- /dev/null
+++ b/catalogue/Group.hpp
@@ -0,0 +1,64 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace cta {
+namespace catalogue {
+
+/**
+  * A fully qualified user group, in other words the name of the disk instance
+  * and the name of the group.
+  */
+struct Group {
+  /**
+   * The name of the disk instance to which the group name belongs.
+   */
+  std::string diskInstanceName;
+
+  /**
+   * The name of the group which is only guaranteed to be unique within its
+   * disk instance.
+   */
+  std::string groupName;
+
+  /**
+   * Constructor.
+   *
+   * @param d The name of the disk instance to which the group name belongs.
+   * @param g The name of the group which is only guaranteed to be unique
+   * within its disk instance.
+   */
+  Group(const std::string &d, const std::string &g): diskInstanceName(d), groupName(g) {
+  }
+
+  /**
+   * Less than operator.
+   *
+   * @param rhs The argument on the right hand side of the operator.
+   * @return True if this object is less than the argument on the right hand
+   * side of the operator.
+   */
+  bool operator<(const Group &rhs) const {
+    return diskInstanceName < rhs.diskInstanceName || groupName < rhs.groupName;
+  }
+}; // struct Group
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/InMemoryCatalogueFactory.cpp b/catalogue/InMemoryCatalogueFactory.cpp
index a3b4003c94..fd54791fcb 100644
--- a/catalogue/InMemoryCatalogueFactory.cpp
+++ b/catalogue/InMemoryCatalogueFactory.cpp
@@ -15,9 +15,13 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "catalogue/CatalogueRetryWrapper.hpp"
-#include "catalogue/InMemoryCatalogueFactory.hpp"
+#include <memory>
+#include <string>
+#include <utility>
+
 #include "catalogue/InMemoryCatalogue.hpp"
+#include "catalogue/InMemoryCatalogueFactory.hpp"
+#include "catalogue/retrywrappers/CatalogueRetryWrapper.hpp"
 #include "common/exception/Exception.hpp"
 
 namespace cta {
diff --git a/catalogue/InMemoryVersionOfCatalogueTest.cpp b/catalogue/InMemoryVersionOfCatalogueTest.cpp
deleted file mode 100644
index 7386e156d0..0000000000
--- a/catalogue/InMemoryVersionOfCatalogueTest.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#include "catalogue/CatalogueTest.hpp"
-#include "catalogue/InMemoryCatalogueFactory.hpp"
-#ifdef STDOUT_LOGGING
-#include "common/log/StdoutLogger.hpp"
-#else
-#include "common/log/DummyLogger.hpp"
-#endif
-
-namespace unitTests {
-
-namespace {
-
-const uint64_t g_in_memory_CatalogueTest_nbConn = 1;
-const uint64_t g_in_memory_nbArchiveFileListingConns = 1;
-const uint64_t g_in_memory_maxTriesToConnect = 1;
-#ifdef STDOUT_LOGGING
-cta::log::StdoutLogger g_in_memory_CatalogueTest_dummyLogger("stdout", "stdout");
-#else
-cta::log::DummyLogger g_in_memory_CatalogueTest_dummyLogger("dummy", "dummy");
-#endif
-
-cta::catalogue::InMemoryCatalogueFactory g_inMemoryCatalogueFactory(g_in_memory_CatalogueTest_dummyLogger,
-  g_in_memory_CatalogueTest_nbConn, g_in_memory_nbArchiveFileListingConns, g_in_memory_maxTriesToConnect);
-
-cta::catalogue::CatalogueFactory *g_inMemoryCatalogueFactoryPtr = &g_inMemoryCatalogueFactory;
-
-} // anonymous namespace
-
-INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_CatalogueTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
-
-} // namespace unitTests
diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp
deleted file mode 100644
index 8eedc3a3ce..0000000000
--- a/catalogue/OracleCatalogue.cpp
+++ /dev/null
@@ -1,1247 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#include <algorithm>
-
-#include "catalogue/ArchiveFileRow.hpp"
-#include "catalogue/CatalogueItor.hpp"
-#include "catalogue/InsertFileRecycleLog.hpp"
-#include "catalogue/OracleCatalogue.hpp"
-#include "catalogue/retryOnLostConnection.hpp"
-#include "catalogue/TapeItemWrittenPointer.hpp"
-#include "common/dataStructures/DeleteArchiveRequest.hpp"
-#include "common/dataStructures/FileRecycleLog.hpp"
-#include "common/exception/Exception.hpp"
-#include "common/exception/FileSizeMismatch.hpp"
-#include "common/exception/LostDatabaseConnection.hpp"
-#include "common/exception/TapeFseqMismatch.hpp"
-#include "common/exception/UserError.hpp"
-#include "common/log/TimingList.hpp"
-#include "common/threading/MutexLocker.hpp"
-#include "common/Timer.hpp"
-#include "common/utils/utils.hpp"
-#include "rdbms/AutoRollback.hpp"
-#include "rdbms/rdbms.hpp"
-#include "rdbms/wrapper/OcciColumn.hpp"
-#include "rdbms/wrapper/OcciStmt.hpp"
-
-namespace cta {
-namespace catalogue {
-
-namespace {
-  /**
-   * Structure used to assemble a batch of rows to insert into the TAPE_FILE
-   * table.
-   */
-  struct TapeFileBatch {
-    size_t nbRows;
-    rdbms::wrapper::OcciColumn vid;
-    rdbms::wrapper::OcciColumn fSeq;
-    rdbms::wrapper::OcciColumn blockId;
-    rdbms::wrapper::OcciColumn fileSize;
-    rdbms::wrapper::OcciColumn copyNb;
-    rdbms::wrapper::OcciColumn creationTime;
-    rdbms::wrapper::OcciColumn archiveFileId;
-
-    /**
-     * Constructor.
-     *
-     * @param nbRowsValue  The Number of rows to be inserted.
-     */
-    TapeFileBatch(const size_t nbRowsValue):
-      nbRows(nbRowsValue),
-      vid("VID", nbRows),
-      fSeq("FSEQ", nbRows),
-      blockId("BLOCK_ID", nbRows),
-      fileSize("LOGICAL_SIZE_IN_BYTES", nbRows),
-      copyNb("COPY_NB", nbRows),
-      creationTime("CREATION_TIME", nbRows),
-      archiveFileId("ARCHIVE_FILE_ID", nbRows) {
-    }
-  }; // struct TapeFileBatch
-
-  /**
-   * Structure used to assemble a batch of rows to insert into the ARCHIVE_FILE
-   * table.
-   */
-  struct ArchiveFileBatch {
-    size_t nbRows;
-    rdbms::wrapper::OcciColumn archiveFileId;
-    rdbms::wrapper::OcciColumn diskInstance;
-    rdbms::wrapper::OcciColumn diskFileId;
-    rdbms::wrapper::OcciColumn diskFileUser;
-    rdbms::wrapper::OcciColumn diskFileGroup;
-    rdbms::wrapper::OcciColumn size;
-    rdbms::wrapper::OcciColumn checksumBlob;
-    rdbms::wrapper::OcciColumn checksumAdler32;
-    rdbms::wrapper::OcciColumn storageClassName;
-    rdbms::wrapper::OcciColumn creationTime;
-    rdbms::wrapper::OcciColumn reconciliationTime;
-
-    /**
-     * Constructor.
-     *
-     * @param nbRowsValue  The Number of rows to be inserted.
-     */
-    ArchiveFileBatch(const size_t nbRowsValue):
-      nbRows(nbRowsValue),
-      archiveFileId("ARCHIVE_FILE_ID", nbRows),
-      diskInstance("DISK_INSTANCE_NAME", nbRows),
-      diskFileId("DISK_FILE_ID", nbRows),
-      diskFileUser("DISK_FILE_UID", nbRows),
-      diskFileGroup("DISK_FILE_GID", nbRows),
-      size("SIZE_IN_BYTES", nbRows),
-      checksumBlob("CHECKSUM_BLOB", nbRows),
-      checksumAdler32("CHECKSUM_ADLER32", nbRows),
-      storageClassName("STORAGE_CLASS_NAME", nbRows),
-      creationTime("CREATION_TIME", nbRows),
-      reconciliationTime("RECONCILIATION_TIME", nbRows) {
-    }
-  }; // struct ArchiveFileBatch
-
-  /**
-   * Structure used to assemble a batch of rows to insert into the
-   * TAPE_FILE_BATCH temporary table.
-   */
-  struct TempTapeFileBatch {
-    size_t nbRows;
-    rdbms::wrapper::OcciColumn archiveFileId;
-
-    /**
-     * Constructor.
-     *
-     * @param nbRowsValue  The Number of rows to be inserted.
-     */
-    TempTapeFileBatch(const size_t nbRowsValue):
-      nbRows(nbRowsValue),
-      archiveFileId("ARCHIVE_FILE_ID", nbRows) {
-    }
-  }; // struct TempTapeFileBatch
-} // anonymous namespace
-
-//------------------------------------------------------------------------------
-// constructor
-//------------------------------------------------------------------------------
-OracleCatalogue::OracleCatalogue(
-  log::Logger &log,
-  const std::string &username,
-  const std::string &password,
-  const std::string &database,
-  const uint64_t nbConns,
-  const uint64_t nbArchiveFileListingConns):
-  RdbmsCatalogue(
-    log,
-    rdbms::Login(rdbms::Login::DBTYPE_ORACLE, username, password, database, "", 0),
-    nbConns,
-    nbArchiveFileListingConns) {
-}
-
-//------------------------------------------------------------------------------
-// destructor
-//------------------------------------------------------------------------------
-OracleCatalogue::~OracleCatalogue() {
-}
-
-//------------------------------------------------------------------------------
-// createAndPopulateTempTableFxid
-//------------------------------------------------------------------------------
-std::string OracleCatalogue::createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const {
-  const std::string tempTableName = "ORA$PTT_DISK_FXIDS";
-
-  try {
-    if(diskFileIds) {
-      conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-      std::string sql = "CREATE PRIVATE TEMPORARY TABLE " + tempTableName +
-        "(DISK_FILE_ID VARCHAR2(100))";
-      conn.executeNonQuery(sql);
-
-      sql = "INSERT INTO " + tempTableName + " VALUES(:DISK_FILE_ID)";
-      auto stmt = conn.createStmt(sql);
-      for(auto &diskFileId : diskFileIds.value()) {
-        stmt.bindString(":DISK_FILE_ID", diskFileId);
-        stmt.executeNonQuery();
-      }
-    }
-
-    return tempTableName;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextArchiveFileId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextArchiveFileId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE_ID_SEQ.NEXTVAL AS ARCHIVE_FILE_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("ARCHIVE_FILE_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextLogicalLibraryId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "LOGICAL_LIBRARY_ID_SEQ.NEXTVAL AS LOGICAL_LIBRARY_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("LOGICAL_LIBRARY_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextVirtualOrganizationId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "VIRTUAL_ORGANIZATION_ID_SEQ.NEXTVAL AS VIRTUAL_ORGANIZATION_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("VIRTUAL_ORGANIZATION_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMediaTypeId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextMediaTypeId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "MEDIA_TYPE_ID_SEQ.NEXTVAL AS MEDIA_TYPE_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("MEDIA_TYPE_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextStorageClassId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextStorageClassId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS_ID_SEQ.NEXTVAL AS STORAGE_CLASS_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("STORAGE_CLASS_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextTapePoolId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextTapePoolId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "TAPE_POOL_ID_SEQ.NEXTVAL AS TAPE_POOL_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("TAPE_POOL_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextFileRecyleLogId
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::getNextFileRecyleLogId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "FILE_RECYCLE_LOG_ID_SEQ.NEXTVAL AS FILE_RECYCLE_LOG_ID "
-      "FROM "
-        "DUAL";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("Result set is unexpectedly empty"));
-    }
-
-    return rset.columnUint64("FILE_RECYCLE_LOG_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// selectTapeForUpdateAndGetLastFSeq
-//------------------------------------------------------------------------------
-uint64_t OracleCatalogue::selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn,
-  const std::string &vid) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "LAST_FSEQ AS LAST_FSEQ "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :VID "
-      "FOR UPDATE";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("The tape with VID " + vid + " does not exist"));
-    }
-
-    return rset.columnUint64("LAST_FSEQ");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// filesWrittenToTape
-//------------------------------------------------------------------------------
-void OracleCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) {
-  try {
-    if (events.empty()) {
-      return;
-    }
-
-    auto firstEventItor = events.begin();
-    const auto &firstEvent = **firstEventItor;
-    checkTapeItemWrittenFieldsAreSet(__FUNCTION__, firstEvent);
-    const time_t now = time(nullptr);
-    threading::MutexLocker locker(m_mutex);
-    auto conn = m_connPool.getConn();
-    rdbms::AutoRollback autoRollback(conn);
-
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-
-    const uint64_t lastFSeq = selectTapeForUpdateAndGetLastFSeq(conn, firstEvent.vid);
-    uint64_t expectedFSeq = lastFSeq + 1;
-    uint64_t totalLogicalBytesWritten = 0;
-
-    uint32_t i = 0;
-    // We have a mix of files and items. Only files will be recorded, but items
-    // allow checking fSeq coherency.
-    // determine the number of files
-    size_t filesCount=std::count_if(events.cbegin(), events.cend(),
-        [](const TapeItemWrittenPointer &e) -> bool {return typeid(*e)==typeid(TapeFileWritten);});
-    TapeFileBatch tapeFileBatch(filesCount);
-
-    std::set<TapeFileWritten> fileEvents;
-
-    for (const auto &eventP: events) {
-      // Check for all item types.
-      const auto &event = *eventP;
-      checkTapeItemWrittenFieldsAreSet(__FUNCTION__, event);
-
-      if (event.vid != firstEvent.vid) {
-        throw exception::Exception(std::string("VID mismatch: expected=") + firstEvent.vid + " actual=" + event.vid);
-      }
-
-      if (expectedFSeq != event.fSeq) {
-        exception::TapeFseqMismatch ex;
-        ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
-          event.fSeq;
-        throw ex;
-      }
-      expectedFSeq++;
-
-      try {
-        // If this is a file (as opposed to a placeholder), do the full processing.
-        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(event);
-
-        checkTapeFileWrittenFieldsAreSet(__FUNCTION__, fileEvent);
-
-        totalLogicalBytesWritten += fileEvent.size;
-
-        // Store the length of each field and implicitly calculate the maximum field
-        // length of each column
-        tapeFileBatch.vid.setFieldLenToValueLen(i, fileEvent.vid);
-        tapeFileBatch.fSeq.setFieldLenToValueLen(i, fileEvent.fSeq);
-        tapeFileBatch.blockId.setFieldLenToValueLen(i, fileEvent.blockId);
-        tapeFileBatch.fileSize.setFieldLenToValueLen(i, fileEvent.size);
-        tapeFileBatch.copyNb.setFieldLenToValueLen(i, fileEvent.copyNb);
-        tapeFileBatch.creationTime.setFieldLenToValueLen(i, now);
-        tapeFileBatch.archiveFileId.setFieldLenToValueLen(i, fileEvent.archiveFileId);
-
-        fileEvents.insert(fileEvent);
-        i++;
-      } catch (std::bad_cast&) {}
-    }
-
-    // Store the value of each field
-    i = 0;
-    for (const auto &event: fileEvents) {
-      tapeFileBatch.vid.setFieldValue(i, event.vid);
-      tapeFileBatch.fSeq.setFieldValue(i, event.fSeq);
-      tapeFileBatch.blockId.setFieldValue(i, event.blockId);
-      tapeFileBatch.fileSize.setFieldValue(i, event.size);
-      tapeFileBatch.copyNb.setFieldValue(i, event.copyNb);
-      tapeFileBatch.creationTime.setFieldValue(i, now);
-      tapeFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
-      i++;
-    }
-
-    // Update the tape because all the necessary information is now available
-    auto lastEventItor = events.cend();
-    lastEventItor--;
-    const TapeItemWritten &lastEvent = **lastEventItor;
-    updateTape(conn, lastEvent.vid, lastEvent.fSeq, totalLogicalBytesWritten, filesCount, lastEvent.tapeDrive);
-
-    // If we had only placeholders and no file recorded, we are done (but we still commit the update of the tape's fSeq).
-    if (fileEvents.empty()) {
-      conn.commit();
-      return;
-    }
-
-    // Create the archive file entries, skipping those that already exist
-    idempotentBatchInsertArchiveFiles(conn, fileEvents);
-
-    {
-      const char *const sql =
-        "INSERT INTO TEMP_TAPE_FILE_INSERTION_BATCH(" "\n"
-          "VID,"                                      "\n"
-          "FSEQ,"                                     "\n"
-          "BLOCK_ID,"                                 "\n"
-          "LOGICAL_SIZE_IN_BYTES,"                    "\n"
-          "COPY_NB,"                                  "\n"
-          "CREATION_TIME,"                            "\n"
-          "ARCHIVE_FILE_ID)"                          "\n"
-        "VALUES("                                     "\n"
-          ":VID,"                                     "\n"
-          ":FSEQ,"                                    "\n"
-          ":BLOCK_ID,"                                "\n"
-          ":LOGICAL_SIZE_IN_BYTES,"                   "\n"
-          ":COPY_NB,"                                 "\n"
-          ":CREATION_TIME,"                           "\n"
-          ":ARCHIVE_FILE_ID)"                         "\n";
-      auto stmt = conn.createStmt(sql);
-      rdbms::wrapper::OcciStmt &occiStmt = dynamic_cast<rdbms::wrapper::OcciStmt &>(stmt.getStmt());
-      occiStmt.setColumn(tapeFileBatch.vid);
-      occiStmt.setColumn(tapeFileBatch.fSeq);
-      occiStmt.setColumn(tapeFileBatch.blockId);
-      occiStmt.setColumn(tapeFileBatch.fileSize);
-      occiStmt.setColumn(tapeFileBatch.copyNb);
-      occiStmt.setColumn(tapeFileBatch.creationTime);
-      occiStmt.setColumn(tapeFileBatch.archiveFileId);
-      try {
-        occiStmt->executeArrayUpdate(tapeFileBatch.nbRows);
-      } catch(oracle::occi::SQLException &ex) {
-        std::ostringstream msg;
-        msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
-          ex.what();
-
-        if(rdbms::wrapper::OcciStmt::connShouldBeClosed(ex)) {
-          // Close the statement first and then the connection
-          try {
-            occiStmt.close();
-          } catch(...) {
-          }
-
-          try {
-            conn.closeUnderlyingStmtsAndConn();
-          } catch(...) {
-          }
-          throw exception::LostDatabaseConnection(msg.str());
-        }
-        throw exception::Exception(msg.str());
-      } catch(std::exception &se) {
-        std::ostringstream msg;
-        msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
-          se.what();
-
-        throw exception::Exception(msg.str());
-      }
-    }
-
-    // Verify that the archive file entries in the catalogue database agree with
-    // the tape file written events
-    const auto fileSizesAndChecksums = selectArchiveFileSizesAndChecksums(conn, fileEvents);
-    for (const auto &event: fileEvents) {
-      const auto fileSizeAndChecksumItor = fileSizesAndChecksums.find(event.archiveFileId);
-
-      std::ostringstream fileContext;
-      fileContext << "archiveFileId=" << event.archiveFileId << ", diskInstanceName=" << event.diskInstance <<
-        ", diskFileId=" << event.diskFileId;
-
-      // This should never happen
-      if(fileSizesAndChecksums.end() == fileSizeAndChecksumItor) {
-        exception::Exception ex;
-        ex.getMessage() << __FUNCTION__ << ": Failed to find archive file entry in the catalogue: " << fileContext.str();
-        throw ex;
-      }
-
-      const auto &fileSizeAndChecksum = fileSizeAndChecksumItor->second;
-
-      if(fileSizeAndChecksum.fileSize != event.size) {
-        catalogue::FileSizeMismatch ex;
-        ex.getMessage() << __FUNCTION__ << ": File size mismatch: expected=" << fileSizeAndChecksum.fileSize <<
-          ", actual=" << event.size << ": " << fileContext.str();
-        throw ex;
-      }
-
-      fileSizeAndChecksum.checksumBlob.validate(event.checksumBlob);
-    }
-
-    std::list<InsertFileRecycleLog> recycledFiles = insertOldCopiesOfFilesIfAnyOnFileRecycleLog(conn);
-
-    {
-      const char *const sql =
-        "INSERT INTO TAPE_FILE (VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES,"              "\n"
-           "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID)"                                     "\n"
-        "SELECT VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES,"                              "\n"
-           "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID FROM TEMP_TAPE_FILE_INSERTION_BATCH";
-      auto stmt = conn.createStmt(sql);
-      stmt.executeNonQuery();
-    }
-
-    for(auto & recycledFile: recycledFiles){
-      const char *const sql =
-        "DELETE FROM "
-          "TAPE_FILE "
-        "WHERE "
-          "TAPE_FILE.VID = :VID AND TAPE_FILE.FSEQ = :FSEQ";
-
-      auto stmt = conn.createStmt(sql);
-      stmt.bindString(":VID",recycledFile.vid);
-      stmt.bindUint64(":FSEQ",recycledFile.fSeq);
-      stmt.executeNonQuery();
-    }
-
-    {
-      conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
-      conn.commit();
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// idempotentBatchInsertArchiveFiles
-//------------------------------------------------------------------------------
-void OracleCatalogue::idempotentBatchInsertArchiveFiles(rdbms::Conn &conn, const std::set<TapeFileWritten> &events) {
-  try {
-    ArchiveFileBatch archiveFileBatch(events.size());
-    const time_t now = time(nullptr);
-    std::vector<uint32_t> adler32(events.size());
-
-    // Store the length of each field and implicitly calculate the maximum field length of each column
-    uint32_t i = 0;
-    for (const auto &event: events) {
-      // Keep transition ADLER32 checksum column up-to-date with the ChecksumBlob
-      try {
-        std::string adler32hex = checksum::ChecksumBlob::ByteArrayToHex(event.checksumBlob.at(checksum::ADLER32));
-        adler32[i] = strtoul(adler32hex.c_str(), 0, 16);
-      } catch(exception::ChecksumTypeMismatch &ex) {
-        // No ADLER32 checksum exists in the checksumBlob
-        adler32[i] = 0;
-      }
-
-      archiveFileBatch.archiveFileId.setFieldLenToValueLen(i, event.archiveFileId);
-      archiveFileBatch.diskInstance.setFieldLenToValueLen(i, event.diskInstance);
-      archiveFileBatch.diskFileId.setFieldLenToValueLen(i, event.diskFileId);
-      archiveFileBatch.diskFileUser.setFieldLenToValueLen(i, event.diskFileOwnerUid);
-      archiveFileBatch.diskFileGroup.setFieldLenToValueLen(i, event.diskFileGid);
-      archiveFileBatch.size.setFieldLenToValueLen(i, event.size);
-      archiveFileBatch.checksumBlob.setFieldLen(i, 2 + event.checksumBlob.length());
-      archiveFileBatch.checksumAdler32.setFieldLenToValueLen(i, adler32[i]);
-      archiveFileBatch.storageClassName.setFieldLenToValueLen(i, event.storageClassName);
-      archiveFileBatch.creationTime.setFieldLenToValueLen(i, now);
-      archiveFileBatch.reconciliationTime.setFieldLenToValueLen(i, now);
-      i++;
-    }
-
-    // Store the value of each field
-    i = 0;
-    for (const auto &event: events) {
-      archiveFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
-      archiveFileBatch.diskInstance.setFieldValue(i, event.diskInstance);
-      archiveFileBatch.diskFileId.setFieldValue(i, event.diskFileId);
-      archiveFileBatch.diskFileUser.setFieldValue(i, event.diskFileOwnerUid);
-      archiveFileBatch.diskFileGroup.setFieldValue(i, event.diskFileGid);
-      archiveFileBatch.size.setFieldValue(i, event.size);
-      archiveFileBatch.checksumBlob.setFieldValueToRaw(i, event.checksumBlob.serialize());
-      archiveFileBatch.checksumAdler32.setFieldValue(i, adler32[i]);
-      archiveFileBatch.storageClassName.setFieldValue(i, event.storageClassName);
-      archiveFileBatch.creationTime.setFieldValue(i, now);
-      archiveFileBatch.reconciliationTime.setFieldValue(i, now);
-      i++;
-    }
-
-    const char *const sql =
-      "INSERT INTO ARCHIVE_FILE("
-        "ARCHIVE_FILE_ID,"
-        "DISK_INSTANCE_NAME,"
-        "DISK_FILE_ID,"
-        "DISK_FILE_UID,"
-        "DISK_FILE_GID,"
-        "SIZE_IN_BYTES,"
-        "CHECKSUM_BLOB,"
-        "CHECKSUM_ADLER32,"
-        "STORAGE_CLASS_ID,"
-        "CREATION_TIME,"
-        "RECONCILIATION_TIME)"
-      "SELECT "
-        ":ARCHIVE_FILE_ID,"
-        ":DISK_INSTANCE_NAME,"
-        ":DISK_FILE_ID,"
-        ":DISK_FILE_UID,"
-        ":DISK_FILE_GID,"
-        ":SIZE_IN_BYTES,"
-        ":CHECKSUM_BLOB,"
-        ":CHECKSUM_ADLER32,"
-        "STORAGE_CLASS_ID,"
-        ":CREATION_TIME,"
-        ":RECONCILIATION_TIME "
-      "FROM "
-        "STORAGE_CLASS "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    rdbms::wrapper::OcciStmt &occiStmt = dynamic_cast<rdbms::wrapper::OcciStmt &>(stmt.getStmt());
-    occiStmt->setBatchErrorMode(true);
-
-    occiStmt.setColumn(archiveFileBatch.archiveFileId);
-    occiStmt.setColumn(archiveFileBatch.diskInstance);
-    occiStmt.setColumn(archiveFileBatch.diskFileId);
-    occiStmt.setColumn(archiveFileBatch.diskFileUser);
-    occiStmt.setColumn(archiveFileBatch.diskFileGroup);
-    occiStmt.setColumn(archiveFileBatch.size);
-    occiStmt.setColumn(archiveFileBatch.checksumBlob, oracle::occi::OCCI_SQLT_VBI);
-    occiStmt.setColumn(archiveFileBatch.checksumAdler32);
-    occiStmt.setColumn(archiveFileBatch.storageClassName);
-    occiStmt.setColumn(archiveFileBatch.creationTime);
-    occiStmt.setColumn(archiveFileBatch.reconciliationTime);
-
-    try {
-      occiStmt->executeArrayUpdate(archiveFileBatch.nbRows);
-    } catch(oracle::occi::BatchSQLException &be) {
-      const unsigned int nbFailedRows = be.getFailedRowCount();
-      exception::Exception ex;
-      ex.getMessage() << "Caught a BatchSQLException" << nbFailedRows;
-      bool foundErrorOtherThanUniqueConstraint = false;
-      for (unsigned int row = 0; row < nbFailedRows; row++ ) {
-        oracle::occi::SQLException err = be.getException(row);
-        const unsigned int rowIndex = be.getRowNum(row);
-        const int errorCode = err.getErrorCode();
-
-        // If the error is anything other than a unique constraint error
-        if(1 != errorCode) {
-          foundErrorOtherThanUniqueConstraint = true;
-          ex.getMessage() << ": Row " << rowIndex << " generated ORA error " << errorCode;
-        }
-      }
-      if (foundErrorOtherThanUniqueConstraint) {
-        throw ex;
-      }
-    } catch(oracle::occi::SQLException &ex) {
-      std::ostringstream msg;
-      msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
-        ex.what();
-
-      if(rdbms::wrapper::OcciStmt::connShouldBeClosed(ex)) {
-        // Close the statement first and then the connection
-        try {
-          occiStmt.close();
-        } catch(...) {
-        }
-
-        try {
-          conn.closeUnderlyingStmtsAndConn();
-        } catch(...) {
-        }
-        throw exception::LostDatabaseConnection(msg.str());
-      }
-      throw exception::Exception(msg.str());
-    } catch(std::exception &se) {
-      std::ostringstream msg;
-      msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
-        se.what();
-
-      throw exception::Exception(msg.str());
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::list<cta::catalogue::InsertFileRecycleLog> OracleCatalogue::insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn& conn) {
-  std::list<cta::catalogue::InsertFileRecycleLog> fileRecycleLogsToInsert;
-  try {
-    //Get the TAPE_FILE entry to put on the file recycle log
-    {
-      const char *const sql =
-        "SELECT "
-          "TAPE_FILE.VID AS VID,"
-          "TAPE_FILE.FSEQ AS FSEQ,"
-          "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-          "TAPE_FILE.COPY_NB AS COPY_NB,"
-          "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
-          "TAPE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
-        "FROM "
-          "TAPE_FILE "
-        "JOIN "
-          "TEMP_TAPE_FILE_INSERTION_BATCH "
-        "ON "
-          "TEMP_TAPE_FILE_INSERTION_BATCH.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID AND TEMP_TAPE_FILE_INSERTION_BATCH.COPY_NB = TAPE_FILE.COPY_NB "
-        "WHERE "
-          "TAPE_FILE.VID != TEMP_TAPE_FILE_INSERTION_BATCH.VID OR TAPE_FILE.FSEQ != TEMP_TAPE_FILE_INSERTION_BATCH.FSEQ";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      while(rset.next()){
-        cta::catalogue::InsertFileRecycleLog fileRecycleLog;
-        fileRecycleLog.vid = rset.columnString("VID");
-        fileRecycleLog.fSeq = rset.columnUint64("FSEQ");
-        fileRecycleLog.blockId = rset.columnUint64("BLOCK_ID");
-        fileRecycleLog.copyNb = rset.columnUint8("COPY_NB");
-        fileRecycleLog.tapeFileCreationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        fileRecycleLog.archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
-        fileRecycleLog.reasonLog = InsertFileRecycleLog::getRepackReasonLog();
-        fileRecycleLog.recycleLogTime = time(nullptr);
-        fileRecycleLogsToInsert.push_back(fileRecycleLog);
-      }
-    }
-    {
-      for(auto & fileRecycleLog: fileRecycleLogsToInsert){
-        insertFileInFileRecycleLog(conn,fileRecycleLog);
-      }
-    }
-    return fileRecycleLogsToInsert;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// selectArchiveFileSizeAndChecksum
-//------------------------------------------------------------------------------
-std::map<uint64_t, OracleCatalogue::FileSizeAndChecksum> OracleCatalogue::selectArchiveFileSizesAndChecksums(
-  rdbms::Conn &conn, const std::set<TapeFileWritten> &events) {
-  try {
-    std::vector<oracle::occi::Number> archiveFileIdList(events.size());
-    for (const auto &event: events) {
-      archiveFileIdList.push_back(oracle::occi::Number(event.archiveFileId));
-    }
-
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32 "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN TEMP_TAPE_FILE_INSERTION_BATCH ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TEMP_TAPE_FILE_INSERTION_BATCH.ARCHIVE_FILE_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-
-    std::map<uint64_t, FileSizeAndChecksum> fileSizesAndChecksums;
-    while (rset.next()) {
-      const uint64_t archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
-
-      if (fileSizesAndChecksums.end() != fileSizesAndChecksums.find(archiveFileId)) {
-        exception::Exception ex;
-        ex.getMessage() << __FUNCTION__ << " failed: "
-          "Found duplicate archive file identifier in batch of files written to tape: archiveFileId=" << archiveFileId;
-        throw ex;
-      }
-      FileSizeAndChecksum fileSizeAndChecksum;
-      fileSizeAndChecksum.fileSize = rset.columnUint64("SIZE_IN_BYTES");
-      fileSizeAndChecksum.checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-      fileSizesAndChecksums[archiveFileId] = fileSizeAndChecksum;
-    }
-
-    return fileSizesAndChecksums;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteArchiveFile
-//------------------------------------------------------------------------------
-void OracleCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
-  log::LogContext &lc) {
-  try {
-    const char *selectSql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "WHERE "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID "
-      "FOR UPDATE";
-    utils::Timer t;
-
-    auto conn = m_connPool.getConn();
-    rdbms::AutoRollback autoRollback(conn);
-
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-
-    const auto getConnTime = t.secs(utils::Timer::resetCounter);
-    auto selectStmt = conn.createStmt(selectSql);
-    const auto createStmtTime = t.secs();
-    selectStmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    t.reset();
-    rdbms::Rset selectRset = selectStmt.executeQuery();
-    const auto selectFromArchiveFileTime = t.secs();
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
-    std::set<std::string> vidsToSetDirty;
-    while(selectRset.next()) {
-      if(nullptr == archiveFile.get()) {
-        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
-
-        archiveFile->archiveFileID = selectRset.columnUint64("ARCHIVE_FILE_ID");
-        archiveFile->diskInstance = selectRset.columnString("DISK_INSTANCE_NAME");
-        archiveFile->diskFileId = selectRset.columnString("DISK_FILE_ID");
-        archiveFile->diskFileInfo.owner_uid = selectRset.columnUint64("DISK_FILE_UID");
-        archiveFile->diskFileInfo.gid = selectRset.columnUint64("DISK_FILE_GID");
-        archiveFile->fileSize = selectRset.columnUint64("SIZE_IN_BYTES");
-        archiveFile->checksumBlob.deserializeOrSetAdler32(selectRset.columnBlob("CHECKSUM_BLOB"), selectRset.columnUint64("CHECKSUM_ADLER32"));
-        archiveFile->storageClass = selectRset.columnString("STORAGE_CLASS_NAME");
-        archiveFile->creationTime = selectRset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-        archiveFile->reconciliationTime = selectRset.columnUint64("RECONCILIATION_TIME");
-      }
-
-      // If there is a tape file
-      if(!selectRset.columnIsNull("VID")) {
-        // Add the tape file to the archive file's in-memory structure
-        common::dataStructures::TapeFile tapeFile;
-        tapeFile.vid = selectRset.columnString("VID");
-        vidsToSetDirty.insert(tapeFile.vid);
-        tapeFile.fSeq = selectRset.columnUint64("FSEQ");
-        tapeFile.blockId = selectRset.columnUint64("BLOCK_ID");
-        tapeFile.fileSize = selectRset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-        tapeFile.copyNb = selectRset.columnUint8("COPY_NB");
-        tapeFile.creationTime = selectRset.columnUint64("TAPE_FILE_CREATION_TIME");
-        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
-        archiveFile->tapeFiles.push_back(tapeFile);
-      }
-    }
-
-    if(nullptr == archiveFile.get()) {
-      log::ScopedParamContainer spc(lc);
-      spc.add("fileId", archiveFileId);
-      lc.log(log::WARNING, "Ignoring request to delete archive file because it does not exist in the catalogue");
-      return;
-    }
-
-    if(diskInstanceName != archiveFile->diskInstance) {
-      log::ScopedParamContainer spc(lc);
-      spc.add("fileId", std::to_string(archiveFile->archiveFileID))
-         .add("diskInstance", archiveFile->diskInstance)
-         .add("requestDiskInstance", diskInstanceName)
-         .add("diskFileId", archiveFile->diskFileId)
-         .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
-         .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
-         .add("fileSize", std::to_string(archiveFile->fileSize))
-         .add("creationTime", std::to_string(archiveFile->creationTime))
-         .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
-         .add("storageClass", archiveFile->storageClass)
-         .add("getConnTime", getConnTime)
-         .add("createStmtTime", createStmtTime)
-         .add("selectFromArchiveFileTime", selectFromArchiveFileTime);
-      archiveFile->checksumBlob.addFirstChecksumToLog(spc);
-      for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
-        std::stringstream tapeCopyLogStream;
-        tapeCopyLogStream << "copy number: " << static_cast<int>(it->copyNb)
-          << " vid: " << it->vid
-          << " fSeq: " << it->fSeq
-          << " blockId: " << it->blockId
-          << " creationTime: " << it->creationTime
-          << " fileSize: " << it->fileSize
-          << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
-          << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
-        spc.add("TAPE FILE", tapeCopyLogStream.str());
-      }
-      lc.log(log::WARNING, "Failed to delete archive file because the disk instance of the request does not match that "
-        "of the archived file");
-
-      exception::UserError ue;
-      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because the disk instance of "
-        "the request does not match that of the archived file: archiveFileId=" << archiveFileId << " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" <<
-        archiveFile->diskInstance;
-      throw ue;
-    }
-
-    t.reset();
-    {
-      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-      stmt.executeNonQuery();
-    }
-
-    const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter);
-
-    //Set the tapes where the files have been deleted to dirty
-    for(auto &vidToSetDirty: vidsToSetDirty){
-      setTapeDirty(conn,vidToSetDirty);
-    }
-
-    const auto setTapeDirtyTime = t.secs(utils::Timer::resetCounter);
-
-    {
-      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-      stmt.executeNonQuery();
-    }
-    const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter);
-
-    conn.commit();
-    const auto commitTime = t.secs();
-
-    log::ScopedParamContainer spc(lc);
-    spc.add("fileId", std::to_string(archiveFile->archiveFileID))
-       .add("diskInstance", archiveFile->diskInstance)
-       .add("diskFileId", archiveFile->diskFileId)
-       .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
-       .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
-       .add("fileSize", std::to_string(archiveFile->fileSize))
-       .add("creationTime", std::to_string(archiveFile->creationTime))
-       .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
-       .add("storageClass", archiveFile->storageClass)
-       .add("getConnTime", getConnTime)
-       .add("createStmtTime", createStmtTime)
-       .add("selectFromArchiveFileTime", selectFromArchiveFileTime)
-       .add("deleteFromTapeFileTime", deleteFromTapeFileTime)
-       .add("setTapeDirtyTime",setTapeDirtyTime)
-       .add("deleteFromArchiveFileTime", deleteFromArchiveFileTime)
-       .add("commitTime", commitTime);
-    archiveFile->checksumBlob.addFirstChecksumToLog(spc);
-    for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
-      std::stringstream tapeCopyLogStream;
-      tapeCopyLogStream << "copy number: " << it->copyNb
-        << " vid: " << it->vid
-        << " fSeq: " << it->fSeq
-        << " blockId: " << it->blockId
-        << " creationTime: " << it->creationTime
-        << " fileSize: " << it->fileSize
-        << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
-        << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
-      spc.add("TAPE FILE", tapeCopyLogStream.str());
-    }
-    lc.log(log::INFO, "Archive file deleted from CTA catalogue");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    std::ostringstream msg;
-    msg << __FUNCTION__ << ": diskInstanceName=" << diskInstanceName <<",archiveFileId=" <<
-      archiveFileId << ": " << ex.getMessage().str();
-    ex.getMessage().str(msg.str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// copyArchiveFileToRecycleBinAndDelete
-//------------------------------------------------------------------------------
-void OracleCatalogue::copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc){
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do an INSERT INTO, update and two DELETE FROM
-    //in a single transaction
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-    copyArchiveFileToFileRecycleLog(conn,request);
-    tl.insertAndReset("insertToRecycleBinTime",t);
-    setTapeDirty(conn,request.archiveFileID);
-    tl.insertAndReset("setTapeDirtyTime",t);
-    deleteTapeFiles(conn,request);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
-    RdbmsCatalogue::deleteArchiveFile(conn,request);
-    tl.insertAndReset("deleteArchiveFileTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId",request.archiveFileID);
-    spc.add("diskFileId",request.diskFileId);
-    spc.add("diskFilePath",request.diskFilePath);
-    spc.add("diskInstance",request.diskInstance);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In OracleCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapeFilesAndArchiveFileFromRecycleBin
-//------------------------------------------------------------------------------
-void OracleCatalogue::deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn& conn, const uint64_t archiveFileId, log::LogContext& lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do two delete in one transaction
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-    deleteTapeFilesFromRecycleBin(conn,archiveFileId);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
-    deleteArchiveFileFromRecycleBin(conn,archiveFileId);
-    tl.insertAndReset("deleteArchiveFileTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId",archiveFileId);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In OracleCatalogue::deleteTapeFilesAndArchiveFileFromRecycleBin: tape files and archiveFiles deleted from the recycle-bin.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// copyTapeFileToFileRecyleLogAndDelete
-//------------------------------------------------------------------------------
-void OracleCatalogue::copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do an INSERT INTO and a DELETE FROM
-    //in a single transaction
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-    copyTapeFilesToFileRecycleLog(conn, file, reason);
-    tl.insertAndReset("insertToRecycleBinTime",t);
-    setTapeDirty(conn, file.archiveFileID);
-    tl.insertAndReset("setTapeDirtyTime",t);
-    deleteTapeFiles(conn,file);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId", file.archiveFileID);
-    spc.add("diskFileId", file.diskFileId);
-    spc.add("diskFilePath", file.diskFileInfo.path);
-    spc.add("diskInstance", file.diskInstance);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In OracleCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreEntryInRecycleLog
-//------------------------------------------------------------------------------
-void OracleCatalogue::restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor,
-  const std::string &newFid, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-
-    if (!fileRecycleLogItor.hasMore()) {
-      throw cta::exception::UserError("No file in the recycle bin matches the parameters passed");
-    }
-    auto fileRecycleLog = fileRecycleLogItor.next();
-    if (fileRecycleLogItor.hasMore()) {
-      //stop restoring more than one file at once
-      throw cta::exception::UserError("More than one recycle bin file matches the parameters passed");
-    }
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
-
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFilePtr = getArchiveFileById(conn, fileRecycleLog.archiveFileId);
-    if (!archiveFilePtr) {
-      restoreArchiveFileInRecycleLog(conn, fileRecycleLog, newFid, lc);
-    } else {
-      if (archiveFilePtr->tapeFiles.find(fileRecycleLog.copyNb) != archiveFilePtr->tapeFiles.end()) {
-        //copy with same copy_nb exists, cannot restore
-        UserSpecifiedExistingDeletedFileCopy ex;
-        ex.getMessage() << "Cannot restore file copy with archiveFileId " << std::to_string(fileRecycleLog.archiveFileId)
-        << " and copy_nb " << std::to_string(fileRecycleLog.copyNb) << " because a tapefile with same archiveFileId and copy_nb already exists";
-        throw ex;
-      }
-    }
-
-
-    restoreFileCopyInRecycleLog(conn, fileRecycleLog, lc);
-
-    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
-    conn.commit();
-
-    log::ScopedParamContainer spc(lc);
-    tl.insertAndReset("commitTime",t);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In OracleCatalogue::restoreEntryInRecycleLog: all file copies successfully restored.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreFileCopyInRecycleLog
-//------------------------------------------------------------------------------
-void OracleCatalogue::restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLog, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    cta::common::dataStructures::TapeFile tapeFile;
-    tapeFile.vid = fileRecycleLog.vid;
-    tapeFile.fSeq = fileRecycleLog.fSeq;
-    tapeFile.copyNb = fileRecycleLog.copyNb;
-    tapeFile.blockId = fileRecycleLog.blockId;
-    tapeFile.fileSize = fileRecycleLog.sizeInBytes;
-    tapeFile.creationTime = fileRecycleLog.tapeFileCreationTime;
-
-    insertTapeFile(conn, tapeFile, fileRecycleLog.archiveFileId);
-    tl.insertAndReset("insertTapeFileTime",t);
-
-    deleteTapeFileCopyFromRecycleBin(conn, fileRecycleLog);
-    tl.insertAndReset("deleteTapeFileCopyFromRecycleBinTime",t);
-
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", tapeFile.vid);
-    spc.add("archiveFileId", fileRecycleLog.archiveFileId);
-    spc.add("fSeq", tapeFile.fSeq);
-    spc.add("copyNb", tapeFile.copyNb);
-    spc.add("fileSize", tapeFile.fileSize);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In OracleCatalogue::restoreFileCopyInRecycleLog: File restored from the recycle log.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/OracleCatalogue.hpp b/catalogue/OracleCatalogue.hpp
deleted file mode 100644
index 26fdd5674e..0000000000
--- a/catalogue/OracleCatalogue.hpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-#include "catalogue/RdbmsCatalogue.hpp"
-#include "catalogue/InsertFileRecycleLog.hpp"
-#include "rdbms/Conn.hpp"
-
-namespace cta {
-namespace catalogue {
-
-/**
- * An Oracle based implementation of the CTA catalogue.
- */
-class OracleCatalogue: public RdbmsCatalogue {
-public:
-
-  /**
-   * Constructor.
-   *
-   * @param log Object representing the API to the CTA logging system.
-   * @param username The database username.
-   * @param password The database password.
-   * @param database The database name.
-   * @param nbConns The maximum number of concurrent connections to the
-   * underlying relational database for all operations accept listing archive
-   * files which can be relatively long operations.
-   * @param nbArchiveFileListingConns The maximum number of concurrent
-   * connections to the underlying relational database for the sole purpose of
-   * listing archive files.
-   */
-  OracleCatalogue(
-    log::Logger       &log,
-    const std::string &username,
-    const std::string &password,
-    const std::string &database,
-    const uint64_t nbConns,
-    const uint64_t nbArchiveFileListingConns);
-
-  /**
-   * Destructor.
-   */
-  ~OracleCatalogue() override;
-
-  /**
-   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
-   *
-   * @param conn The database connection.
-   * @param diskFileIds List of disk file IDs (fxid).
-   * @return Name of the temporary table
-   */
-  std::string createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const override;
-
-  /**
-   * Returns a unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return A unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   */
-  uint64_t getNextArchiveFileId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique logical library ID that can be used by a new logical
-   * library within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique logical library ID that can be used by a new logical
-   * library storage class within the catalogue.
-   */
-  uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
-  
-  /**
-   * Returns a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   * 
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   * 
-   * @param conn The database connection
-   * @return a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   */
-  uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique media type ID that can be used by a new media type within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique media type ID that can be used by a new media type
-   * within the catalogue.
-   */
-  uint64_t getNextMediaTypeId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   */
-  uint64_t getNextStorageClassId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   */
-  uint64_t getNextTapePoolId(rdbms::Conn &conn) override;
-  
-  /**
-   * Returns a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   */
-  uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) override;
-  
-
-  /**
-   * Notifies the catalogue that the specified files have been written to tape.
-   *
-   * @param events The tape file written events.
-   */
-  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) override;
-
-  /**
-   * !!!!!!!!!!!!!!!!!!! THIS METHOD SHOULD NOT BE USED !!!!!!!!!!!!!!!!!!!!!!!
-   * Deletes the specified archive file and its associated tape copies from the
-   * catalogue.
-   *
-   * Please note that the name of the disk instance is specified in order to
-   * prevent a disk instance deleting an archive file that belongs to another
-   * disk instance.
-   *
-   * Please note that this method is idempotent.  If the file to be deleted does
-   * not exist in the CTA catalogue then this method returns without error.
-   *
-   * @param instanceName The name of the instance from where the deletion request
-   * originated
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param lc The log context.
-   * @return The metadata of the deleted archive file including the metadata of
-   * the associated and also deleted tape copies.
-   */
-  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &instanceName, const uint64_t archiveFileId,
-    log::LogContext &lc) override;
-
-private:
-
-  /**
-   * Selects the specified tape for update and returns its last FSeq.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @return The last FSeq of the tape.
-   */
-  uint64_t selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn, const std::string &vid);
-
-  /**
-   * Batch inserts rows into the ARCHIVE_FILE table that correspond to the
-   * specified TapeFileWritten events.
-   *
-   * This method has idempotent behaviour in the case where an ARCHIVE_FILE
-   * already exists.  Such a situation will occur when a file has more than one
-   * copy on tape.  The first tape copy will cause two successful inserts, one
-   * into the ARCHIVE_FILE table and one into the  TAPE_FILE table.  The second
-   * tape copy will try to do the same, but the insert into the ARCHIVE_FILE
-   * table will fail or simply bounce as the row will already exists.  The
-   * insert into the TABLE_FILE table will succeed because the two TAPE_FILE
-   * rows will be unique.
-   *
-   * @param conn The database connection.
-   * @param events The tape file written events.
-   */
-  void idempotentBatchInsertArchiveFiles(rdbms::Conn &conn, const std::set<TapeFileWritten> &events);
-  
-  /**
-   * In the case we insert a TAPE_FILE that already has a copy on the catalogue (same copyNb),
-   * this TAPE_FILE will go to the FILE_RECYCLE_LOG table.
-   * 
-   * This case happens always during the repacking of a tape: the new TAPE_FILE created 
-   * will replace the old one, the old one will then be moved to the FILE_RECYCLE_LOG table
-   * 
-   * @param conn The database connection.
-   * @returns the list of inserted fileRecycleLog
-   */
-  std::list<cta::catalogue::InsertFileRecycleLog> insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn & conn);
-
-   /**
-   * Copy the archiveFile and the associated tape files from the ARCHIVE_FILE and TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the ARCHIVE_FILE and TAPE_FILE entries.
-   * @param conn the database connection
-   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the FILE_RECYCLE_LOG table
-   * @param lc the log context
-   */
-  void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
-  
-   /**
-   * Delete the TapeFiles and the ArchiveFile from the recycle-bin in one transaction
-   * @param conn the database connection
-   * @param archiveFileId the archiveFileId of the file to delete from the recycle-bin
-   */
-  void deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn& conn, const uint64_t archiveFileId, log::LogContext& lc) override;
-  
-  /**
-   * Copy the tape files from the TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the TAPE_FILE entry.
-   * @param conn the database connection
-   * @param file the file to be deleted
-   * @param reason The reason for deleting the tape file copy
-   * @param lc the log context
-   */
-  void copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file, 
-                                            const std::string &reason, log::LogContext & lc) override;
-
-  /**
-   * Copy the files in fileRecycleLogItor to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entries
-   * @param conn the database connection
-   * @param fileRecycleLogItor the collection of fileRecycleLogs we want to restore
-   * @param lc the log context
-   */
-  void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) override;
-
-  /**
-   * Copy the fileRecycleLog to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entry
-   * @param conn the database connection
-   * @param fileRecycleLog the fileRecycleLog we want to restore
-   * @param lc the log context
-   */
-  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLogItor, log::LogContext & lc);
-
-  /**
-   * The size and checksum of a file.
-   */
-  struct FileSizeAndChecksum {
-    uint64_t fileSize;
-    checksum::ChecksumBlob checksumBlob;
-  };
-
-  /**
-   * Returns the sizes and checksums of the specified archive files.
-   *
-   * @param conn The database connection.
-   * @param events The tape file written events that identify the archive files.
-   * @return A map from the identifier of each archive file to its size and checksum.
-   */
-  std::map<uint64_t, FileSizeAndChecksum> selectArchiveFileSizesAndChecksums(rdbms::Conn &conn,
-    const std::set<TapeFileWritten> &events);
-
-}; // class OracleCatalogue
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/OracleCatalogueFactory.cpp b/catalogue/OracleCatalogueFactory.cpp
index 3df6517956..4347879d19 100644
--- a/catalogue/OracleCatalogueFactory.cpp
+++ b/catalogue/OracleCatalogueFactory.cpp
@@ -15,9 +15,13 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "catalogue/CatalogueRetryWrapper.hpp"
+#include <memory>
+#include <string>
+#include <utility>
+
 #include "catalogue/OracleCatalogueFactory.hpp"
-#include "catalogue/OracleCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleCatalogue.hpp"
+#include "catalogue/retrywrappers/CatalogueRetryWrapper.hpp"
 #include "common/exception/Exception.hpp"
 
 namespace cta {
@@ -37,7 +41,7 @@ OracleCatalogueFactory::OracleCatalogueFactory(
   m_nbConns(nbConns),
   m_nbArchiveFileListingConns(nbArchiveFileListingConns),
   m_maxTriesToConnect(maxTriesToConnect) {
-  if(rdbms::Login::DBTYPE_ORACLE != login.dbType) {
+  if (rdbms::Login::DBTYPE_ORACLE != login.dbType) {
     exception::Exception ex;
     ex.getMessage() << __FUNCTION__ << "failed: Incorrect database type: expected=DBTYPE_ORACLE actual=" <<
       login.dbTypeToString(login.dbType);
@@ -58,5 +62,5 @@ std::unique_ptr<Catalogue> OracleCatalogueFactory::create() {
   }
 }
 
-} // namespace catalogue
-} // namespace cta
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/OracleCatalogueFactory.hpp b/catalogue/OracleCatalogueFactory.hpp
index f4addb1f4b..8ebb524529 100644
--- a/catalogue/OracleCatalogueFactory.hpp
+++ b/catalogue/OracleCatalogueFactory.hpp
@@ -21,6 +21,11 @@
 #include "rdbms/Login.hpp"
 
 namespace cta {
+
+namespace log {
+class Logger;
+}
+
 namespace catalogue {
 
 /**
diff --git a/catalogue/PostgresCatalogue.cpp b/catalogue/PostgresCatalogue.cpp
deleted file mode 100644
index 031ab83fce..0000000000
--- a/catalogue/PostgresCatalogue.cpp
+++ /dev/null
@@ -1,1204 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#include <algorithm>
-
-#include "catalogue/ArchiveFileRow.hpp"
-#include "catalogue/CatalogueItor.hpp"
-#include "catalogue/InsertFileRecycleLog.hpp"
-#include "catalogue/PostgresCatalogue.hpp"
-#include "catalogue/retryOnLostConnection.hpp"
-#include "catalogue/TapeItemWrittenPointer.hpp"
-#include "common/dataStructures/DeleteArchiveRequest.hpp"
-#include "common/dataStructures/FileRecycleLog.hpp"
-#include "common/exception/Exception.hpp"
-#include "common/exception/FileSizeMismatch.hpp"
-#include "common/exception/LostDatabaseConnection.hpp"
-#include "common/exception/TapeFseqMismatch.hpp"
-#include "common/exception/UserError.hpp"
-#include "common/log/TimingList.hpp"
-#include "common/Timer.hpp"
-#include "common/utils/utils.hpp"
-#include "rdbms/AutoRollback.hpp"
-#include "rdbms/rdbms.hpp"
-#include "rdbms/wrapper/PostgresColumn.hpp"
-#include "rdbms/wrapper/PostgresStmt.hpp"
-
-namespace cta {
-namespace catalogue {
-
-namespace {
-  /**
-   * Structure used to assemble a batch of rows to insert into the TAPE_FILE
-   * table.
-   */
-  struct TapeFileBatch {
-    size_t nbRows;
-    rdbms::wrapper::PostgresColumn vid;
-    rdbms::wrapper::PostgresColumn fSeq;
-    rdbms::wrapper::PostgresColumn blockId;
-    rdbms::wrapper::PostgresColumn fileSize;
-    rdbms::wrapper::PostgresColumn copyNb;
-    rdbms::wrapper::PostgresColumn creationTime;
-    rdbms::wrapper::PostgresColumn archiveFileId;
-
-    /**
-     * Constructor.
-     *
-     * @param nbRowsValue  The Number of rows to be inserted.
-     */
-    TapeFileBatch(const size_t nbRowsValue):
-      nbRows(nbRowsValue),
-      vid("VID", nbRows),
-      fSeq("FSEQ", nbRows),
-      blockId("BLOCK_ID", nbRows),
-      fileSize("LOGICAL_SIZE_IN_BYTES", nbRows),
-      copyNb("COPY_NB", nbRows),
-      creationTime("CREATION_TIME", nbRows),
-      archiveFileId("ARCHIVE_FILE_ID", nbRows) {
-    }
-  }; // struct TapeFileBatch
-
-  /**
-   * Structure used to assemble a batch of rows to insert into the TEMP_ARCHIVE_FILE_BATCH
-   * table.
-   */
-  struct ArchiveFileBatch {
-    size_t nbRows;
-    rdbms::wrapper::PostgresColumn archiveFileId;
-    rdbms::wrapper::PostgresColumn diskInstance;
-    rdbms::wrapper::PostgresColumn diskFileId;
-    rdbms::wrapper::PostgresColumn diskFileUser;
-    rdbms::wrapper::PostgresColumn diskFileGroup;
-    rdbms::wrapper::PostgresColumn size;
-    rdbms::wrapper::PostgresColumn checksumBlob;
-    rdbms::wrapper::PostgresColumn checksumAdler32;
-    rdbms::wrapper::PostgresColumn storageClassName;
-    rdbms::wrapper::PostgresColumn creationTime;
-    rdbms::wrapper::PostgresColumn reconciliationTime;
-
-    /**
-     * Constructor.
-     *
-     * @param nbRowsValue  The Number of rows to be inserted.
-     */
-    ArchiveFileBatch(const size_t nbRowsValue):
-      nbRows(nbRowsValue),
-      archiveFileId("ARCHIVE_FILE_ID", nbRows),
-      diskInstance("DISK_INSTANCE_NAME", nbRows),
-      diskFileId("DISK_FILE_ID", nbRows),
-      diskFileUser("DISK_FILE_UID", nbRows),
-      diskFileGroup("DISK_FILE_GID", nbRows),
-      size("SIZE_IN_BYTES", nbRows),
-      checksumBlob("CHECKSUM_BLOB", nbRows),
-      checksumAdler32("CHECKSUM_ADLER32", nbRows),
-      storageClassName("STORAGE_CLASS_NAME", nbRows),
-      creationTime("CREATION_TIME", nbRows),
-      reconciliationTime("RECONCILIATION_TIME", nbRows) {
-    }
-  }; // struct ArchiveFileBatch
-
-  /**
-   * Structure used to assemble a batch of rows to insert into the
-   * TAPE_FILE_BATCH temporary table.
-   */
-  struct TempTapeFileBatch {
-    size_t nbRows;
-    rdbms::wrapper::PostgresColumn archiveFileId;
-
-    /**
-     * Constructor.
-     *
-     * @param nbRowsValue  The Number of rows to be inserted.
-     */
-    TempTapeFileBatch(const size_t nbRowsValue):
-      nbRows(nbRowsValue),
-      archiveFileId("ARCHIVE_FILE_ID", nbRows) {
-    }
-  }; // struct TempTapeFileBatch
-} // anonymous namespace
-
-//------------------------------------------------------------------------------
-// constructor
-//------------------------------------------------------------------------------
-PostgresCatalogue::PostgresCatalogue(
-  log::Logger &log,
-  const rdbms::Login &login,
-  const uint64_t nbConns,
-  const uint64_t nbArchiveFileListingConns):
-  RdbmsCatalogue(
-    log,
-    rdbms::Login(rdbms::Login::DBTYPE_POSTGRESQL,
-                 login.username, login.password, login.database,
-                 login.hostname, login.port),
-    nbConns,
-    nbArchiveFileListingConns) {
-}
-
-//------------------------------------------------------------------------------
-// destructor
-//------------------------------------------------------------------------------
-PostgresCatalogue::~PostgresCatalogue() {
-}
-
-//------------------------------------------------------------------------------
-// createAndPopulateTempTableFxid
-//------------------------------------------------------------------------------
-std::string PostgresCatalogue::createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const {
-  const std::string tempTableName = "TEMP_DISK_FXIDS";
-
-  if(diskFileIds) {
-    try {
-      std::string sql = "CREATE TEMPORARY TABLE " + tempTableName + "(DISK_FILE_ID VARCHAR(100))";
-      try {
-        conn.executeNonQuery(sql);
-      } catch(exception::Exception &ex) {
-        // Postgres does not drop temporary tables until the end of the session; trying to create another
-        // temporary table in the same unit test will fail. If this happens, truncate the table and carry on.
-        sql = "TRUNCATE TABLE " + tempTableName;
-        conn.executeNonQuery(sql);
-      }
-
-      sql = "INSERT INTO " + tempTableName + " VALUES(:DISK_FILE_ID)";
-      auto stmt = conn.createStmt(sql);
-      for(auto &diskFileId : diskFileIds.value()) {
-        stmt.bindString(":DISK_FILE_ID", diskFileId);
-        stmt.executeNonQuery();
-      }
-    } catch(exception::Exception &ex) {
-      ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-      throw;
-    }
-  }
-  return tempTableName;
-}
-
-//------------------------------------------------------------------------------
-// getNextArchiveFileId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextArchiveFileId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "select NEXTVAL('ARCHIVE_FILE_ID_SEQ') AS ARCHIVE_FILE_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("ARCHIVE_FILE_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextLogicalLibraryId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "select NEXTVAL('LOGICAL_LIBRARY_ID_SEQ') AS LOGICAL_LIBRARY_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("LOGICAL_LIBRARY_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextVirtualOrganizationId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "select NEXTVAL('VIRTUAL_ORGANIZATION_ID_SEQ') AS VIRTUAL_ORGANIZATION_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("VIRTUAL_ORGANIZATION_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextMediaTypeId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextMediaTypeId(rdbms::Conn &conn) {
-  try {
-    const char *const sql = "select NEXTVAL('MEDIA_TYPE_ID_SEQ') AS MEDIA_TYPE_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("MEDIA_TYPE_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextStorageClassId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextStorageClassId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "select NEXTVAL('STORAGE_CLASS_ID_SEQ') AS STORAGE_CLASS_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("STORAGE_CLASS_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextTapePoolId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextTapePoolId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "select NEXTVAL('TAPE_POOL_ID_SEQ') AS TAPE_POOL_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("TAPE_POOL_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextFileRecyleLogId
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::getNextFileRecyleLogId(rdbms::Conn &conn) {
-  try {
-    const char *const sql =
-      "select NEXTVAL('FILE_RECYCLE_LOG_ID_SEQ') AS FILE_RECYCLE_LOG_ID";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set is unexpectedly empty");
-    }
-    return rset.columnUint64("FILE_RECYCLE_LOG_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// selectTapeForUpdateAndGetNextFSeq
-//------------------------------------------------------------------------------
-uint64_t PostgresCatalogue::selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn, const std::string &vid) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "LAST_FSEQ AS LAST_FSEQ "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :VID "
-      "FOR UPDATE";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("The tape with VID " + vid + " does not exist"));
-    }
-
-    return rset.columnUint64("LAST_FSEQ");
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// filesWrittenToTape
-//------------------------------------------------------------------------------
-void PostgresCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) {
-  try {
-    if (events.empty()) {
-      return;
-    }
-
-    auto firstEventItor = events.begin();
-    const auto &firstEvent = **firstEventItor;
-    checkTapeItemWrittenFieldsAreSet(__FUNCTION__, firstEvent);
-    const time_t now = time(nullptr);
-    auto conn = m_connPool.getConn();
-    rdbms::AutoRollback autoRollback(conn);
-
-    // Start DB transaction and create temporary tables TEMP_ARCHIVE_FILE_BATCH and TEMP_TAPE_FILE_BATCH.
-    // These two tables will exist only for the duration of the transaction.
-    // Set deferrable for second (disk instance, disk file id) constraint of the ARCHIVE_FILE table
-    // to avoid violation in the case of concurrent inserts of a previously not existing archive file.
-    beginCreateTemporarySetDeferred(conn);
-
-    const uint64_t lastFSeq = selectTapeForUpdateAndGetLastFSeq(conn, firstEvent.vid);
-    uint64_t expectedFSeq = lastFSeq + 1;
-    uint64_t totalLogicalBytesWritten = 0;
-
-    // We have a mix of files and items. Only files will be recorded, but items
-    // allow checking fSeq coherency.
-    // determine the number of files
-    size_t filesCount=std::count_if(events.cbegin(), events.cend(),
-        [](const TapeItemWrittenPointer &e) -> bool {return typeid(*e)==typeid(TapeFileWritten);});
-    TapeFileBatch tapeFileBatch(filesCount);
-
-    std::set<TapeFileWritten> fileEvents;
-
-    for (const auto &eventP: events) {
-      // Check for all item types.
-      const auto &event = *eventP;
-      checkTapeItemWrittenFieldsAreSet(__FUNCTION__, event);
-
-      if (event.vid != firstEvent.vid) {
-        throw exception::Exception(std::string("VID mismatch: expected=") + firstEvent.vid + " actual=" + event.vid);
-      }
-
-      if (expectedFSeq != event.fSeq) {
-        exception::TapeFseqMismatch ex;
-        ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
-          event.fSeq;
-        throw ex;
-      }
-      expectedFSeq++;
-
-      try {
-        // If this is a file (as opposed to a placeholder), do the full processing.
-        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(event);
-
-        checkTapeFileWrittenFieldsAreSet(__FUNCTION__, fileEvent);
-
-        totalLogicalBytesWritten += fileEvent.size;
-
-        fileEvents.insert(fileEvent);
-      } catch (std::bad_cast&) {}
-    }
-
-    // Update the tape because all the necessary information is now available
-    auto lastEventItor = events.cend();
-    lastEventItor--;
-    const TapeItemWritten &lastEvent = **lastEventItor;
-    updateTape(conn, lastEvent.vid, lastEvent.fSeq, totalLogicalBytesWritten, filesCount, lastEvent.tapeDrive);
-
-    // If we had only placeholders and no file recorded, we are done (but we still commit the update of the tape's fSeq).
-    if (fileEvents.empty()) {
-      conn.commit();
-      return;
-    }
-
-    // Create the archive file entries, skipping those that already exist
-    // However we don't currently lock existing rows, so this transaction may
-    // still fail later, in the face of certain concurrent modifications such
-    // as the deletion of one of the existing archive files for which we are
-    // inserting another tape file.
-    idempotentBatchInsertArchiveFiles(conn, fileEvents);
-
-    insertTapeFileBatchIntoTempTable(conn, fileEvents);
-
-    // Verify that the archive file entries in the catalogue database agree with
-    // the tape file written events
-    const auto fileSizesAndChecksums = selectArchiveFileSizesAndChecksums(conn, fileEvents);
-    for (const auto &event: fileEvents) {
-      const auto fileSizeAndChecksumItor = fileSizesAndChecksums.find(event.archiveFileId);
-
-      std::ostringstream fileContext;
-      fileContext << "archiveFileId=" << event.archiveFileId << ", diskInstanceName=" << event.diskInstance <<
-        ", diskFileId=" << event.diskFileId;
-
-      // This should never happen
-      if(fileSizesAndChecksums.end() == fileSizeAndChecksumItor) {
-        exception::Exception ex;
-        ex.getMessage() << __FUNCTION__ << ": Failed to find archive file entry in the catalogue: " << fileContext.str();
-        throw ex;
-      }
-
-      const auto &fileSizeAndChecksum = fileSizeAndChecksumItor->second;
-
-      if(fileSizeAndChecksum.fileSize != event.size) {
-        catalogue::FileSizeMismatch ex;
-        ex.getMessage() << __FUNCTION__ << ": File size mismatch: expected=" << fileSizeAndChecksum.fileSize <<
-          ", actual=" << event.size << ": " << fileContext.str();
-        throw ex;
-      }
-
-      fileSizeAndChecksum.checksumBlob.validate(event.checksumBlob);
-    }
-
-    // Store the value of each field
-    uint32_t i = 0;
-    for (const auto &event: fileEvents) {
-      tapeFileBatch.vid.setFieldValue(i, event.vid);
-      tapeFileBatch.fSeq.setFieldValue(i, event.fSeq);
-      tapeFileBatch.blockId.setFieldValue(i, event.blockId);
-      tapeFileBatch.fileSize.setFieldValue(i, event.size);
-      tapeFileBatch.copyNb.setFieldValue(i, event.copyNb);
-      tapeFileBatch.creationTime.setFieldValue(i, now);
-      tapeFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
-      i++;
-    }
-
-    const char *const sql =
-    "CREATE TEMPORARY TABLE TEMP_TAPE_FILE_INSERTION_BATCH ("                        "\n"
-      "LIKE TAPE_FILE) "                                                             "\n"
-      "ON COMMIT DROP;"                                                              "\n"
-    "COPY TEMP_TAPE_FILE_INSERTION_BATCH("                                           "\n"
-      "VID,"                                                                         "\n"
-      "FSEQ,"                                                                        "\n"
-      "BLOCK_ID,"                                                                    "\n"
-      "LOGICAL_SIZE_IN_BYTES,"                                                       "\n"
-      "COPY_NB,"                                                                     "\n"
-      "CREATION_TIME,"                                                               "\n"
-      "ARCHIVE_FILE_ID) "                                                            "\n"
-    "FROM STDIN; --"                                                                 "\n"
-      "-- :VID,"                                                                     "\n"
-      "-- :FSEQ,"                                                                    "\n"
-      "-- :BLOCK_ID,"                                                                "\n"
-      "-- :LOGICAL_SIZE_IN_BYTES,"                                                   "\n"
-      "-- :COPY_NB,"                                                                 "\n"
-      "-- :CREATION_TIME,"                                                           "\n"
-      "-- :ARCHIVE_FILE_ID;"                                                         "\n";
-
-    auto stmt = conn.createStmt(sql);
-    rdbms::wrapper::PostgresStmt &postgresStmt = dynamic_cast<rdbms::wrapper::PostgresStmt &>(stmt.getStmt());
-    postgresStmt.setColumn(tapeFileBatch.vid);
-    postgresStmt.setColumn(tapeFileBatch.fSeq);
-    postgresStmt.setColumn(tapeFileBatch.blockId);
-    postgresStmt.setColumn(tapeFileBatch.fileSize);
-    postgresStmt.setColumn(tapeFileBatch.copyNb);
-    postgresStmt.setColumn(tapeFileBatch.creationTime);
-    postgresStmt.setColumn(tapeFileBatch.archiveFileId);
-
-    postgresStmt.executeCopyInsert(tapeFileBatch.nbRows);
-
-    auto recycledFiles = insertOldCopiesOfFilesIfAnyOnFileRecycleLog(conn);
-
-    {
-      //Insert the tapefiles from the TEMP_TAPE_FILE_INSERTION_BATCH
-      const char * const insertTapeFileSql =
-      "INSERT INTO TAPE_FILE (VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES,"            "\n"
-      "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID) "                                     "\n"
-      "SELECT VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES,"                            "\n"
-      "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID FROM TEMP_TAPE_FILE_INSERTION_BATCH;"  "\n";
-      conn.executeNonQuery(insertTapeFileSql);
-    }
-
-    for(auto & recycledFile: recycledFiles){
-      const char * const deleteTapeFileSql =
-      "DELETE FROM TAPE_FILE WHERE TAPE_FILE.VID = :VID AND TAPE_FILE.FSEQ = :FSEQ";
-      auto deleteTapeFileStmt = conn.createStmt(deleteTapeFileSql);
-      deleteTapeFileStmt.bindString(":VID",recycledFile.vid);
-      deleteTapeFileStmt.bindUint64(":FSEQ",recycledFile.fSeq);
-      deleteTapeFileStmt.executeNonQuery();
-    }
-
-    autoRollback.cancel();
-    conn.commit();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// idempotentBatchInsertArchiveFiles
-//------------------------------------------------------------------------------
-void PostgresCatalogue::idempotentBatchInsertArchiveFiles(rdbms::Conn &conn,
-   const std::set<TapeFileWritten> &events) const {
-  try {
-    ArchiveFileBatch archiveFileBatch(events.size());
-    const time_t now = time(nullptr);
-
-    // Store the value of each field
-    uint32_t i = 0;
-    for (const auto &event: events) {
-      archiveFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
-      archiveFileBatch.diskInstance.setFieldValue(i, event.diskInstance);
-      archiveFileBatch.diskFileId.setFieldValue(i, event.diskFileId);
-      archiveFileBatch.diskFileUser.setFieldValue(i, event.diskFileOwnerUid);
-      archiveFileBatch.diskFileGroup.setFieldValue(i, event.diskFileGid);
-      archiveFileBatch.size.setFieldValue(i, event.size);
-      archiveFileBatch.checksumBlob.setFieldByteA(conn, i, event.checksumBlob.serialize());
-      // Keep transition ADLER32 checksum up-to-date if it exists
-      std::string adler32str;
-      try {
-        std::string adler32hex = checksum::ChecksumBlob::ByteArrayToHex(event.checksumBlob.at(checksum::ADLER32));
-        uint32_t adler32 = strtoul(adler32hex.c_str(), 0, 16);
-        adler32str = std::to_string(adler32);
-      } catch(exception::ChecksumTypeMismatch &ex) {
-        adler32str = "0";
-      }
-      archiveFileBatch.checksumAdler32.setFieldValue(i, adler32str);
-      archiveFileBatch.storageClassName.setFieldValue(i, event.storageClassName);
-      archiveFileBatch.creationTime.setFieldValue(i, now);
-      archiveFileBatch.reconciliationTime.setFieldValue(i, now);
-      i++;
-    }
-
-    const char *const sql =
-      "COPY TEMP_ARCHIVE_FILE_BATCH("
-        "ARCHIVE_FILE_ID,"
-        "DISK_INSTANCE_NAME,"
-        "DISK_FILE_ID,"
-        "DISK_FILE_UID,"
-        "DISK_FILE_GID,"
-        "SIZE_IN_BYTES,"
-        "CHECKSUM_BLOB,"
-        "CHECKSUM_ADLER32,"
-        "STORAGE_CLASS_NAME,"
-        "CREATION_TIME,"
-        "RECONCILIATION_TIME) "
-      "FROM STDIN --"
-        ":ARCHIVE_FILE_ID,"
-        ":DISK_INSTANCE_NAME,"
-        ":DISK_FILE_ID,"
-        ":DISK_FILE_UID,"
-        ":DISK_FILE_GID,"
-        ":SIZE_IN_BYTES,"
-        ":CHECKSUM_BLOB,"
-        ":CHECKSUM_ADLER32,"
-        ":STORAGE_CLASS_NAME,"
-        ":CREATION_TIME,"
-        ":RECONCILIATION_TIME";
-
-    auto stmt = conn.createStmt(sql);
-    rdbms::wrapper::PostgresStmt &postgresStmt = dynamic_cast<rdbms::wrapper::PostgresStmt &>(stmt.getStmt());
-
-    postgresStmt.setColumn(archiveFileBatch.archiveFileId);
-    postgresStmt.setColumn(archiveFileBatch.diskInstance);
-    postgresStmt.setColumn(archiveFileBatch.diskFileId);
-    postgresStmt.setColumn(archiveFileBatch.diskFileUser);
-    postgresStmt.setColumn(archiveFileBatch.diskFileGroup);
-    postgresStmt.setColumn(archiveFileBatch.size);
-    postgresStmt.setColumn(archiveFileBatch.checksumBlob);
-    postgresStmt.setColumn(archiveFileBatch.checksumAdler32);
-    postgresStmt.setColumn(archiveFileBatch.storageClassName);
-    postgresStmt.setColumn(archiveFileBatch.creationTime);
-    postgresStmt.setColumn(archiveFileBatch.reconciliationTime);
-
-    postgresStmt.executeCopyInsert(archiveFileBatch.nbRows);
-
-    const char *const sql_insert =
-      "INSERT INTO ARCHIVE_FILE("
-        "ARCHIVE_FILE_ID,"
-  	"DISK_INSTANCE_NAME,"
-        "DISK_FILE_ID,"
-        "DISK_FILE_UID,"
-        "DISK_FILE_GID,"
-        "SIZE_IN_BYTES,"
-        "CHECKSUM_BLOB,"
-        "CHECKSUM_ADLER32,"
-        "STORAGE_CLASS_ID,"
-        "CREATION_TIME,"
-        "RECONCILIATION_TIME) "
-      "SELECT "
-        "A.ARCHIVE_FILE_ID,"
-        "A.DISK_INSTANCE_NAME,"
-        "A.DISK_FILE_ID,"
-        "A.DISK_FILE_UID,"
-        "A.DISK_FILE_GID,"
-        "A.SIZE_IN_BYTES,"
-        "A.CHECKSUM_BLOB,"
-        "A.CHECKSUM_ADLER32,"
-        "S.STORAGE_CLASS_ID,"
-        "A.CREATION_TIME,"
-        "A.RECONCILIATION_TIME "
-      "FROM TEMP_ARCHIVE_FILE_BATCH AS A, STORAGE_CLASS AS S "
-        "WHERE A.STORAGE_CLASS_NAME = S.STORAGE_CLASS_NAME "
-      "ORDER BY A.ARCHIVE_FILE_ID "
-      "ON CONFLICT (ARCHIVE_FILE_ID) DO NOTHING";
-
-    // Concerns for bulk insertion in archive_file: deadlock with concurrent
-    // inserts of previously not-existing entry for the same archive file,
-    // hence insert with ORDER BY to define an update order.
-
-    auto stmt_insert = conn.createStmt(sql_insert);
-    stmt_insert.executeNonQuery();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::list<cta::catalogue::InsertFileRecycleLog> PostgresCatalogue::insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn& conn){
-  std::list<cta::catalogue::InsertFileRecycleLog> fileRecycleLogsToInsert;
-  try {
-    //Get the TAPE_FILE entry to put on the file recycle log
-    {
-      const char *const sql =
-        "SELECT "
-          "TAPE_FILE.VID AS VID,"
-          "TAPE_FILE.FSEQ AS FSEQ,"
-          "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-          "TAPE_FILE.COPY_NB AS COPY_NB,"
-          "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
-          "TAPE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
-        "FROM "
-          "TAPE_FILE "
-        "JOIN "
-          "TEMP_TAPE_FILE_INSERTION_BATCH "
-        "ON "
-          "TEMP_TAPE_FILE_INSERTION_BATCH.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID AND TEMP_TAPE_FILE_INSERTION_BATCH.COPY_NB = TAPE_FILE.COPY_NB "
-        "WHERE "
-          "TAPE_FILE.VID != TEMP_TAPE_FILE_INSERTION_BATCH.VID OR TAPE_FILE.FSEQ != TEMP_TAPE_FILE_INSERTION_BATCH.FSEQ";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      while(rset.next()){
-        cta::catalogue::InsertFileRecycleLog fileRecycleLog;
-        fileRecycleLog.vid = rset.columnString("VID");
-        fileRecycleLog.fSeq = rset.columnUint64("FSEQ");
-        fileRecycleLog.blockId = rset.columnUint64("BLOCK_ID");
-        fileRecycleLog.copyNb = rset.columnUint8("COPY_NB");
-        fileRecycleLog.tapeFileCreationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        fileRecycleLog.archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
-        fileRecycleLog.reasonLog = InsertFileRecycleLog::getRepackReasonLog();
-        fileRecycleLog.recycleLogTime = time(nullptr);
-        fileRecycleLogsToInsert.push_back(fileRecycleLog);
-      }
-    }
-    {
-      for(auto & fileRecycleLog: fileRecycleLogsToInsert){
-        insertFileInFileRecycleLog(conn,fileRecycleLog);
-      }
-      return fileRecycleLogsToInsert;
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// selectArchiveFileSizeAndChecksum
-//------------------------------------------------------------------------------
-std::map<uint64_t, PostgresCatalogue::FileSizeAndChecksum> PostgresCatalogue::selectArchiveFileSizesAndChecksums(
-  rdbms::Conn &conn, const std::set<TapeFileWritten> &events) const {
-  try {
-    std::vector<uint64_t> archiveFileIdList(events.size());
-    for (const auto &event: events) {
-      archiveFileIdList.push_back(event.archiveFileId);
-    }
-
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32 "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN TEMP_TAPE_FILE_BATCH ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TEMP_TAPE_FILE_BATCH.ARCHIVE_FILE_ID";
-    auto stmt = conn.createStmt(sql);
-
-    auto rset = stmt.executeQuery();
-
-    std::map<uint64_t, FileSizeAndChecksum> fileSizesAndChecksums;
-    while (rset.next()) {
-      const uint64_t archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
-
-      if (fileSizesAndChecksums.end() != fileSizesAndChecksums.find(archiveFileId)) {
-        exception::Exception ex;
-        ex.getMessage() << __FUNCTION__ << " failed: "
-          "Found duplicate archive file identifier in batch of files written to tape: archiveFileId=" << archiveFileId;
-        throw ex;
-      }
-
-      FileSizeAndChecksum fileSizeAndChecksum;
-      fileSizeAndChecksum.fileSize = rset.columnUint64("SIZE_IN_BYTES");
-      fileSizeAndChecksum.checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-      fileSizesAndChecksums[archiveFileId] = fileSizeAndChecksum;
-    }
-
-    return fileSizesAndChecksums;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// insertArchiveFilesIntoTempTable
-//------------------------------------------------------------------------------
-void PostgresCatalogue::insertTapeFileBatchIntoTempTable(rdbms::Conn &conn,
-   const std::set<TapeFileWritten> &events) const {
-  try {
-    TempTapeFileBatch tempTapeFileBatch(events.size());
-
-    // Store the value of each field
-    uint32_t i = 0;
-    for (const auto &event: events) {
-      tempTapeFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
-      i++;
-    }
-
-    const char *const sql =
-      "COPY TEMP_TAPE_FILE_BATCH("
-        "ARCHIVE_FILE_ID) "
-      "FROM STDIN --"
-        ":ARCHIVE_FILE_ID";
-
-    auto stmt = conn.createStmt(sql);
-    rdbms::wrapper::PostgresStmt &postgresStmt = dynamic_cast<rdbms::wrapper::PostgresStmt &>(stmt.getStmt());
-
-    postgresStmt.setColumn(tempTapeFileBatch.archiveFileId);
-    postgresStmt.executeCopyInsert(tempTapeFileBatch.nbRows);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteArchiveFile
-//------------------------------------------------------------------------------
-void PostgresCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
-  log::LogContext &lc) {
-  try {
-    const char *selectSql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "WHERE "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID "
-      "FOR UPDATE OF ARCHIVE_FILE";
-    utils::Timer t;
-    auto conn = m_connPool.getConn();
-    rdbms::AutoRollback autoRollback(conn);
-    conn.executeNonQuery("BEGIN");
-
-    const auto getConnTime = t.secs(utils::Timer::resetCounter);
-    auto selectStmt = conn.createStmt(selectSql);
-    const auto createStmtTime = t.secs();
-    selectStmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    t.reset();
-    rdbms::Rset selectRset = selectStmt.executeQuery();
-    const auto selectFromArchiveFileTime = t.secs();
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
-    std::set<std::string> vidsToSetDirty;
-    while(selectRset.next()) {
-      if(nullptr == archiveFile.get()) {
-        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
-
-        archiveFile->archiveFileID = selectRset.columnUint64("ARCHIVE_FILE_ID");
-        archiveFile->diskInstance = selectRset.columnString("DISK_INSTANCE_NAME");
-        archiveFile->diskFileId = selectRset.columnString("DISK_FILE_ID");
-        archiveFile->diskFileInfo.owner_uid = selectRset.columnUint64("DISK_FILE_UID");
-        archiveFile->diskFileInfo.gid = selectRset.columnUint64("DISK_FILE_GID");
-        archiveFile->fileSize = selectRset.columnUint64("SIZE_IN_BYTES");
-        archiveFile->checksumBlob.deserializeOrSetAdler32(selectRset.columnBlob("CHECKSUM_BLOB"), selectRset.columnUint64("CHECKSUM_ADLER32"));
-        archiveFile->storageClass = selectRset.columnString("STORAGE_CLASS_NAME");
-        archiveFile->creationTime = selectRset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-        archiveFile->reconciliationTime = selectRset.columnUint64("RECONCILIATION_TIME");
-      }
-
-      // If there is a tape file
-      if(!selectRset.columnIsNull("VID")) {
-        // Add the tape file to the archive file's in-memory structure
-        common::dataStructures::TapeFile tapeFile;
-        tapeFile.vid = selectRset.columnString("VID");
-        vidsToSetDirty.insert(tapeFile.vid);
-        tapeFile.fSeq = selectRset.columnUint64("FSEQ");
-        tapeFile.blockId = selectRset.columnUint64("BLOCK_ID");
-        tapeFile.fileSize = selectRset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-        tapeFile.copyNb = selectRset.columnUint64("COPY_NB");
-        tapeFile.creationTime = selectRset.columnUint64("TAPE_FILE_CREATION_TIME");
-        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
-
-        archiveFile->tapeFiles.push_back(tapeFile);
-      }
-    }
-
-    if(nullptr == archiveFile.get()) {
-      log::ScopedParamContainer spc(lc);
-      spc.add("fileId", archiveFileId);
-      lc.log(log::WARNING, "Ignoring request to delete archive file because it does not exist in the catalogue");
-      return;
-    }
-
-    if(diskInstanceName != archiveFile->diskInstance) {
-      log::ScopedParamContainer spc(lc);
-      spc.add("fileId", std::to_string(archiveFile->archiveFileID))
-         .add("diskInstance", archiveFile->diskInstance)
-         .add("requestDiskInstance", diskInstanceName)
-         .add("diskFileId", archiveFile->diskFileId)
-         .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
-         .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
-         .add("fileSize", std::to_string(archiveFile->fileSize))
-         .add("creationTime", std::to_string(archiveFile->creationTime))
-         .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
-         .add("storageClass", archiveFile->storageClass)
-         .add("getConnTime", getConnTime)
-         .add("createStmtTime", createStmtTime)
-         .add("selectFromArchiveFileTime", selectFromArchiveFileTime);
-      archiveFile->checksumBlob.addFirstChecksumToLog(spc);
-      for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
-        std::stringstream tapeCopyLogStream;
-        tapeCopyLogStream << "copy number: " << it->copyNb
-          << " vid: " << it->vid
-          << " fSeq: " << it->fSeq
-          << " blockId: " << it->blockId
-          << " creationTime: " << it->creationTime
-          << " fileSize: " << it->fileSize
-          << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
-          << " copyNb: " << it->copyNb;
-        spc.add("TAPE FILE", tapeCopyLogStream.str());
-      }
-      lc.log(log::WARNING, "Failed to delete archive file because the disk instance of the request does not match that "
-        "of the archived file");
-
-      exception::UserError ue;
-      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because the disk instance of "
-        "the request does not match that of the archived file: archiveFileId=" << archiveFileId << " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" <<
-        archiveFile->diskInstance;
-      throw ue;
-    }
-
-    t.reset();
-    {
-      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-      stmt.executeNonQuery();
-    }
-
-    const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter);
-
-    for(auto &vidToSetDirty: vidsToSetDirty){
-      //We deleted the TAPE_FILE so the tapes containing them should be set as dirty
-      setTapeDirty(conn,vidToSetDirty);
-    }
-
-    const auto setTapeDirtyTime = t.secs(utils::Timer::resetCounter);
-
-    {
-      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-      stmt.executeNonQuery();
-    }
-    const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter);
-
-    conn.commit();
-    autoRollback.cancel();
-    const auto commitTime = t.secs();
-
-    log::ScopedParamContainer spc(lc);
-    spc.add("fileId", std::to_string(archiveFile->archiveFileID))
-       .add("diskInstance", archiveFile->diskInstance)
-       .add("diskFileId", archiveFile->diskFileId)
-       .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
-       .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
-       .add("fileSize", std::to_string(archiveFile->fileSize))
-       .add("creationTime", std::to_string(archiveFile->creationTime))
-       .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
-       .add("storageClass", archiveFile->storageClass)
-       .add("getConnTime", getConnTime)
-       .add("createStmtTime", createStmtTime)
-       .add("selectFromArchiveFileTime", selectFromArchiveFileTime)
-       .add("deleteFromTapeFileTime", deleteFromTapeFileTime)
-       .add("deleteFromArchiveFileTime", deleteFromArchiveFileTime)
-       .add("setTapeDirtyTime",setTapeDirtyTime)
-       .add("commitTime", commitTime);
-    archiveFile->checksumBlob.addFirstChecksumToLog(spc);
-    for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
-      std::stringstream tapeCopyLogStream;
-      tapeCopyLogStream << "copy number: " << it->copyNb
-        << " vid: " << it->vid
-        << " fSeq: " << it->fSeq
-        << " blockId: " << it->blockId
-        << " creationTime: " << it->creationTime
-        << " fileSize: " << it->fileSize
-        << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
-        << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
-      spc.add("TAPE FILE", tapeCopyLogStream.str());
-    }
-    lc.log(log::INFO, "Archive file deleted from CTA catalogue");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// beginCreateTemporarySetDeferred
-//------------------------------------------------------------------------------
-void PostgresCatalogue::beginCreateTemporarySetDeferred(rdbms::Conn &conn) const {
-  conn.executeNonQuery("BEGIN");
-  conn.executeNonQuery("CREATE TEMPORARY TABLE TEMP_ARCHIVE_FILE_BATCH (LIKE ARCHIVE_FILE) ON COMMIT DROP");
-  conn.executeNonQuery("ALTER TABLE TEMP_ARCHIVE_FILE_BATCH ADD COLUMN STORAGE_CLASS_NAME VARCHAR(100)");
-  conn.executeNonQuery("ALTER TABLE TEMP_ARCHIVE_FILE_BATCH ALTER COLUMN STORAGE_CLASS_ID DROP NOT NULL");
-  conn.executeNonQuery("ALTER TABLE TEMP_ARCHIVE_FILE_BATCH ALTER COLUMN IS_DELETED DROP NOT NULL");
-  conn.executeNonQuery("CREATE INDEX TEMP_A_F_B_ARCHIVE_FILE_ID_I ON TEMP_ARCHIVE_FILE_BATCH(ARCHIVE_FILE_ID)");
-  conn.executeNonQuery("CREATE INDEX TEMP_A_F_B_DIN_SCN_I ON TEMP_ARCHIVE_FILE_BATCH(DISK_INSTANCE_NAME, STORAGE_CLASS_NAME)");
-  conn.executeNonQuery("CREATE TEMPORARY TABLE TEMP_TAPE_FILE_BATCH(ARCHIVE_FILE_ID NUMERIC(20,0)) ON COMMIT DROP");
-  conn.executeNonQuery("CREATE INDEX TEMP_T_F_B_ARCHIVE_FILE_ID_I ON TEMP_TAPE_FILE_BATCH(ARCHIVE_FILE_ID)");
-  conn.executeNonQuery("SET CONSTRAINTS ARCHIVE_FILE_DIN_DFI_UN DEFERRED");
-}
-
-//------------------------------------------------------------------------------
-// copyArchiveFileToRecycleBinAndDelete
-//------------------------------------------------------------------------------
-void PostgresCatalogue::copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do an INSERT INTO and a DELETE FROM
-    //in a single transaction
-    conn.executeNonQuery("BEGIN");
-    copyArchiveFileToFileRecycleLog(conn,request);
-    tl.insertAndReset("insertToRecycleBinTime",t);
-    setTapeDirty(conn,request.archiveFileID);
-    tl.insertAndReset("setTapeDirtyTime",t);
-    deleteTapeFiles(conn,request);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    RdbmsCatalogue::deleteArchiveFile(conn,request);
-    tl.insertAndReset("deleteArchiveFileTime",t);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId",request.archiveFileID);
-    spc.add("diskFileId",request.diskFileId);
-    spc.add("diskFilePath",request.diskFilePath);
-    spc.add("diskInstance",request.diskInstance);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In PostgresCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapeFilesAndArchiveFileFromRecycleBin
-//------------------------------------------------------------------------------
-void PostgresCatalogue::deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn& conn, const uint64_t archiveFileId, log::LogContext& lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do two delete in one transaction
-    conn.executeNonQuery("BEGIN");
-    deleteTapeFilesFromRecycleBin(conn,archiveFileId);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    deleteArchiveFileFromRecycleBin(conn,archiveFileId);
-    tl.insertAndReset("deleteArchiveFileTime",t);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId",archiveFileId);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In PostgresCatalogue::deleteTapeFilesAndArchiveFileFromRecycleBin: tape files and archiveFiles deleted from the recycle-bin.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// copyTapeFileToFileRecyleLogAndDelete
-//------------------------------------------------------------------------------
-void PostgresCatalogue::copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file,
-                                                            const std::string &reason, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do an INSERT INTO and a DELETE FROM
-    //in a single transaction
-    conn.executeNonQuery("BEGIN");
-    copyTapeFilesToFileRecycleLog(conn, file, reason);
-    tl.insertAndReset("insertToRecycleBinTime",t);
-    setTapeDirty(conn, file.archiveFileID);
-    tl.insertAndReset("setTapeDirtyTime",t);
-    deleteTapeFiles(conn,file);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId", file.archiveFileID);
-    spc.add("diskFileId", file.diskFileId);
-    spc.add("diskFilePath", file.diskFileInfo.path);
-    spc.add("diskInstance", file.diskInstance);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In PostgresCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreEntryInRecycleLog
-//------------------------------------------------------------------------------
-void PostgresCatalogue::restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor,
-  const std::string &newFid, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-
-    if (!fileRecycleLogItor.hasMore()) {
-      throw cta::exception::UserError("No file in the recycle bin matches the parameters passed");
-    }
-    auto fileRecycleLog = fileRecycleLogItor.next();
-    if (fileRecycleLogItor.hasMore()) {
-      //stop restoring more than one file at once
-      throw cta::exception::UserError("More than one recycle bin file matches the parameters passed");
-    }
-
-    //We currently do all file copies restoring in a single transaction
-    conn.executeNonQuery("BEGIN");
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFilePtr = getArchiveFileById(conn, fileRecycleLog.archiveFileId);
-    if (!archiveFilePtr) {
-      restoreArchiveFileInRecycleLog(conn, fileRecycleLog, newFid, lc);
-    } else {
-      if (archiveFilePtr->tapeFiles.find(fileRecycleLog.copyNb) != archiveFilePtr->tapeFiles.end()) {
-        //copy with same copy_nb exists, cannot restore
-        UserSpecifiedExistingDeletedFileCopy ex;
-        ex.getMessage() << "Cannot restore file copy with archiveFileId " << std::to_string(fileRecycleLog.archiveFileId)
-        << " and copy_nb " << std::to_string(fileRecycleLog.copyNb) << " because a tapefile with same archiveFileId and copy_nb already exists";
-        throw ex;
-      }
-    }
-
-    restoreFileCopyInRecycleLog(conn, fileRecycleLog, lc);
-    conn.commit();
-
-    log::ScopedParamContainer spc(lc);
-    tl.insertAndReset("commitTime",t);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In PostgresCatalogue::restoreEntryInRecycleLog: all file copies successfully restored.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreFileCopyInRecycleLog
-//------------------------------------------------------------------------------
-void PostgresCatalogue::restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLog, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    cta::common::dataStructures::TapeFile tapeFile;
-    tapeFile.vid = fileRecycleLog.vid;
-    tapeFile.fSeq = fileRecycleLog.fSeq;
-    tapeFile.copyNb = fileRecycleLog.copyNb;
-    tapeFile.blockId = fileRecycleLog.blockId;
-    tapeFile.fileSize = fileRecycleLog.sizeInBytes;
-    tapeFile.creationTime = fileRecycleLog.tapeFileCreationTime;
-
-    insertTapeFile(conn, tapeFile, fileRecycleLog.archiveFileId);
-    tl.insertAndReset("insertTapeFileTime",t);
-
-    deleteTapeFileCopyFromRecycleBin(conn, fileRecycleLog);
-    tl.insertAndReset("deleteTapeFileCopyFromRecycleBinTime",t);
-
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", tapeFile.vid);
-    spc.add("archiveFileId", fileRecycleLog.archiveFileId);
-    spc.add("fSeq", tapeFile.fSeq);
-    spc.add("copyNb", tapeFile.copyNb);
-    spc.add("fileSize", tapeFile.fileSize);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In PostgresCatalogue::restoreFileCopyInRecycleLog: File restored from the recycle log.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/PostgresCatalogue.hpp b/catalogue/PostgresCatalogue.hpp
deleted file mode 100644
index a3932aebfe..0000000000
--- a/catalogue/PostgresCatalogue.hpp
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-#include "catalogue/RdbmsCatalogue.hpp"
-#include "rdbms/Conn.hpp"
-#include "InsertFileRecycleLog.hpp"
-
-namespace cta {
-namespace catalogue {
-
-/**
- * An Postgres based implementation of the CTA catalogue.
- */
-class PostgresCatalogue: public RdbmsCatalogue {
-public:
-
-  /**
-   * Constructor.
-   *
-   * @param log Object representing the API to the CTA logging system.
-   * @param username The database username.
-   * @param password The database password.
-   * @param database The database name.
-   * @param nbConns The maximum number of concurrent connections to the
-   * underlying relational database for all operations accept listing archive
-   * files which can be relatively long operations.
-   * @param nbArchiveFileListingConns The maximum number of concurrent
-   * connections to the underlying relational database for the sole purpose of
-   * listing archive files.
-   */
-  PostgresCatalogue(
-    log::Logger &log,
-    const rdbms::Login &login,
-    const uint64_t nbConns,
-    const uint64_t nbArchiveFileListingConns);
-
-  /**
-   * Destructor.
-   */
-  ~PostgresCatalogue() override;
-
-  /**
-   * !!!!!!!!!!!!!!!!!!! THIS METHOD SHOULD NOT BE USED !!!!!!!!!!!!!!!!!!!!!!!
-   * Deletes the specified archive file and its associated tape copies from the
-   * catalogue.
-   *
-   * Please note that the name of the disk instance is specified in order to
-   * prevent a disk instance deleting an archive file that belongs to another
-   * disk instance.
-   *
-   * Please note that this method is idempotent.  If the file to be deleted does
-   * not exist in the CTA catalogue then this method returns without error.
-   *
-   * @param instanceName The name of the instance from where the deletion request
-   * originated
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param lc The log context.
-   * @return The metadata of the deleted archive file including the metadata of
-   * the associated and also deleted tape copies.
-   */
-  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &instanceName, const uint64_t archiveFileId,
-    log::LogContext &lc) override;
-
-  /**
-   * Notifies the catalogue that the specified files have been written to tape.
-   *
-   * @param events The tape file written events.
-   */
-  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) override;
-
-  /**
-   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
-   *
-   * @param conn The database connection.
-   * @param diskFileIds List of disk file IDs (fxid).
-   * @return Name of the temporary table
-   */
-  std::string createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const override;
-
-  /**
-   * Returns a unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return A unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   */
-  uint64_t getNextArchiveFileId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique logical library ID that can be used by a new logical
-   * library within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique logical library ID that can be used by a new logical
-   * library storage class within the catalogue.
-   */
-  uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   * 
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   * 
-   * @param conn The database connection
-   * @return a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   */
-  uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique media type ID that can be used by a new media type within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique media type ID that can be used by a new media type
-   * within the catalogue.
-   */
-  uint64_t getNextMediaTypeId(rdbms::Conn &conn) override;
-  
-  /**
-   * Returns a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   */
-  uint64_t getNextStorageClassId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   */
-  uint64_t getNextTapePoolId(rdbms::Conn &conn) override;
-  
-  /**
-   * Returns a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   */
-  uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) override;
-
-private:
-
-  /**
-   * Start a database transaction and then create the temporary
-   * tables TEMP_ARCHIVE_FILE_BATCH and TEMP_TAPE_FILE_BATCH.
-   * Sets deferred mode for one of the db constraints to avoid
-   * violations during concurrent bulk insert.
-   *
-   * @parm conn The database connection.
-   */
-  void beginCreateTemporarySetDeferred(rdbms::Conn &conn) const;
-
-  /**
-   * Selects the specified tape for update and returns its last FSeq.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @param The last FSeq of the tape.
-   */
-  uint64_t selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn, const std::string &vid) const;
-
-  /**
-   * Batch inserts rows into the ARCHIVE_FILE table that correspond to the
-   * specified TapeFileWritten events.
-   *
-   * This method has idempotent behaviour in the case where an ARCHIVE_FILE
-   * already exists.  Such a situation will occur when a file has more than one
-   * copy on tape.  The first tape copy will cause two successful inserts, one
-   * into the ARCHIVE_FILE table and one into the  TAPE_FILE table.  The second
-   * tape copy will try to do the same, but the insert into the ARCHIVE_FILE
-   * table will fail or simply bounce as the row will already exists.  The
-   * insert into the TABLE_FILE table will succeed because the two TAPE_FILE
-   * rows will be unique.
-   *
-   * @param conn The database connection.
-   * @param events The tape file written events.
-   */
-  void idempotentBatchInsertArchiveFiles(rdbms::Conn &conn, const std::set<TapeFileWritten> &events) const;
-
-  /**
-   * In the case we insert a TAPE_FILE that already has a copy on the catalogue (same copyNb),
-   * this TAPE_FILE will go to the FILE_RECYCLE_LOG table.
-   * 
-   * This case happens always during the repacking of a tape: the new TAPE_FILE created 
-   * will replace the old one, the old one will then be moved to the FILE_RECYCLE_LOG table
-   * 
-   * @param conn The database connection.
-   * @returns the list of inserted fileRecycleLog
-   */
-  std::list<cta::catalogue::InsertFileRecycleLog> insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn & conn);
-  
-  /**
-   * The size and checksum of a file.
-   */
-  struct FileSizeAndChecksum {
-    uint64_t fileSize;
-    checksum::ChecksumBlob checksumBlob;
-  };
-
-  /**
-   * Returns the sizes and checksums of the specified archive files.
-   *
-   * @param conn The database connection.
-   * @param events The tape file written events that identify the archive files.
-   * @return A map from the identifier of each archive file to its size and checksum.
-   */
-  std::map<uint64_t, FileSizeAndChecksum> selectArchiveFileSizesAndChecksums(rdbms::Conn &conn,
-    const std::set<TapeFileWritten> &events) const;
-
-  /**
-   * Batch inserts rows into the TAPE_FILE_BATCH temporary table that correspond
-   * to the specified TapeFileWritten events.
-   *
-   * @param conn The database connection.
-   * @param events The tape file written events.
-   */
-  void insertTapeFileBatchIntoTempTable(rdbms::Conn &conn, const std::set<TapeFileWritten> &events) const;
-  
-  /**
-   * Copy the archiveFile and the associated tape files from the ARCHIVE_FILE and TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the ARCHIVE_FILE and TAPE_FILE entries.
-   * @param conn the database connection
-   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the FILE_RECYCLE_LOG table
-   * @param lc the log context
-   */
-  void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
-  
-  /**
-   * Delete the TapeFiles and the ArchiveFile from the recycle-bin in one transaction
-   * @param conn the database connection
-   * @param archiveFileId the archiveFileId of the file to delete from the recycle-bin
-   */
-  virtual void deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn & conn, const uint64_t archiveFileId, log::LogContext & lc);
-  
-  /**
-   * Copy the tape files from the TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the TAPE_FILE entry.
-   * @param conn the database connection
-   * @param file the file to be deleted
-   * @param reason The reason for deleting the tape file copy
-   * @param lc the log context
-   */
-  void copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file, 
-                                            const std::string &reason, log::LogContext & lc) override;
-
-  /**
-   * Copy the files in fileRecycleLogItor to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entries
-   * @param conn the database connection
-   * @param fileRecycleLogItor the collection of fileRecycleLogs we want to restore
-   * @param lc the log context
-   */
-  void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) override;
-
-  /**
-   * Copy the fileRecycleLog to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entry
-   * @param conn the database connection
-   * @param fileRecycleLog the fileRecycleLog we want to restore
-   * @param lc the log context
-   */
-  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLogItor, log::LogContext & lc);
-
-}; // class PostgresCatalogue
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/PostgresqlCatalogueFactory.cpp b/catalogue/PostgresqlCatalogueFactory.cpp
index f5443a38b4..3b8cb9253b 100644
--- a/catalogue/PostgresqlCatalogueFactory.cpp
+++ b/catalogue/PostgresqlCatalogueFactory.cpp
@@ -15,10 +15,12 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "catalogue/CatalogueRetryWrapper.hpp"
 #include "catalogue/PostgresqlCatalogueFactory.hpp"
-#include "catalogue/PostgresCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresCatalogue.hpp"
+#include "catalogue/retrywrappers/CatalogueRetryWrapper.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
 #include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
 
 namespace cta {
 namespace catalogue {
@@ -40,7 +42,7 @@ PostgresqlCatalogueFactory::PostgresqlCatalogueFactory(
   if(rdbms::Login::DBTYPE_POSTGRESQL != login.dbType) {
     exception::Exception ex;
     ex.getMessage() << __FUNCTION__ << "failed: Incorrect database type: expected=DBTYPE_POSTGRESQL actual=" <<
-      login.dbTypeToString(login.dbType);
+      rdbms::Login::dbTypeToString(login.dbType);
     throw ex;
   }
 }
diff --git a/catalogue/PostgresqlCatalogueFactory.hpp b/catalogue/PostgresqlCatalogueFactory.hpp
index 6669f423ff..1314d68512 100644
--- a/catalogue/PostgresqlCatalogueFactory.hpp
+++ b/catalogue/PostgresqlCatalogueFactory.hpp
@@ -21,6 +21,15 @@
 #include "rdbms/Login.hpp"
 
 namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace rdbms {
+class Login;
+}
+
 namespace catalogue {
 
 /**
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
deleted file mode 100644
index 64b895c93a..0000000000
--- a/catalogue/RdbmsCatalogue.cpp
+++ /dev/null
@@ -1,12101 +0,0 @@
- /*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#include <algorithm>
-#include <climits>
-#include <ctype.h>
-#include <list>
-#include <memory>
-#include <string>
-#include <time.h>
-#include <tuple>
-#include <utility>
-
-#include "catalogue/ArchiveFileRow.hpp"
-#include "catalogue/ArchiveFileRowWithoutTimestamps.hpp"
-#include "catalogue/CatalogueItor.hpp"
-#include "catalogue/CreateMountPolicyAttributes.hpp"
-#include "catalogue/CreateTapeAttributes.hpp"
-#include "catalogue/MediaType.hpp"
-#include "catalogue/MediaTypeWithLogs.hpp"
-#include "catalogue/RdbmsCatalogue.hpp"
-#include "catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp"
-#include "catalogue/RdbmsCatalogueGetArchiveFilesItor.hpp"
-#include "catalogue/RdbmsCatalogueTapeContentsItor.hpp"
-#include "catalogue/RecyleTapeFileSearchCriteria.hpp"
-#include "catalogue/SchemaVersion.hpp"
-#include "catalogue/SqliteCatalogueSchema.hpp"
-#include "catalogue/TapeForWriting.hpp"
-#include "catalogue/TapePool.hpp"
-#include "common/dataStructures/AdminUser.hpp"
-#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
-#include "common/dataStructures/ArchiveFileSummary.hpp"
-#include "common/dataStructures/ArchiveRoute.hpp"
-#include "common/dataStructures/DeleteArchiveRequest.hpp"
-#include "common/dataStructures/DesiredDriveState.hpp"
-#include "common/dataStructures/DiskInstance.hpp"
-#include "common/dataStructures/EntryLog.hpp"
-#include "common/dataStructures/FileRecycleLog.hpp"
-#include "common/dataStructures/LabelFormat.hpp"
-#include "common/dataStructures/LogicalLibrary.hpp"
-#include "common/dataStructures/RequesterActivityMountRule.hpp"
-#include "common/dataStructures/RequesterGroupMountRule.hpp"
-#include "common/dataStructures/RequesterMountRule.hpp"
-#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
-#include "common/dataStructures/SecurityIdentity.hpp"
-#include "common/dataStructures/StorageClass.hpp"
-#include "common/dataStructures/TapeDrive.hpp"
-#include "common/dataStructures/TapeDriveStatistics.hpp"
-#include "common/dataStructures/TapeFile.hpp"
-#include "common/exception/Exception.hpp"
-#include "common/exception/LostDatabaseConnection.hpp"
-#include "common/exception/UserError.hpp"
-#include "common/exception/UserErrorWithCacheInfo.hpp"
-#include "common/log/TimingList.hpp"
-#include "common/threading/MutexLocker.hpp"
-#include "common/Timer.hpp"
-#include "common/utils/Regex.hpp"
-#include "common/utils/utils.hpp"
-#include "disk/DiskSystem.hpp"
-#include "rdbms/AutoRollback.hpp"
-#include "RdbmsCatalogueGetFileRecycleLogItor.hpp"
-#include "version.h"
-
-namespace cta {
-namespace catalogue {
-
-//------------------------------------------------------------------------------
-// constructor
-//------------------------------------------------------------------------------
-RdbmsCatalogue::RdbmsCatalogue(
-  log::Logger &log,
-  const rdbms::Login &login,
-  const uint64_t nbConns,
-  const uint64_t nbArchiveFileListingConns):
-  m_log(log),
-  m_connPool(login, nbConns),
-  m_archiveFileListingConnPool(login, nbArchiveFileListingConns),
-  m_tapeCopyToPoolCache(10),
-  m_groupMountPolicyCache(10),
-  m_userMountPolicyCache(10),
-  m_allMountPoliciesCache(60),
-  m_tapepoolVirtualOrganizationCache(60),
-  m_expectedNbArchiveRoutesCache(10),
-  m_isAdminCache(10) {}
-
-//------------------------------------------------------------------------------
-// destructor
-//------------------------------------------------------------------------------
-RdbmsCatalogue::~RdbmsCatalogue() {
-}
-
-//------------------------------------------------------------------------------
-// createAdminUser
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createAdminUser(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &username,
-  const std::string &comment) {
-  try {
-    if (username.empty()) {
-      throw UserSpecifiedAnEmptyStringUsername("Cannot create admin user because the username is an empty string");
-    }
-
-    if (comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create admin user because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    auto conn = m_connPool.getConn();
-    if (adminUserExists(conn, username)) {
-      throw exception::UserError(std::string("Cannot create admin user " + username +
-        " because an admin user with the same name already exists"));
-    }
-    const uint64_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO ADMIN_USER("
-        "ADMIN_USER_NAME,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":ADMIN_USER_NAME,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":ADMIN_USER_NAME", username);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// adminUserExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::adminUserExists(rdbms::Conn &conn, const std::string adminUsername) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ADMIN_USER_NAME AS ADMIN_USER_NAME "
-      "FROM "
-        "ADMIN_USER "
-      "WHERE "
-        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":ADMIN_USER_NAME", adminUsername);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// virtualOrganizationExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::virtualOrganizationExists(rdbms::Conn &conn, const std::string &voName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
-      "FROM "
-        "VIRTUAL_ORGANIZATION "
-      "WHERE "
-        "UPPER(VIRTUAL_ORGANIZATION_NAME) = UPPER(:VIRTUAL_ORGANIZATION_NAME)";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteAdminUser
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteAdminUser(const std::string &username) {
-  try {
-    const char *const sql = "DELETE FROM ADMIN_USER WHERE ADMIN_USER_NAME = :ADMIN_USER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":ADMIN_USER_NAME", username);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete admin-user ") + username + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getAdminUsers
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::AdminUser> RdbmsCatalogue::getAdminUsers() const {
-  try {
-    std::list<common::dataStructures::AdminUser> admins;
-    const char *const sql =
-      "SELECT "
-        "ADMIN_USER_NAME AS ADMIN_USER_NAME,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "ADMIN_USER "
-      "ORDER BY "
-        "ADMIN_USER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::AdminUser admin;
-
-      admin.name = rset.columnString("ADMIN_USER_NAME");
-      admin.comment = rset.columnString("USER_COMMENT");
-      admin.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      admin.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      admin.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      admin.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      admin.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      admin.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      admins.push_back(admin);
-    }
-
-    return admins;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyAdminUserComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &username, const std::string &comment) {
-  try {
-    if (username.empty()) {
-      throw UserSpecifiedAnEmptyStringUsername("Cannot modify admin user because the username is an empty string");
-    }
-
-    if (comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot modify admin user because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE ADMIN_USER SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":ADMIN_USER_NAME", username);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify admin user ") + username + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createVirtualOrganization
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo){
-  try {
-    if (vo.name.empty()){
-      throw UserSpecifiedAnEmptyStringVo("Cannot create virtual organization because the name is an empty string");
-    }
-    if (vo.comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create virtual organization because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(vo.comment);
-    if (vo.diskInstanceName.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create virtual organization because the disk instance is an empty string");
-    }
-
-    auto conn = m_connPool.getConn();
-    if (virtualOrganizationExists(conn, vo.name)) {
-      throw exception::UserError(std::string("Cannot create vo : ") +
-        vo.name + " because it already exists");
-    }
-    const uint64_t virtualOrganizationId = getNextVirtualOrganizationId(conn);
-    const time_t now = time(nullptr);
-    const char *const sql =
-    "INSERT INTO VIRTUAL_ORGANIZATION("
-        "VIRTUAL_ORGANIZATION_ID,"
-        "VIRTUAL_ORGANIZATION_NAME,"
-
-        "READ_MAX_DRIVES,"
-        "WRITE_MAX_DRIVES,"
-        "MAX_FILE_SIZE,"
-
-        "DISK_INSTANCE_NAME,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":VIRTUAL_ORGANIZATION_ID,"
-        ":VIRTUAL_ORGANIZATION_NAME,"
-        ":READ_MAX_DRIVES,"
-        ":WRITE_MAX_DRIVES,"
-        ":MAX_FILE_SIZE,"
-
-        ":DISK_INSTANCE_NAME,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindUint64(":VIRTUAL_ORGANIZATION_ID", virtualOrganizationId);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", vo.name);
-
-    stmt.bindUint64(":READ_MAX_DRIVES",vo.readMaxDrives);
-    stmt.bindUint64(":WRITE_MAX_DRIVES",vo.writeMaxDrives);
-    stmt.bindUint64(":MAX_FILE_SIZE", vo.maxFileSize);
-
-    stmt.bindString(":DISK_INSTANCE_NAME", vo.diskInstanceName);
-
-    stmt.bindString(":USER_COMMENT", vo.comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-
-    m_tapepoolVirtualOrganizationCache.invalidate();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteVirtualOrganization
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteVirtualOrganization(const std::string &voName){
-  try {
-    auto conn = m_connPool.getConn();
-
-    if(virtualOrganizationIsUsedByStorageClasses(conn, voName)) {
-      throw UserSpecifiedStorageClassUsedByArchiveRoutes(std::string("The Virtual Organization ") + voName +
-        " is being used by one or more storage classes");
-    }
-
-    if(virtualOrganizationIsUsedByTapepools(conn, voName)) {
-      throw UserSpecifiedStorageClassUsedByArchiveFiles(std::string("The Virtual Organization ") + voName +
-        " is being used by one or more Tapepools");
-    }
-
-    const char *const sql =
-      "DELETE FROM "
-        "VIRTUAL_ORGANIZATION "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-
-    stmt.executeNonQuery();
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete Virtual Organization : ") +
-        voName + " because it does not exist");
-    }
-    m_tapepoolVirtualOrganizationCache.invalidate();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getVirtualOrganizations
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::VirtualOrganization> RdbmsCatalogue::getVirtualOrganizations() const {
-  try {
-    std::list<common::dataStructures::VirtualOrganization> virtualOrganizations;
-    const char *const sql =
-      "SELECT "
-        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
-
-        "READ_MAX_DRIVES AS READ_MAX_DRIVES,"
-        "WRITE_MAX_DRIVES AS WRITE_MAX_DRIVES,"
-        "MAX_FILE_SIZE AS MAX_FILE_SIZE,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "VIRTUAL_ORGANIZATION "
-      "ORDER BY "
-        "VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::VirtualOrganization virtualOrganization;
-
-      virtualOrganization.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
-
-      virtualOrganization.readMaxDrives = rset.columnUint64("READ_MAX_DRIVES");
-      virtualOrganization.writeMaxDrives = rset.columnUint64("WRITE_MAX_DRIVES");
-      virtualOrganization.maxFileSize = rset.columnUint64("MAX_FILE_SIZE");
-      virtualOrganization.comment = rset.columnString("USER_COMMENT");
-      virtualOrganization.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      virtualOrganization.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      virtualOrganization.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      virtualOrganization.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      virtualOrganization.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      virtualOrganization.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-      virtualOrganization.diskInstanceName = rset.columnString("DISK_INSTANCE_NAME");
-
-      virtualOrganizations.push_back(virtualOrganization);
-    }
-
-    return virtualOrganizations;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getVirtualOrganizationOfTapepool
-//------------------------------------------------------------------------------
-common::dataStructures::VirtualOrganization RdbmsCatalogue::getVirtualOrganizationOfTapepool(const std::string & tapepoolName) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getVirtualOrganizationOfTapepool(conn,tapepoolName);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getVirtualOrganizationOfTapepool
-//------------------------------------------------------------------------------
-common::dataStructures::VirtualOrganization RdbmsCatalogue::getVirtualOrganizationOfTapepool(rdbms::Conn & conn, const std::string & tapepoolName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
-
-        "VIRTUAL_ORGANIZATION.READ_MAX_DRIVES AS READ_MAX_DRIVES,"
-        "VIRTUAL_ORGANIZATION.WRITE_MAX_DRIVES AS WRITE_MAX_DRIVES,"
-        "VIRTUAL_ORGANIZATION.MAX_FILE_SIZE AS MAX_FILE_SIZE,"
-
-        "VIRTUAL_ORGANIZATION.USER_COMMENT AS USER_COMMENT,"
-
-        "VIRTUAL_ORGANIZATION.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "VIRTUAL_ORGANIZATION.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "VIRTUAL_ORGANIZATION.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "VIRTUAL_ORGANIZATION.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-
-        "VIRTUAL_ORGANIZATION.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "VIRTUAL_ORGANIZATION.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "VIRTUAL_ORGANIZATION.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "TAPE_POOL "
-      "INNER JOIN "
-        "VIRTUAL_ORGANIZATION "
-      "ON "
-        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-      "WHERE "
-        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME",tapepoolName);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()){
-      throw exception::UserError(std::string("In RdbmsCatalogue::getVirtualOrganizationsOfTapepool() unable to find the Virtual Organization of the tapepool ") + tapepoolName + ".");
-    }
-    common::dataStructures::VirtualOrganization virtualOrganization;
-
-    virtualOrganization.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
-    virtualOrganization.readMaxDrives = rset.columnUint64("READ_MAX_DRIVES");
-    virtualOrganization.writeMaxDrives = rset.columnUint64("WRITE_MAX_DRIVES");
-    virtualOrganization.maxFileSize = rset.columnUint64("MAX_FILE_SIZE");
-    virtualOrganization.comment = rset.columnString("USER_COMMENT");
-    virtualOrganization.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-    virtualOrganization.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-    virtualOrganization.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-    virtualOrganization.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-    virtualOrganization.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-    virtualOrganization.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-    virtualOrganization.diskInstanceName = rset.columnString("DISK_INSTANCE_NAME");
-    return virtualOrganization;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getCachedVirtualOrganizationOfTapepool
-//------------------------------------------------------------------------------
-common::dataStructures::VirtualOrganization RdbmsCatalogue::getCachedVirtualOrganizationOfTapepool(const std::string & tapepoolName) const {
-  try {
-    auto getNonCachedValue = [&] {
-      auto conn = m_connPool.getConn();
-      return getVirtualOrganizationOfTapepool(conn,tapepoolName);
-    };
-    return m_tapepoolVirtualOrganizationCache.getCachedValue(tapepoolName,getNonCachedValue).value;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// modifyVirtualOrganizationName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyVirtualOrganizationName(const common::dataStructures::SecurityIdentity& admin, const std::string& currentVoName, const std::string& newVoName) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE VIRTUAL_ORGANIZATION SET "
-        "VIRTUAL_ORGANIZATION_NAME = :NEW_VIRTUAL_ORGANIZATION_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :CUR_VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-    if(newVoName != currentVoName){
-      if(virtualOrganizationExists(conn,newVoName)){
-        throw exception::UserError(std::string("Cannot modify the virtual organization name ") + currentVoName +". The new name : " + newVoName+" already exists in the database.");
-      }
-    }
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":NEW_VIRTUAL_ORGANIZATION_NAME", newVoName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":CUR_VIRTUAL_ORGANIZATION_NAME", currentVoName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify virtual organization : ") + currentVoName +
-        " because it does not exist");
-    }
-
-    m_tapepoolVirtualOrganizationCache.invalidate();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives){
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE VIRTUAL_ORGANIZATION SET "
-        "READ_MAX_DRIVES = :READ_MAX_DRIVES,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":READ_MAX_DRIVES", readMaxDrives);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
-        " because it does not exist");
-    }
-
-    m_tapepoolVirtualOrganizationCache.invalidate();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives){
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE VIRTUAL_ORGANIZATION SET "
-        "WRITE_MAX_DRIVES = :WRITE_MAX_DRIVES,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":WRITE_MAX_DRIVES", writeMaxDrives);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
-        " because it does not exist");
-    }
-
-    m_tapepoolVirtualOrganizationCache.invalidate();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize){
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE VIRTUAL_ORGANIZATION SET "
-        "MAX_FILE_SIZE = :MAX_FILE_SIZE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":MAX_FILE_SIZE", maxFileSize);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
-        " because it does not exist");
-    }
-
-    m_tapepoolVirtualOrganizationCache.invalidate();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity& admin, const std::string& voName, const std::string& comment) {
-try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE VIRTUAL_ORGANIZATION SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
-        " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyVirtualOrganizationDiskInstanceName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity& admin, const std::string& voName, const std::string& diskInstance) {
-try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE VIRTUAL_ORGANIZATION SET "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
-        " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// createStorageClass
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createStorageClass(
-  const common::dataStructures::SecurityIdentity &admin,
-  const common::dataStructures::StorageClass &storageClass) {
-  try {
-
-    if(storageClass.name.empty()) {
-      throw UserSpecifiedAnEmptyStringStorageClassName("Cannot create storage class because the storage class name is"
-        " an empty string");
-    }
-
-    if (storageClass.comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create storage class because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(storageClass.comment);
-    std::string vo = storageClass.vo.name;
-
-    if(vo.empty()) {
-      throw UserSpecifiedAnEmptyStringVo("Cannot create storage class because the vo is an empty string");
-    }
-
-    auto conn = m_connPool.getConn();
-    if(storageClassExists(conn, storageClass.name)) {
-      throw exception::UserError(std::string("Cannot create storage class : ") +
-        storageClass.name + " because it already exists");
-    }
-    if(!virtualOrganizationExists(conn,vo)){
-      throw exception::UserError(std::string("Cannot create storage class : ") +
-        storageClass.name + " because the vo : " + vo + " does not exist");
-    }
-    const uint64_t storageClassId = getNextStorageClassId(conn);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO STORAGE_CLASS("
-        "STORAGE_CLASS_ID,"
-        "STORAGE_CLASS_NAME,"
-        "NB_COPIES,"
-        "VIRTUAL_ORGANIZATION_ID,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":STORAGE_CLASS_ID,"
-        ":STORAGE_CLASS_NAME,"
-        ":NB_COPIES,"
-        "(SELECT VIRTUAL_ORGANIZATION_ID FROM VIRTUAL_ORGANIZATION WHERE VIRTUAL_ORGANIZATION_NAME = :VO),"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindUint64(":STORAGE_CLASS_ID", storageClassId);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.name);
-    stmt.bindUint64(":NB_COPIES", storageClass.nbCopies);
-    stmt.bindString(":VO",vo);
-
-    stmt.bindString(":USER_COMMENT", storageClass.comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// storageClassExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::storageClassExists(rdbms::Conn &conn,
-  const std::string &storageClassName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
-      "FROM "
-        "STORAGE_CLASS "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteStorageClass
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteStorageClass(const std::string &storageClassName) {
-  try {
-    auto conn = m_connPool.getConn();
-
-    if(storageClassIsUsedByArchiveRoutes(conn, storageClassName)) {
-      throw UserSpecifiedStorageClassUsedByArchiveRoutes(std::string("The ") + storageClassName +
-        " storage class is being used by one or more archive routes");
-    }
-
-    if(storageClassIsUsedByArchiveFiles(conn, storageClassName)) {
-      throw UserSpecifiedStorageClassUsedByArchiveFiles(std::string("The ") + storageClassName +
-        " storage class is being used by one or more archive files");
-    }
-
-    if(storageClassIsUsedByFileRecyleLogs(conn,storageClassName)){
-      throw UserSpecifiedStorageClassUsedByFileRecycleLogs(std::string("The ") + storageClassName +
-        " storage class is being used by one or more file in the recycle logs");
-    }
-
-    const char *const sql =
-      "DELETE FROM "
-        "STORAGE_CLASS "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-
-    stmt.executeNonQuery();
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete storage-class : ") +
-        storageClassName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// MediaTypeIsUsedByTapes
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::mediaTypeIsUsedByTapes(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT"                                          "\n"
-        "MEDIA_TYPE.MEDIA_TYPE_NAME"                    "\n"
-      "FROM"                                            "\n"
-        "TAPE"                                          "\n"
-      "INNER JOIN"                                      "\n"
-        "MEDIA_TYPE"                                    "\n"
-      "ON"                                              "\n"
-        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID" "\n"
-      "WHERE"                                           "\n"
-        "MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// storageClassIsUsedByArchiveRoutes
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::storageClassIsUsedByArchiveRoutes(rdbms::Conn &conn, const std::string &storageClassName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
-      "FROM "
-        "ARCHIVE_ROUTE "
-      "INNER JOIN "
-        "STORAGE_CLASS "
-      "ON "
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// storageClassIsUsedByARchiveFiles
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::storageClassIsUsedByArchiveFiles(rdbms::Conn &conn, const std::string &storageClassName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN "
-        "STORAGE_CLASS "
-      "ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// storageClassIsUsedByFileRecyleLogs
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::storageClassIsUsedByFileRecyleLogs(rdbms::Conn &conn, const std::string &storageClassName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
-      "FROM "
-        "FILE_RECYCLE_LOG "
-      "INNER JOIN "
-        "STORAGE_CLASS "
-      "ON "
-        "FILE_RECYCLE_LOG.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// virtualOrganizationIsUsedByStorageClasses
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::virtualOrganizationIsUsedByStorageClasses(rdbms::Conn &conn, const std::string &voName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
-      "FROM "
-        "VIRTUAL_ORGANIZATION "
-      "INNER JOIN "
-        "STORAGE_CLASS "
-      "ON "
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID = STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// virtualOrganizationIsUsedByTapepools
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::virtualOrganizationIsUsedByTapepools(rdbms::Conn &conn, const std::string &voName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
-      "FROM "
-        "VIRTUAL_ORGANIZATION "
-      "INNER JOIN "
-        "TAPE_POOL "
-      "ON "
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID = TAPE_POOL.VIRTUAL_ORGANIZATION_ID "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getStorageClasses
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::StorageClass> RdbmsCatalogue::getStorageClasses() const {
-  try {
-    std::list<common::dataStructures::StorageClass> storageClasses;
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "NB_COPIES AS NB_COPIES,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
-
-        "STORAGE_CLASS.USER_COMMENT AS USER_COMMENT,"
-
-        "STORAGE_CLASS.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "STORAGE_CLASS.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "STORAGE_CLASS.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "STORAGE_CLASS.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "STORAGE_CLASS.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "STORAGE_CLASS.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "STORAGE_CLASS "
-      "INNER JOIN "
-        "VIRTUAL_ORGANIZATION ON STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-      "ORDER BY "
-        "STORAGE_CLASS_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::StorageClass storageClass;
-
-      storageClass.name = rset.columnString("STORAGE_CLASS_NAME");
-      storageClass.nbCopies = rset.columnUint64("NB_COPIES");
-      storageClass.vo.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
-      storageClass.comment = rset.columnString("USER_COMMENT");
-      storageClass.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      storageClass.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      storageClass.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      storageClass.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      storageClass.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      storageClass.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      storageClasses.push_back(storageClass);
-    }
-
-    return storageClasses;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getStorageClasses
-//------------------------------------------------------------------------------
-common::dataStructures::StorageClass RdbmsCatalogue::getStorageClass(const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "NB_COPIES AS NB_COPIES,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
-        "VIRTUAL_ORGANIZATION.MAX_FILE_SIZE AS MAX_FILE_SIZE,"
-        "STORAGE_CLASS.USER_COMMENT AS USER_COMMENT,"
-
-        "STORAGE_CLASS.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "STORAGE_CLASS.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "STORAGE_CLASS.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "STORAGE_CLASS.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "STORAGE_CLASS.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "STORAGE_CLASS.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "STORAGE_CLASS "
-      "INNER JOIN "
-        "VIRTUAL_ORGANIZATION ON STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", name);
-    auto rset = stmt.executeQuery();
-    if (rset.isEmpty()) {
-      throw exception::UserError(std::string("Cannot get storage class : ") + name +
-        " because it does not exist");
-    }
-    rset.next();
-    common::dataStructures::StorageClass storageClass;
-
-    storageClass.name = rset.columnString("STORAGE_CLASS_NAME");
-    storageClass.nbCopies = rset.columnUint64("NB_COPIES");
-    storageClass.vo.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
-    storageClass.vo.maxFileSize = rset.columnUint64("MAX_FILE_SIZE");
-    storageClass.comment = rset.columnString("USER_COMMENT");
-    storageClass.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-    storageClass.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-    storageClass.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-    storageClass.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-    storageClass.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-    storageClass.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-    return storageClass;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// modifyStorageClassNbCopies
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t nbCopies) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE STORAGE_CLASS SET "
-        "NB_COPIES = :NB_COPIES,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":NB_COPIES", nbCopies);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":STORAGE_CLASS_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
-        " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyStorageClassComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE STORAGE_CLASS SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":STORAGE_CLASS_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
-        " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo){
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE STORAGE_CLASS SET "
-        "VIRTUAL_ORGANIZATION_ID = (SELECT VIRTUAL_ORGANIZATION_ID FROM VIRTUAL_ORGANIZATION WHERE VIRTUAL_ORGANIZATION_NAME = :VO),"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto conn = m_connPool.getConn();
-    if(vo.empty()){
-      throw UserSpecifiedAnEmptyStringVo(std::string("Cannot modify the vo of the storage class : ") + name + " because the vo is an empty string");
-    }
-    if(!virtualOrganizationExists(conn,vo)){
-      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
-        " because the vo " + vo + " does not exist");
-    }
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VO", vo);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":STORAGE_CLASS_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
-        " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyStorageClassName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &currentName, const std::string &newName) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE STORAGE_CLASS SET "
-        "STORAGE_CLASS_NAME = :NEW_STORAGE_CLASS_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :CURRENT_STORAGE_CLASS_NAME";
-    auto conn = m_connPool.getConn();
-    if(newName != currentName){
-      if(storageClassExists(conn,newName)){
-        throw exception::UserError(std::string("Cannot modify the storage class name ") + currentName +". The new name : " + newName+" already exists in the database.");
-      }
-    }
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":NEW_STORAGE_CLASS_NAME", newName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":CURRENT_STORAGE_CLASS_NAME", currentName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify storage class : ") + currentName +
-        " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-// -----------------------------------------------------------------------------
-// modifyArchiveFileStorageClassId
-// -----------------------------------------------------------------------------
-void RdbmsCatalogue::modifyArchiveFileStorageClassId(const uint64_t archiveFileId, const std::string& newStorageClassName) const {
-  try {
-    auto conn = m_connPool.getConn();
-    if(!storageClassExists(conn, newStorageClassName)) {
-      exception::UserError ue;
-      ue.getMessage() << "Cannot modify archive file " << ": " << archiveFileId << " because storage class "
-      << ":" << newStorageClassName << " does not exist";
-      throw ue;
-    }
-
-    const char *const sql =
-    "UPDATE ARCHIVE_FILE   "
-    "SET STORAGE_CLASS_ID = ("
-      "SELECT STORAGE_CLASS_ID FROM STORAGE_CLASS WHERE STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME "
-    ") "
-    "WHERE "
-      "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", newStorageClassName);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    auto rset = stmt.executeQuery();
-
-  } catch(exception::UserError &ue) {
-      throw ue;
-  } catch(exception::Exception &ex) {
-      ex.getMessage().str(std::string(__FUNCTION__) + ": " +  ex.getMessage().str());
-  }
-}
-
-//------------------------------------------------------------------------------
-// createMediaType
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createMediaType(
-  const common::dataStructures::SecurityIdentity &admin,
-  const MediaType &mediaType) {
-  try {
-    if (mediaType.name.empty()) {
-      throw UserSpecifiedAnEmptyStringMediaTypeName("Cannot create media type because the media type name is an"
-        " empty string");
-    }
-
-    if (mediaType.cartridge.empty()) {
-      throw UserSpecifiedAnEmptyStringCartridge(std::string("Cannot create media type ") + mediaType.name +
-        " because the cartridge is an empty string");
-    }
-
-    if (mediaType.comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment(std::string("Cannot create media type ") + mediaType.name +
-        " because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(mediaType.comment);
-    if (mediaType.capacityInBytes == 0){
-      throw UserSpecifiedAZeroCapacity(std::string("Cannot create media type ") + mediaType.name + " because the capacity is zero");
-    }
-    auto conn = m_connPool.getConn();
-    if (mediaTypeExists(conn, mediaType.name)) {
-      throw exception::UserError(std::string("Cannot create media type ") + mediaType.name +
-        " because it already exists");
-    }
-    const uint64_t mediaTypeId = getNextMediaTypeId(conn);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO MEDIA_TYPE("
-        "MEDIA_TYPE_ID,"
-        "MEDIA_TYPE_NAME,"
-        "CARTRIDGE,"
-        "CAPACITY_IN_BYTES,"
-        "PRIMARY_DENSITY_CODE,"
-        "SECONDARY_DENSITY_CODE,"
-        "NB_WRAPS,"
-        "MIN_LPOS,"
-        "MAX_LPOS,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":MEDIA_TYPE_ID,"
-        ":MEDIA_TYPE_NAME,"
-        ":CARTRIDGE,"
-        ":CAPACITY_IN_BYTES,"
-        ":PRIMARY_DENSITY_CODE,"
-        ":SECONDARY_DENSITY_CODE,"
-        ":NB_WRAPS,"
-        ":MIN_LPOS,"
-        ":MAX_LPOS,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindUint64(":MEDIA_TYPE_ID", mediaTypeId);
-    stmt.bindString(":MEDIA_TYPE_NAME", mediaType.name);
-    stmt.bindString(":CARTRIDGE", mediaType.cartridge);
-    stmt.bindUint64(":CAPACITY_IN_BYTES", mediaType.capacityInBytes);
-    stmt.bindUint8(":PRIMARY_DENSITY_CODE", mediaType.primaryDensityCode);
-    stmt.bindUint8(":SECONDARY_DENSITY_CODE", mediaType.secondaryDensityCode);
-    stmt.bindUint32(":NB_WRAPS", mediaType.nbWraps);
-    stmt.bindUint64(":MIN_LPOS", mediaType.minLPos);
-    stmt.bindUint64(":MAX_LPOS", mediaType.maxLPos);
-
-    stmt.bindString(":USER_COMMENT", mediaType.comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// mediaTypeExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::mediaTypeExists(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT"                               "\n"
-        "MEDIA_TYPE_NAME AS MEDIA_TYPE_NAME" "\n"
-      "FROM"                                 "\n"
-        "MEDIA_TYPE"                         "\n"
-      "WHERE"                                "\n"
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteMediaType
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteMediaType(const std::string &name) {
-  try {
-    auto conn = m_connPool.getConn();
-
-    if(mediaTypeIsUsedByTapes(conn, name)) {
-      throw UserSpecifiedMediaTypeUsedByTapes(std::string("The ") + name +
-        " media type is being used by one or more tapes");
-    }
-
-    const char *const sql =
-      "DELETE FROM"                          "\n"
-        "MEDIA_TYPE"                         "\n"
-      "WHERE"                                "\n"
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-
-    stmt.executeNonQuery();
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMediaTypes
-//------------------------------------------------------------------------------
-std::list<MediaTypeWithLogs> RdbmsCatalogue::getMediaTypes() const {
-  try {
-    std::list<MediaTypeWithLogs> mediaTypes;
-    const char *const sql =
-      "SELECT"                                              "\n"
-        "MEDIA_TYPE_NAME AS MEDIA_TYPE_NAME,"               "\n"
-        "CARTRIDGE AS CARTRIDGE,"                           "\n"
-        "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"           "\n"
-        "PRIMARY_DENSITY_CODE AS PRIMARY_DENSITY_CODE,"     "\n"
-        "SECONDARY_DENSITY_CODE AS SECONDARY_DENSITY_CODE," "\n"
-        "NB_WRAPS AS NB_WRAPS,"                             "\n"
-        "MIN_LPOS AS MIN_LPOS,"                             "\n"
-        "MAX_LPOS AS MAX_LPOS,"                             "\n"
-
-        "USER_COMMENT AS USER_COMMENT,"                     "\n"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," "\n"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," "\n"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"           "\n"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"   "\n"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"   "\n"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME"              "\n"
-      "FROM"                                                "\n"
-        "MEDIA_TYPE"                                        "\n"
-      "ORDER BY"                                            "\n"
-        "MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      MediaTypeWithLogs mediaType;
-
-      mediaType.name = rset.columnString("MEDIA_TYPE_NAME");
-      mediaType.cartridge = rset.columnString("CARTRIDGE");
-      mediaType.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-      mediaType.primaryDensityCode = rset.columnOptionalUint8("PRIMARY_DENSITY_CODE");
-      mediaType.secondaryDensityCode = rset.columnOptionalUint8("SECONDARY_DENSITY_CODE");
-      mediaType.nbWraps = rset.columnOptionalUint32("NB_WRAPS");
-      mediaType.minLPos = rset.columnOptionalUint64("MIN_LPOS");
-      mediaType.maxLPos = rset.columnOptionalUint64("MAX_LPOS");
-      mediaType.comment = rset.columnString("USER_COMMENT");
-      mediaType.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      mediaType.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      mediaType.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      mediaType.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      mediaType.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      mediaType.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      mediaTypes.push_back(mediaType);
-    }
-
-    return mediaTypes;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-MediaType RdbmsCatalogue::getMediaTypeByVid(const std::string& vid) const {
-  try {
-    std::list<MediaTypeWithLogs> mediaTypes;
-    const char *const sql =
-      "SELECT"                                              "\n"
-        "MEDIA_TYPE_NAME AS MEDIA_TYPE_NAME,"               "\n"
-        "CARTRIDGE AS CARTRIDGE,"                           "\n"
-        "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"           "\n"
-        "PRIMARY_DENSITY_CODE AS PRIMARY_DENSITY_CODE,"     "\n"
-        "SECONDARY_DENSITY_CODE AS SECONDARY_DENSITY_CODE," "\n"
-        "NB_WRAPS AS NB_WRAPS,"                             "\n"
-        "MIN_LPOS AS MIN_LPOS,"                             "\n"
-        "MAX_LPOS AS MAX_LPOS,"                             "\n"
-
-        "MEDIA_TYPE.USER_COMMENT AS USER_COMMENT "          "\n"
-      "FROM"                                                "\n"
-        "MEDIA_TYPE "                                       "\n"
-      "INNER JOIN TAPE "                                    "\n"
-        "ON MEDIA_TYPE.MEDIA_TYPE_ID = TAPE.MEDIA_TYPE_ID " "\n"
-      "WHERE "                                              "\n"
-        "TAPE.VID = :VID"                                   "\n";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID",vid);
-    auto rset = stmt.executeQuery();
-    if(rset.next()){
-      MediaType mediaType;
-
-      mediaType.name = rset.columnString("MEDIA_TYPE_NAME");
-      mediaType.cartridge = rset.columnString("CARTRIDGE");
-      mediaType.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-      mediaType.primaryDensityCode = rset.columnOptionalUint8("PRIMARY_DENSITY_CODE");
-      mediaType.secondaryDensityCode = rset.columnOptionalUint8("SECONDARY_DENSITY_CODE");
-      mediaType.nbWraps = rset.columnOptionalUint32("NB_WRAPS");
-      mediaType.minLPos = rset.columnOptionalUint64("MIN_LPOS");
-      mediaType.maxLPos = rset.columnOptionalUint64("MAX_LPOS");
-      mediaType.comment = rset.columnString("USER_COMMENT");
-
-      return mediaType;
-    } else {
-      throw exception::Exception("The tape vid "+vid+" does not exist.");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &currentName, const std::string &newName) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "MEDIA_TYPE_NAME = :NEW_MEDIA_TYPE_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :CURRENT_MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    if(newName != currentName){
-      if(mediaTypeExists(conn, newName)){
-        throw exception::UserError(std::string("Cannot modify the media type name ") + currentName +". The new name : "
-        + newName + " already exists in the database.");
-      }
-    }
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":NEW_MEDIA_TYPE_NAME", newName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":CURRENT_MEDIA_TYPE_NAME", currentName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + currentName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeCartridge
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &cartridge) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "CARTRIDGE = :CARTRIDGE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":CARTRIDGE", cartridge);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeCapacityInBytes
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t capacityInBytes) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":CAPACITY_IN_BYTES", capacityInBytes);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypePrimaryDensityCode
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint8_t primaryDensityCode) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "PRIMARY_DENSITY_CODE = :PRIMARY_DENSITY_CODE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint8(":PRIMARY_DENSITY_CODE", primaryDensityCode);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeSecondaryDensityCode
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint8_t secondaryDensityCode) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "SECONDARY_DENSITY_CODE = :SECONDARY_DENSITY_CODE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint8(":SECONDARY_DENSITY_CODE", secondaryDensityCode);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeNbWraps
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
-  const std::optional<std::uint32_t> &nbWraps) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "NB_WRAPS = :NB_WRAPS,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint32(":NB_WRAPS", nbWraps);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeMinLPos
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::optional<std::uint64_t> &minLPos) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "MIN_LPOS = :MIN_LPOS,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":MIN_LPOS", minLPos);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeMaxLPos
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::optional<std::uint64_t> &maxLPos) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "MAX_LPOS = :MAX_LPOS,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":MAX_LPOS", maxLPos);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyMediaTypeComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MEDIA_TYPE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createTapePool
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createTapePool(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name,
-  const std::string &vo,
-  const uint64_t nbPartialTapes,
-  const bool encryptionValue,
-  const std::optional<std::string> &supply,
-  const std::string &comment) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create tape pool because the tape pool name is an empty string");
-    }
-
-    if(vo.empty()) {
-      throw UserSpecifiedAnEmptyStringVo("Cannot create tape pool because the VO is an empty string");
-    }
-
-    if (comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create tape pool because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    auto conn = m_connPool.getConn();
-
-    if(tapePoolExists(conn, name)) {
-      throw exception::UserError(std::string("Cannot create tape pool ") + name +
-        " because a tape pool with the same name already exists");
-    }
-    if(!virtualOrganizationExists(conn,vo)){
-      throw exception::UserError(std::string("Cannot create tape pool ") + name + \
-        " because vo : "+vo+" does not exist.");
-    }
-    const uint64_t tapePoolId = getNextTapePoolId(conn);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO TAPE_POOL("
-        "TAPE_POOL_ID,"
-        "TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION_ID,"
-        "NB_PARTIAL_TAPES,"
-        "IS_ENCRYPTED,"
-        "SUPPLY,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "SELECT "
-        ":TAPE_POOL_ID,"
-        ":TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION_ID,"
-        ":NB_PARTIAL_TAPES,"
-        ":IS_ENCRYPTED,"
-        ":SUPPLY,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME "
-      "FROM "
-        "VIRTUAL_ORGANIZATION "
-      "WHERE "
-        "VIRTUAL_ORGANIZATION_NAME = :VO";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindUint64(":TAPE_POOL_ID", tapePoolId);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.bindString(":VO", vo);
-    stmt.bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes);
-    stmt.bindBool(":IS_ENCRYPTED", encryptionValue);
-    stmt.bindString(":SUPPLY", supply);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapePoolExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::tapePoolExists(const std::string &tapePoolName) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return tapePoolExists(conn, tapePoolName);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapePoolExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::tapePoolExists(rdbms::Conn &conn, const std::string &tapePoolName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "TAPE_POOL_NAME AS TAPE_POOL_NAME "
-      "FROM "
-        "TAPE_POOL "
-      "WHERE "
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapePoolUsedInAnArchiveRoute
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::tapePoolUsedInAnArchiveRoute(rdbms::Conn &conn, const std::string &tapePoolName) const {
-  try {
-    const char *const sql =
-      "SELECT"                                                "\n"
-        "TAPE_POOL_NAME AS TAPE_POOL_NAME"                    "\n"
-      "FROM"                                                  "\n"
-        "TAPE_POOL"                                           "\n"
-      "INNER JOIN ARCHIVE_ROUTE ON"                           "\n"
-        "TAPE_POOL.TAPE_POOL_ID = ARCHIVE_ROUTE.TAPE_POOL_ID" "\n"
-      "WHERE"                                                 "\n"
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// archiveFileExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::archiveFileIdExists(rdbms::Conn &conn, const uint64_t archiveFileId) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
-      "FROM "
-        "ARCHIVE_FILE "
-      "WHERE "
-        "ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskFileIdExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskFileIdExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-  const std::string &diskFileId) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
-        "DISK_FILE_ID AS DISK_FILE_ID "
-      "FROM "
-        "ARCHIVE_FILE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "DISK_FILE_ID = :DISK_FILE_ID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":DISK_FILE_ID", diskFileId);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskFileUserExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskFileUserExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-  uint32_t diskFileOwnerUid) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
-        "DISK_FILE_UID AS DISK_FILE_UID "
-      "FROM "
-        "ARCHIVE_FILE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "DISK_FILE_UID = :DISK_FILE_UID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindUint64(":DISK_FILE_UID", diskFileOwnerUid);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskFileGroupExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskFileGroupExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-  uint32_t diskFileGid) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
-        "DISK_FILE_GID AS DISK_FILE_GID "
-      "FROM "
-        "ARCHIVE_FILE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "DISK_FILE_GID = :DISK_FILE_GID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindUint64(":DISK_FILE_GID", diskFileGid);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// archiveRouteExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::archiveRouteExists(rdbms::Conn &conn,
-  const std::string &storageClassName, const uint32_t copyNb) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID AS STORAGE_CLASS_ID,"
-        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB "
-      "FROM "
-        "ARCHIVE_ROUTE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "WHERE "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND "
-        "ARCHIVE_ROUTE.COPY_NB = :COPY_NB";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    stmt.bindUint64(":COPY_NB", copyNb);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapePool
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteTapePool(const std::string &name) {
-  try {
-    auto conn = m_connPool.getConn();
-
-    if(tapePoolUsedInAnArchiveRoute(conn, name)) {
-      UserSpecifiedTapePoolUsedInAnArchiveRoute ex;
-      ex.getMessage() << "Cannot delete tape-pool " << name << " because it is used in an archive route";
-      throw ex;
-    }
-
-    const uint64_t nbTapesInPool = getNbTapesInPool(conn, name);
-
-    if(0 == nbTapesInPool) {
-      const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindString(":TAPE_POOL_NAME", name);
-      stmt.executeNonQuery();
-
-      if(0 == stmt.getNbAffectedRows()) {
-        throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
-      }
-
-      m_tapepoolVirtualOrganizationCache.invalidate();
-
-    } else {
-      throw UserSpecifiedAnEmptyTapePool(std::string("Cannot delete tape-pool ") + name + " because it is not empty");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// getTapePools
-//------------------------------------------------------------------------------
-std::list<TapePool> RdbmsCatalogue::getTapePools(const TapePoolSearchCriteria &searchCriteria) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getTapePools(conn, searchCriteria);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-std::list<TapePool> RdbmsCatalogue::getTapePools(rdbms::Conn &conn, const TapePoolSearchCriteria &searchCriteria) const {
-  if (isSetAndEmpty(searchCriteria.name)) throw exception::UserError("Pool name cannot be an empty string");
-  if (isSetAndEmpty(searchCriteria.vo)) throw exception::UserError("Virtual organisation cannot be an empty string");
-  try {
-
-    if (searchCriteria.name && !tapePoolExists(conn, searchCriteria.name.value())) {
-      UserSpecifiedANonExistentTapePool ex;
-      ex.getMessage() << "Cannot list tape pools because tape pool " + searchCriteria.name.value() + " does not exist";
-      throw ex;
-    }
-
-    if (searchCriteria.vo && !virtualOrganizationExists(conn, searchCriteria.vo.value())) {
-      UserSpecifiedANonExistentVirtualOrganization ex;
-      ex.getMessage() << "Cannot list tape pools because virtual organization " + searchCriteria.vo.value() + " does not exist";
-      throw ex;
-    }
-
-    std::list<TapePool> pools;
-    std::string sql =
-      "SELECT "
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
-        "TAPE_POOL.NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES,"
-        "TAPE_POOL.IS_ENCRYPTED AS IS_ENCRYPTED,"
-        "TAPE_POOL.SUPPLY AS SUPPLY,"
-
-        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.DATA_IN_BYTES = 0 THEN 1 ELSE 0 END), 0) AS NB_EMPTY_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_DISABLED THEN 1 ELSE 0 END), 0) AS NB_DISABLED_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.IS_FULL <> '0' THEN 1 ELSE 0 END), 0) AS NB_FULL_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_ACTIVE AND TAPE.IS_FULL = '0' THEN 1 ELSE 0 END), 0) AS NB_WRITABLE_TAPES,"
-        "COALESCE(SUM(MEDIA_TYPE.CAPACITY_IN_BYTES), 0) AS CAPACITY_IN_BYTES,"
-        "COALESCE(SUM(TAPE.DATA_IN_BYTES), 0) AS DATA_IN_BYTES,"
-        "COALESCE(SUM(TAPE.LAST_FSEQ), 0) AS NB_PHYSICAL_FILES,"
-
-        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
-
-        "TAPE_POOL.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "TAPE_POOL.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "TAPE_POOL.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "TAPE_POOL.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "TAPE_POOL.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "TAPE_POOL.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "TAPE_POOL "
-      "INNER JOIN VIRTUAL_ORGANIZATION ON "
-        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-      "LEFT OUTER JOIN TAPE ON "
-        "TAPE_POOL.TAPE_POOL_ID = TAPE.TAPE_POOL_ID "
-      "LEFT OUTER JOIN MEDIA_TYPE ON "
-        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID";
-
-    if (searchCriteria.name || searchCriteria.vo || searchCriteria.encrypted) {
-      sql += " WHERE ";
-    }
-    bool addedAWhereConstraint = false;
-    if (searchCriteria.name) {
-      sql += "TAPE_POOL.TAPE_POOL_NAME = :NAME";
-      addedAWhereConstraint = true;
-    }
-
-    if (searchCriteria.vo) {
-      if (addedAWhereConstraint) sql += " AND ";
-      sql += "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME = :VO";
-      addedAWhereConstraint = true;
-    }
-
-    if (searchCriteria.encrypted) {
-      if (addedAWhereConstraint) sql += " AND ";
-      sql += "TAPE_POOL.IS_ENCRYPTED = :ENCRYPTED";
-    }
-
-    sql +=
-        " GROUP BY "
-           "TAPE_POOL.TAPE_POOL_NAME,"
-           "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME,"
-           "TAPE_POOL.NB_PARTIAL_TAPES,"
-           "TAPE_POOL.IS_ENCRYPTED,"
-           "TAPE_POOL.SUPPLY,"
-           "TAPE_POOL.USER_COMMENT,"
-           "TAPE_POOL.CREATION_LOG_USER_NAME,"
-           "TAPE_POOL.CREATION_LOG_HOST_NAME,"
-           "TAPE_POOL.CREATION_LOG_TIME,"
-           "TAPE_POOL.LAST_UPDATE_USER_NAME,"
-           "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
-           "TAPE_POOL.LAST_UPDATE_TIME "
-         "ORDER BY "
-           "TAPE_POOL_NAME";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STATE_DISABLED",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::DISABLED));
-    stmt.bindString(":STATE_ACTIVE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
-
-    if (searchCriteria.name) {
-      stmt.bindString(":NAME", searchCriteria.name.value());
-    }
-
-    if (searchCriteria.vo) {
-      stmt.bindString(":VO", searchCriteria.vo.value());
-    }
-
-    if(searchCriteria.encrypted) {
-      stmt.bindBool(":ENCRYPTED", searchCriteria.encrypted.value());
-    }
-
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      TapePool pool;
-
-      pool.name = rset.columnString("TAPE_POOL_NAME");
-      pool.vo.name = rset.columnString("VO");
-      pool.nbPartialTapes = rset.columnUint64("NB_PARTIAL_TAPES");
-      pool.encryption = rset.columnBool("IS_ENCRYPTED");
-      pool.supply = rset.columnOptionalString("SUPPLY");
-      pool.nbTapes = rset.columnUint64("NB_TAPES");
-      pool.nbEmptyTapes = rset.columnUint64("NB_EMPTY_TAPES");
-      pool.nbDisabledTapes = rset.columnUint64("NB_DISABLED_TAPES");
-      pool.nbFullTapes = rset.columnUint64("NB_FULL_TAPES");
-      pool.nbWritableTapes = rset.columnUint64("NB_WRITABLE_TAPES");
-      pool.capacityBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-      pool.dataBytes = rset.columnUint64("DATA_IN_BYTES");
-      pool.nbPhysicalFiles = rset.columnUint64("NB_PHYSICAL_FILES");
-      pool.comment = rset.columnString("USER_COMMENT");
-      pool.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      pool.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      pool.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      pool.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      pool.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      pool.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      pools.push_back(pool);
-    }
-
-    return pools;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapePool
-//------------------------------------------------------------------------------
-std::optional<TapePool> RdbmsCatalogue::getTapePool(const std::string &tapePoolName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
-        "TAPE_POOL.NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES,"
-        "TAPE_POOL.IS_ENCRYPTED AS IS_ENCRYPTED,"
-        "TAPE_POOL.SUPPLY AS SUPPLY,"
-
-        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.DATA_IN_BYTES = 0 THEN 1 ELSE 0 END), 0) AS NB_EMPTY_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_DISABLED THEN 1 ELSE 0 END), 0) AS NB_DISABLED_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.IS_FULL <> '0' THEN 1 ELSE 0 END), 0) AS NB_FULL_TAPES,"
-        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_ACTIVE AND TAPE.IS_FULL = '0' THEN 1 ELSE 0 END), 0) AS NB_WRITABLE_TAPES,"
-        "COALESCE(SUM(MEDIA_TYPE.CAPACITY_IN_BYTES), 0) AS CAPACITY_IN_BYTES,"
-        "COALESCE(SUM(TAPE.DATA_IN_BYTES), 0) AS DATA_IN_BYTES,"
-        "COALESCE(SUM(TAPE.LAST_FSEQ), 0) AS NB_PHYSICAL_FILES,"
-
-        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
-
-        "TAPE_POOL.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "TAPE_POOL.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "TAPE_POOL.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "TAPE_POOL.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "TAPE_POOL.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "TAPE_POOL.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "TAPE_POOL "
-      "INNER JOIN VIRTUAL_ORGANIZATION ON "
-        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-      "LEFT OUTER JOIN TAPE ON "
-        "TAPE_POOL.TAPE_POOL_ID = TAPE.TAPE_POOL_ID "
-      "LEFT OUTER JOIN MEDIA_TYPE ON "
-        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
-      "GROUP BY "
-        "TAPE_POOL.TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME,"
-        "TAPE_POOL.NB_PARTIAL_TAPES,"
-        "TAPE_POOL.IS_ENCRYPTED,"
-        "TAPE_POOL.SUPPLY,"
-        "TAPE_POOL.USER_COMMENT,"
-        "TAPE_POOL.CREATION_LOG_USER_NAME,"
-        "TAPE_POOL.CREATION_LOG_HOST_NAME,"
-        "TAPE_POOL.CREATION_LOG_TIME,"
-        "TAPE_POOL.LAST_UPDATE_USER_NAME,"
-        "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
-        "TAPE_POOL.LAST_UPDATE_TIME "
-      "HAVING "
-        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME "
-      "ORDER BY "
-        "TAPE_POOL_NAME";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-    stmt.bindString(":STATE_DISABLED",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::DISABLED));
-    stmt.bindString(":STATE_ACTIVE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
-
-    auto rset = stmt.executeQuery();
-
-    if (!rset.next()) {
-      return std::nullopt;
-    }
-
-    TapePool pool;
-    pool.name = rset.columnString("TAPE_POOL_NAME");
-    pool.vo.name = rset.columnString("VO");
-    pool.nbPartialTapes = rset.columnUint64("NB_PARTIAL_TAPES");
-    pool.encryption = rset.columnBool("IS_ENCRYPTED");
-    pool.supply = rset.columnOptionalString("SUPPLY");
-    pool.nbTapes = rset.columnUint64("NB_TAPES");
-    pool.nbEmptyTapes = rset.columnUint64("NB_EMPTY_TAPES");
-    pool.nbDisabledTapes = rset.columnUint64("NB_DISABLED_TAPES");
-    pool.nbFullTapes = rset.columnUint64("NB_FULL_TAPES");
-    pool.nbWritableTapes = rset.columnUint64("NB_WRITABLE_TAPES");
-    pool.capacityBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-    pool.dataBytes = rset.columnUint64("DATA_IN_BYTES");
-    pool.nbPhysicalFiles = rset.columnUint64("NB_PHYSICAL_FILES");
-    pool.comment = rset.columnString("USER_COMMENT");
-    pool.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-    pool.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-    pool.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-    pool.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-    pool.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-    pool.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-    return pool;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapePoolVO
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &vo) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
-        " string");
-    }
-
-    if(vo.empty()) {
-      throw UserSpecifiedAnEmptyStringVo("Cannot modify tape pool because the new VO is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE_POOL SET "
-        "VIRTUAL_ORGANIZATION_ID = (SELECT VIRTUAL_ORGANIZATION_ID FROM VIRTUAL_ORGANIZATION WHERE VIRTUAL_ORGANIZATION_NAME=:VO),"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto conn = m_connPool.getConn();
-
-    if(!virtualOrganizationExists(conn,vo)){
-      throw exception::UserError(std::string("Cannot modify tape pool ") + name +" because the vo " + vo + " does not exist");
-    }
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VO", vo);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
-    }
-    //The VO of this tapepool has changed, invalidate the tapepool-VO cache
-    m_tapepoolVirtualOrganizationCache.invalidate();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapePoolNbPartialTapes
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t nbPartialTapes) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
-        " string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE_POOL SET "
-        "NB_PARTIAL_TAPES = :NB_PARTIAL_TAPES,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapePoolComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
-        " string");
-    }
-
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot modify tape pool because the new comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE_POOL SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapePoolEncryption
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const bool encryptionValue) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE_POOL SET "
-        "IS_ENCRYPTED = :IS_ENCRYPTED,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindBool(":IS_ENCRYPTED", encryptionValue);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapePoolSupply
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &supply) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
-        " string");
-    }
-
-    std::optional<std::string> optionalSupply;
-    if(!supply.empty()) {
-      optionalSupply = supply;
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE_POOL SET "
-        "SUPPLY = :SUPPLY,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":SUPPLY", optionalSupply);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapePoolName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &currentName, const std::string &newName) {
-  try {
-    if(currentName.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
-        " string");
-    }
-
-    if(newName.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the new name is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE_POOL SET "
-        "TAPE_POOL_NAME = :NEW_TAPE_POOL_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "TAPE_POOL_NAME = :CURRENT_TAPE_POOL_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":NEW_TAPE_POOL_NAME", newName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":CURRENT_TAPE_POOL_NAME", currentName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape pool ") + currentName + " because it does not exist");
-    }
-
-    m_tapepoolVirtualOrganizationCache.invalidate();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createArchiveRoute
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createArchiveRoute(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &storageClassName,
-  const uint32_t copyNb,
-  const std::string &tapePoolName,
-  const std::string &comment) {
-  try {
-    if(storageClassName.empty()) {
-      throw UserSpecifiedAnEmptyStringStorageClassName("Cannot create archive route because storage class name is an"
-        " empty string");
-    }
-    if(0 == copyNb) {
-      throw UserSpecifiedAZeroCopyNb("Cannot create archive route because copy number is zero");
-    }
-    if(tapePoolName.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create archive route because tape pool name is an empty"
-        " string");
-    }
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create archive route because comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    const time_t now = time(nullptr);
-    auto conn = m_connPool.getConn();
-    if(archiveRouteExists(conn, storageClassName, copyNb)) {
-      exception::UserError ue;
-      ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
-        << "->" << tapePoolName << " because it already exists";
-      throw ue;
-    }
-    {
-      const auto routes = getArchiveRoutes(conn, storageClassName, tapePoolName);
-      if(!routes.empty()) {
-        exception::UserError ue;
-        ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
-          << "->" << tapePoolName << " because a route already exists for this storage class and tape pool";
-        throw ue;
-      }
-    }
-    if(!storageClassExists(conn, storageClassName)) {
-      exception::UserError ue;
-      ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
-        << "->" << tapePoolName << " because storage class " << ":" << storageClassName <<
-        " does not exist";
-      throw ue;
-    }
-    if(!tapePoolExists(conn, tapePoolName)) {
-      exception::UserError ue;
-      ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
-        << "->" << tapePoolName << " because tape pool " << tapePoolName + " does not exist";
-      throw ue;
-    }
-
-    const char *const sql =
-      "INSERT INTO ARCHIVE_ROUTE("
-        "STORAGE_CLASS_ID,"
-        "COPY_NB,"
-        "TAPE_POOL_ID,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "SELECT "
-        "STORAGE_CLASS_ID,"
-        ":COPY_NB,"
-        "(SELECT TAPE_POOL_ID FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME) AS TAPE_POOL_ID,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME "
-      "FROM "
-        "STORAGE_CLASS "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    stmt.bindUint64(":COPY_NB", copyNb);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteArchiveRoute
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteArchiveRoute(const std::string &storageClassName,
-  const uint32_t copyNb) {
-  try {
-    const char *const sql =
-      "DELETE FROM "
-        "ARCHIVE_ROUTE "
-      "WHERE "
-        "STORAGE_CLASS_ID = ("
-          "SELECT "
-            "STORAGE_CLASS_ID "
-          "FROM "
-            "STORAGE_CLASS "
-          "WHERE "
-            "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME) AND "
-        "COPY_NB = :COPY_NB";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    stmt.bindUint64(":COPY_NB", copyNb);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      exception::UserError ue;
-      ue.getMessage() << "Cannot delete archive route for storage-class " << ":" + storageClassName +
-        " and copy number " << copyNb << " because it does not exist";
-      throw ue;
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveRoutes
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::ArchiveRoute> RdbmsCatalogue::getArchiveRoutes() const {
-  try {
-    std::list<common::dataStructures::ArchiveRoute> routes;
-    const char *const sql =
-      "SELECT "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB,"
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-
-        "ARCHIVE_ROUTE.USER_COMMENT AS USER_COMMENT,"
-
-        "ARCHIVE_ROUTE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "ARCHIVE_ROUTE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "ARCHIVE_ROUTE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "ARCHIVE_ROUTE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "ARCHIVE_ROUTE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "ARCHIVE_ROUTE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "ARCHIVE_ROUTE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_POOL ON "
-        "ARCHIVE_ROUTE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-      "ORDER BY "
-        "STORAGE_CLASS_NAME, COPY_NB";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::ArchiveRoute route;
-
-      route.storageClassName = rset.columnString("STORAGE_CLASS_NAME");
-      route.copyNb = rset.columnUint64("COPY_NB");
-      route.tapePoolName = rset.columnString("TAPE_POOL_NAME");
-      route.comment = rset.columnString("USER_COMMENT");
-      route.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      route.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      route.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      route.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      route.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      route.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      routes.push_back(route);
-    }
-
-    return routes;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveRoutes
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::ArchiveRoute> RdbmsCatalogue::getArchiveRoutes(
-  const std::string &storageClassName,
-  const std::string &tapePoolName) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getArchiveRoutes(conn, storageClassName, tapePoolName);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveRoutes
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::ArchiveRoute> RdbmsCatalogue::getArchiveRoutes(rdbms::Conn &conn,
-  const std::string &storageClassName, const std::string &tapePoolName) const {
-  try {
-    std::list<common::dataStructures::ArchiveRoute> routes;
-    const char *const sql =
-      "SELECT"                                                            "\n"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"         "\n"
-        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB,"                               "\n"
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"                     "\n"
-
-        "ARCHIVE_ROUTE.USER_COMMENT AS USER_COMMENT,"                     "\n"
-
-        "ARCHIVE_ROUTE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," "\n"
-        "ARCHIVE_ROUTE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," "\n"
-        "ARCHIVE_ROUTE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"           "\n"
-
-        "ARCHIVE_ROUTE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"   "\n"
-        "ARCHIVE_ROUTE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"   "\n"
-        "ARCHIVE_ROUTE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME"              "\n"
-      "FROM"                                                              "\n"
-        "ARCHIVE_ROUTE"                                                   "\n"
-      "INNER JOIN STORAGE_CLASS ON"                                       "\n"
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID" "\n"
-      "INNER JOIN TAPE_POOL ON"                                           "\n"
-        "ARCHIVE_ROUTE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID"             "\n"
-      "WHERE"                                                             "\n"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND"      "\n"
-        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME"                      "\n"
-      "ORDER BY"                                                          "\n"
-        "STORAGE_CLASS_NAME, COPY_NB";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::ArchiveRoute route;
-
-      route.storageClassName = rset.columnString("STORAGE_CLASS_NAME");
-      route.copyNb = rset.columnUint64("COPY_NB");
-      route.tapePoolName = rset.columnString("TAPE_POOL_NAME");
-      route.comment = rset.columnString("USER_COMMENT");
-      route.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      route.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      route.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      route.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      route.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      route.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      routes.push_back(route);
-    }
-
-    return routes;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyArchiveRouteTapePoolName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &storageClassName, const uint32_t copyNb,
-  const std::string &tapePoolName) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE ARCHIVE_ROUTE SET "
-        "TAPE_POOL_ID = (SELECT TAPE_POOL_ID FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME),"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "STORAGE_CLASS_ID = ("
-          "SELECT "
-            "STORAGE_CLASS_ID "
-          "FROM "
-            "STORAGE_CLASS "
-          "WHERE "
-            "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME) AND "
-        "COPY_NB = :COPY_NB";
-    auto conn = m_connPool.getConn();
-
-    if(!archiveRouteExists(conn, storageClassName, copyNb)) {
-      throw UserSpecifiedANonExistentArchiveRoute("Archive route does not exist");
-    }
-
-    if(!tapePoolExists(conn, tapePoolName)) {
-      throw UserSpecifiedANonExistentTapePool("Tape pool does not exist");
-    }
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    stmt.bindUint64(":COPY_NB", copyNb);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentArchiveRoute("Archive route does not exist");
-    }
-  } catch(exception::UserError &ue) {
-    std::ostringstream msg;
-    msg << "Cannot modify tape pool of archive route: storageClassName=" << storageClassName << " copyNb=" << copyNb <<
-      " tapePoolName=" << tapePoolName << ": " << ue.getMessage().str();
-    ue.getMessage().str(msg.str());
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyArchiveRouteComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &storageClassName, const uint32_t copyNb,
-  const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE ARCHIVE_ROUTE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "STORAGE_CLASS_ID = ("
-          "SELECT "
-            "STORAGE_CLASS_ID "
-          "FROM "
-            "STORAGE_CLASS "
-          "WHERE "
-            "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME) AND "
-        "COPY_NB = :COPY_NB";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
-    stmt.bindUint64(":COPY_NB", copyNb);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      exception::UserError ue;
-      ue.getMessage() << "Cannot modify archive route for storage-class " << ":" + storageClassName +
-        " and copy number " << copyNb << " because it does not exist";
-      throw ue;
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createLogicalLibrary
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createLogicalLibrary(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name,
-  const bool isDisabled,
-  const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    auto conn = m_connPool.getConn();
-    if(logicalLibraryExists(conn, name)) {
-      throw exception::UserError(std::string("Cannot create logical library ") + name +
-        " because a logical library with the same name already exists");
-    }
-    const uint64_t logicalLibraryId = getNextLogicalLibraryId(conn);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO LOGICAL_LIBRARY("
-        "LOGICAL_LIBRARY_ID,"
-        "LOGICAL_LIBRARY_NAME,"
-        "IS_DISABLED,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":LOGICAL_LIBRARY_ID,"
-        ":LOGICAL_LIBRARY_NAME,"
-        ":IS_DISABLED,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindUint64(":LOGICAL_LIBRARY_ID", logicalLibraryId);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
-    stmt.bindBool(":IS_DISABLED", isDisabled);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// logicalLibraryExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::logicalLibraryExists(rdbms::Conn &conn, const std::string &logicalLibraryName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME "
-      "FROM "
-        "LOGICAL_LIBRARY "
-      "WHERE "
-        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteLogicalLibrary
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteLogicalLibrary(const std::string &name) {
-  try {
-    const char *const sql =
-      "DELETE FROM LOGICAL_LIBRARY"                                         "\n"
-      "WHERE"                                                               "\n"
-        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME AND"                  "\n"
-        "NOT EXISTS ("                                                      "\n"
-          "SELECT"                                                          "\n"
-            "TAPE.LOGICAL_LIBRARY_ID"                                       "\n"
-          "FROM"                                                            "\n"
-            "TAPE"                                                          "\n"
-          "WHERE"                                                           "\n"
-            "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID)";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
-    stmt.executeNonQuery();
-
-    // The delete statement will effect no rows and will not raise an error if
-    // either the logical library does not exist or if it still contains tapes
-    if(0 == stmt.getNbAffectedRows()) {
-      if(logicalLibraryExists(conn, name)) {
-        throw UserSpecifiedANonEmptyLogicalLibrary(std::string("Cannot delete logical library ") + name +
-          " because it contains one or more tapes");
-      } else {
-        throw UserSpecifiedANonExistentLogicalLibrary(std::string("Cannot delete logical library ") + name +
-          " because it does not exist");
-      }
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getLogicalLibraries
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::LogicalLibrary> RdbmsCatalogue::getLogicalLibraries() const {
-  try {
-    std::list<common::dataStructures::LogicalLibrary> libs;
-    const char *const sql =
-      "SELECT "
-        "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
-        "IS_DISABLED AS IS_DISABLED,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-        "DISABLED_REASON AS DISABLED_REASON,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "LOGICAL_LIBRARY "
-      "ORDER BY "
-        "LOGICAL_LIBRARY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::LogicalLibrary lib;
-
-      lib.name = rset.columnString("LOGICAL_LIBRARY_NAME");
-      lib.isDisabled = rset.columnBool("IS_DISABLED");
-      lib.comment = rset.columnString("USER_COMMENT");
-      lib.disabledReason = rset.columnOptionalString("DISABLED_REASON");
-      lib.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      lib.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      lib.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      lib.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      lib.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      lib.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      libs.push_back(lib);
-    }
-
-    return libs;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyLogicalLibraryName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &currentName, const std::string &newName) {
-  try {
-    if(currentName.empty()) {
-      throw UserSpecifiedAnEmptyStringLogicalLibraryName(
-        "Cannot modify logical library because the logical library name is an empty string");
-    }
-
-    if(newName.empty()) {
-      throw UserSpecifiedAnEmptyStringLogicalLibraryName(
-        "Cannot modify logical library because the new name is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE LOGICAL_LIBRARY SET "
-        "LOGICAL_LIBRARY_NAME = :NEW_LOGICAL_LIBRARY_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "LOGICAL_LIBRARY_NAME = :CURRENT_LOGICAL_LIBRARY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":NEW_LOGICAL_LIBRARY_NAME", newName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":CURRENT_LOGICAL_LIBRARY_NAME", currentName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify logical library ") + currentName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyLogicalLibraryComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE LOGICAL_LIBRARY SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyLogicalLibraryDisabledReason
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &disabledReason) {
-  try {
-    checkCommentOrReasonMaxLength(disabledReason);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE LOGICAL_LIBRARY SET "
-        "DISABLED_REASON = :DISABLED_REASON,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISABLED_REASON", disabledReason.empty() ? std::nullopt : std::optional<std::string>(disabledReason));
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setLogicalLibraryDisabled
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const bool disabledValue) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE LOGICAL_LIBRARY SET "
-        "IS_DISABLED = :IS_DISABLED,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindBool(":IS_DISABLED", disabledValue);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createTape
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createTape(
-  const common::dataStructures::SecurityIdentity &admin,
-  const CreateTapeAttributes &tape) {
-  // CTA hard code this field to FALSE
-  const bool isFromCastor = false;
-  try {
-    std::string vid = tape.vid;
-    std::string mediaTypeName = tape.mediaType;
-    std::string vendor = tape.vendor;
-    std::string logicalLibraryName = tape.logicalLibraryName;
-    std::string tapePoolName = tape.tapePoolName;
-    bool full = tape.full;
-    // Translate an empty comment string to a NULL database value
-    const std::optional<std::string> tapeComment = tape.comment && tape.comment->empty() ? std::nullopt : tape.comment;
-    checkCommentOrReasonMaxLength(tapeComment);
-    const std::optional<std::string> stateReason = tape.stateReason && cta::utils::trimString(tape.stateReason.value()).empty() ? std::nullopt : tape.stateReason;
-    checkCommentOrReasonMaxLength(stateReason);
-    if(vid.empty()) {
-      throw UserSpecifiedAnEmptyStringVid("Cannot create tape because the VID is an empty string");
-    }
-
-    if(!utils::isUpper(vid)) {
-      throw UserSpecifiedAnEmptyStringVid("Cannot create tape because the VID has non uppercase characters");
-    }
-
-    if(mediaTypeName.empty()) {
-      throw UserSpecifiedAnEmptyStringMediaType("Cannot create tape because the media type is an empty string");
-    }
-
-    if(vendor.empty()) {
-      throw UserSpecifiedAnEmptyStringVendor("Cannot create tape because the vendor is an empty string");
-    }
-
-    if(logicalLibraryName.empty()) {
-      throw UserSpecifiedAnEmptyStringLogicalLibraryName("Cannot create tape because the logical library name is an"
-        " empty string");
-    }
-
-    if(tapePoolName.empty()) {
-      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create tape because the tape pool name is an empty string");
-    }
-
-    std::string tapeState;
-    try {
-      tapeState = common::dataStructures::Tape::stateToString(tape.state);
-    } catch(cta::exception::Exception &ex) {
-      std::string errorMsg = "Cannot create tape because the state specified does not exist. Possible values for state are: " + common::dataStructures::Tape::getAllPossibleStates();
-      throw UserSpecifiedANonExistentTapeState(errorMsg);
-    }
-
-    if(tape.state != common::dataStructures::Tape::ACTIVE){
-      if(!stateReason){
-        throw UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive("Cannot create tape because no reason has been provided for the state " + tapeState);
-      }
-    }
-
-    auto conn = m_connPool.getConn();
-    if(tapeExists(conn, vid)) {
-      throw exception::UserError(std::string("Cannot create tape ") + vid +
-        " because a tape with the same volume identifier already exists");
-    }
-    const auto logicalLibraryId = getLogicalLibraryId(conn, logicalLibraryName);
-    if(!logicalLibraryId) {
-      throw exception::UserError(std::string("Cannot create tape ") + vid + " because logical library " +
-        logicalLibraryName + " does not exist");
-    }
-    const auto tapePoolId = getTapePoolId(conn, tapePoolName);
-    if(!tapePoolId) {
-      throw exception::UserError(std::string("Cannot create tape ") + vid + " because tape pool " +
-       tapePoolName + " does not exist");
-    }
-
-    const auto mediaTypeId = getMediaTypeId(conn, mediaTypeName);
-    if(!mediaTypeId) {
-      throw exception::UserError(std::string("Cannot create tape ") + vid + " because media type " +
-        mediaTypeName + " does not exist");
-    }
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO TAPE("          "\n"
-        "VID,"                     "\n"
-        "MEDIA_TYPE_ID,"           "\n"
-        "VENDOR,"                  "\n"
-        "LOGICAL_LIBRARY_ID,"      "\n"
-        "TAPE_POOL_ID,"            "\n"
-        "DATA_IN_BYTES,"           "\n"
-        "LAST_FSEQ,"               "\n"
-        "IS_FULL,"                 "\n"
-        "IS_FROM_CASTOR,"          "\n"
-
-        "USER_COMMENT,"            "\n"
-
-        "TAPE_STATE,"              "\n"
-        "STATE_REASON,"            "\n"
-        "STATE_UPDATE_TIME,"       "\n"
-        "STATE_MODIFIED_BY,"       "\n"
-
-        "CREATION_LOG_USER_NAME,"  "\n"
-        "CREATION_LOG_HOST_NAME,"  "\n"
-        "CREATION_LOG_TIME,"       "\n"
-
-        "LAST_UPDATE_USER_NAME,"   "\n"
-        "LAST_UPDATE_HOST_NAME,"   "\n"
-        "LAST_UPDATE_TIME)"        "\n"
-      "VALUES("                    "\n"
-        ":VID,"                    "\n"
-        ":MEDIA_TYPE_ID,"          "\n"
-        ":VENDOR,"                 "\n"
-        ":LOGICAL_LIBRARY_ID,"     "\n"
-        ":TAPE_POOL_ID,"           "\n"
-        ":DATA_IN_BYTES,"          "\n"
-        ":LAST_FSEQ,"              "\n"
-        ":IS_FULL,"                "\n"
-        ":IS_FROM_CASTOR,"         "\n"
-
-        ":USER_COMMENT,"           "\n"
-
-        ":TAPE_STATE,"             "\n"
-        ":STATE_REASON,"           "\n"
-        ":STATE_UPDATE_TIME,"      "\n"
-        ":STATE_MODIFIED_BY,"       "\n"
-
-        ":CREATION_LOG_USER_NAME," "\n"
-        ":CREATION_LOG_HOST_NAME," "\n"
-        ":CREATION_LOG_TIME,"      "\n"
-
-        ":LAST_UPDATE_USER_NAME,"  "\n"
-        ":LAST_UPDATE_HOST_NAME,"  "\n"
-        ":LAST_UPDATE_TIME"        "\n"
-      ")";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":VID", vid);
-    stmt.bindUint64(":MEDIA_TYPE_ID", mediaTypeId.value());
-    stmt.bindString(":VENDOR", vendor);
-    stmt.bindUint64(":LOGICAL_LIBRARY_ID", logicalLibraryId.value());
-    stmt.bindUint64(":TAPE_POOL_ID", tapePoolId.value());
-    stmt.bindUint64(":DATA_IN_BYTES", 0);
-    stmt.bindUint64(":LAST_FSEQ", 0);
-    stmt.bindBool(":IS_FULL", full);
-    stmt.bindBool(":IS_FROM_CASTOR", isFromCastor);
-
-    stmt.bindString(":USER_COMMENT", tapeComment);
-
-    std::string stateModifiedBy = RdbmsCatalogue::generateTapeStateModifiedBy(admin);
-    stmt.bindString(":TAPE_STATE",cta::common::dataStructures::Tape::stateToString(tape.state));
-    stmt.bindString(":STATE_REASON",stateReason);
-    stmt.bindUint64(":STATE_UPDATE_TIME",now);
-    stmt.bindString(":STATE_MODIFIED_BY", stateModifiedBy);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("mediaType", mediaTypeName)
-       .add("vendor", vendor)
-       .add("logicalLibraryName", logicalLibraryName)
-       .add("tapePoolName", tapePoolName)
-       .add("isFull", full ? 1 : 0)
-       .add("isFromCastor", isFromCastor ? 1 : 0)
-       .add("userComment", tape.comment ? tape.comment.value() : "")
-       .add("tapeState",cta::common::dataStructures::Tape::stateToString(tape.state))
-       .add("stateReason",stateReason ? stateReason.value() : "")
-       .add("stateUpdateTime",now)
-       .add("stateModifiedBy",stateModifiedBy)
-       .add("creationLogUserName", admin.username)
-       .add("creationLogHostName", admin.host)
-       .add("creationLogTime", now);
-    lc.log(log::INFO, "Catalogue - user created tape");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapeExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::tapeExists(const std::string &vid) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return tapeExists(conn, vid);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapeExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::tapeExists(rdbms::Conn &conn, const std::string &vid) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "VID AS VID "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskSystemExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskSystemExists(const std::string &name) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return diskSystemExists(conn, name);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskSystemExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskSystemExists(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME "
-      "FROM "
-        "DISK_SYSTEM "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_SYSTEM_NAME", name);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskInstanceExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskInstanceExists(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME "
-      "FROM "
-        "DISK_INSTANCE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", name);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// diskInstanceSpaceExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::diskInstanceSpaceExists(rdbms::Conn &conn, const std::string &name, const std::string &diskInstance) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_SPACE_NAME AS DISK_INSTANCE_SPACE_NAME "
-      "FROM "
-        "DISK_INSTANCE_SPACE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-      " AND "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-
-//------------------------------------------------------------------------------
-// deleteTape
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteTape(const std::string &vid) {
-  try {
-    const char *const delete_sql =
-      "DELETE "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :DELETE_VID AND "
-        "NOT EXISTS (SELECT VID FROM TAPE_FILE WHERE VID = :SELECT_VID) AND "
-        "NOT EXISTS (SELECT VID FROM FILE_RECYCLE_LOG WHERE VID = :SELECT_VID2)";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(delete_sql);
-    stmt.bindString(":DELETE_VID", vid);
-    stmt.bindString(":SELECT_VID", vid);
-    stmt.bindString(":SELECT_VID2", vid);
-    stmt.executeNonQuery();
-
-    // The delete statement will effect no rows and will not raise an error if
-    // either the tape does not exist or if it still has tape files or files in the recycle log
-    if(0 == stmt.getNbAffectedRows()) {
-      if(tapeExists(conn, vid)) {
-        throw UserSpecifiedANonEmptyTape(std::string("Cannot delete tape ") + vid + " because either it contains one or more files or the files that were in it are in the file recycle log.");
-      } else {
-        throw UserSpecifiedANonExistentTape(std::string("Cannot delete tape ") + vid + " because it does not exist");
-      }
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapes
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(const TapeSearchCriteria &searchCriteria) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getTapes(conn, searchCriteria);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapes
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &conn,
-  const TapeSearchCriteria &searchCriteria) const {
-  if(isSetAndEmpty(searchCriteria.vid)) throw exception::UserError("VID cannot be an empty string");
-  if(isSetAndEmpty(searchCriteria.mediaType)) throw exception::UserError("Media type cannot be an empty string");
-  if(isSetAndEmpty(searchCriteria.vendor)) throw exception::UserError("Vendor cannot be an empty string");
-  if(isSetAndEmpty(searchCriteria.logicalLibrary)) throw exception::UserError("Logical library cannot be an empty string");
-  if(isSetAndEmpty(searchCriteria.tapePool)) throw exception::UserError("Tape pool cannot be an empty string");
-  if(isSetAndEmpty(searchCriteria.vo)) throw exception::UserError("Virtual organisation cannot be an empty string");
-  if(isSetAndEmpty(searchCriteria.diskFileIds)) throw exception::UserError("Disk file ID list cannot be empty");
-
-  try {
-    if(searchCriteria.tapePool && !tapePoolExists(conn, searchCriteria.tapePool.value())) {
-      UserSpecifiedANonExistentTapePool ex;
-      ex.getMessage() << "Cannot list tapes because tape pool " + searchCriteria.tapePool.value() + " does not exist";
-      throw ex;
-    }
-
-    std::list<common::dataStructures::Tape> tapes;
-    std::string sql =
-      "SELECT "
-        "TAPE.VID AS VID,"
-        "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
-        "TAPE.VENDOR AS VENDOR,"
-        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
-        "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME,"
-        "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
-        "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
-        "TAPE.NB_MASTER_FILES AS NB_MASTER_FILES,"
-        "TAPE.MASTER_DATA_IN_BYTES AS MASTER_DATA_IN_BYTES,"
-        "TAPE.LAST_FSEQ AS LAST_FSEQ,"
-        "TAPE.IS_FULL AS IS_FULL,"
-        "TAPE.DIRTY AS DIRTY,"
-
-        "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR,"
-
-        "TAPE.LABEL_FORMAT AS LABEL_FORMAT,"
-
-        "TAPE.LABEL_DRIVE AS LABEL_DRIVE,"
-        "TAPE.LABEL_TIME AS LABEL_TIME,"
-
-        "TAPE.LAST_READ_DRIVE AS LAST_READ_DRIVE,"
-        "TAPE.LAST_READ_TIME AS LAST_READ_TIME,"
-
-        "TAPE.LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE,"
-        "TAPE.LAST_WRITE_TIME AS LAST_WRITE_TIME,"
-
-        "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
-        "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
-
-        "TAPE.VERIFICATION_STATUS AS VERIFICATION_STATUS,"
-
-        "TAPE.USER_COMMENT AS USER_COMMENT,"
-
-        "TAPE.TAPE_STATE AS TAPE_STATE,"
-        "TAPE.STATE_REASON AS STATE_REASON,"
-        "TAPE.STATE_UPDATE_TIME AS STATE_UPDATE_TIME,"
-        "TAPE.STATE_MODIFIED_BY AS STATE_MODIFIED_BY,"
-
-        "TAPE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "TAPE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "TAPE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "TAPE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "TAPE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "TAPE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "TAPE "
-      "INNER JOIN TAPE_POOL ON "
-        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-      "INNER JOIN LOGICAL_LIBRARY ON "
-        "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
-      "INNER JOIN MEDIA_TYPE ON "
-        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
-      "INNER JOIN VIRTUAL_ORGANIZATION ON "
-        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID";
-
-    if(searchCriteria.vid ||
-       searchCriteria.mediaType ||
-       searchCriteria.vendor ||
-       searchCriteria.logicalLibrary ||
-       searchCriteria.tapePool ||
-       searchCriteria.vo ||
-       searchCriteria.capacityInBytes ||
-       searchCriteria.full ||
-       searchCriteria.diskFileIds ||
-       searchCriteria.state ||
-       searchCriteria.fromCastor) {
-      sql += " WHERE";
-    }
-
-    bool addedAWhereConstraint = false;
-
-    if(searchCriteria.vid) {
-      sql += " TAPE.VID = :VID";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.mediaType) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.vendor) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " TAPE.VENDOR = :VENDOR";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.logicalLibrary) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.tapePool) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.vo) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME = :VO";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.capacityInBytes) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " MEDIA_TYPE.CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.full) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " TAPE.IS_FULL = :IS_FULL";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.diskFileIds) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " VID IN ("
-         "SELECT DISTINCT A.VID "
-         "FROM "
-           "TAPE_FILE A, ARCHIVE_FILE B "
-         "WHERE "
-           "A.ARCHIVE_FILE_ID = B.ARCHIVE_FILE_ID AND "
-           "B.DISK_FILE_ID IN (:DISK_FID0)"
-           //"B.DISK_FILE_ID IN (:DISK_FID0, :DISK_FID1, :DISK_FID2, :DISK_FID3, :DISK_FID4, :DISK_FID5, :DISK_FID6, :DISK_FID7, :DISK_FID8, :DISK_FID9)"
-         ")";
-      addedAWhereConstraint = true;
-    }
-
-    if(searchCriteria.state) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " TAPE.TAPE_STATE = :TAPE_STATE";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.fromCastor) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += " TAPE.IS_FROM_CASTOR = :FROM_CASTOR";
-      addedAWhereConstraint = true;
-    }
-
-    sql += " ORDER BY TAPE.VID";
-
-    auto stmt = conn.createStmt(sql);
-
-    if(searchCriteria.vid) stmt.bindString(":VID", searchCriteria.vid.value());
-    if(searchCriteria.mediaType) stmt.bindString(":MEDIA_TYPE", searchCriteria.mediaType.value());
-    if(searchCriteria.vendor) stmt.bindString(":VENDOR", searchCriteria.vendor.value());
-    if(searchCriteria.logicalLibrary) stmt.bindString(":LOGICAL_LIBRARY_NAME", searchCriteria.logicalLibrary.value());
-    if(searchCriteria.tapePool) stmt.bindString(":TAPE_POOL_NAME", searchCriteria.tapePool.value());
-    if(searchCriteria.vo) stmt.bindString(":VO", searchCriteria.vo.value());
-    if(searchCriteria.capacityInBytes) stmt.bindUint64(":CAPACITY_IN_BYTES", searchCriteria.capacityInBytes.value());
-    if(searchCriteria.full) stmt.bindBool(":IS_FULL", searchCriteria.full.value());
-    if(searchCriteria.fromCastor) stmt.bindBool(":FROM_CASTOR", searchCriteria.fromCastor.value());
-    try{
-      if(searchCriteria.state) stmt.bindString(":TAPE_STATE",cta::common::dataStructures::Tape::stateToString(searchCriteria.state.value()));
-    } catch(cta::exception::Exception &ex){
-      throw cta::exception::UserError(std::string("The state provided does not exist. Possible values are: ") + cta::common::dataStructures::Tape::getAllPossibleStates());
-    }
-
-    // Disk file ID lookup requires multiple queries
-    std::vector<std::string>::const_iterator diskFileId_it;
-    std::set<std::string> vidsInList;
-    if(searchCriteria.diskFileIds) diskFileId_it = searchCriteria.diskFileIds.value().begin();
-    int num_queries = searchCriteria.diskFileIds ? searchCriteria.diskFileIds.value().size() : 1;
-
-    for(int i = 0; i < num_queries; ++i) {
-      if(searchCriteria.diskFileIds) {
-        stmt.bindString(":DISK_FID0", *diskFileId_it++);
-      }
-
-      auto rset = stmt.executeQuery();
-      while (rset.next()) {
-        auto vid = rset.columnString("VID");
-        if(vidsInList.count(vid) == 1) continue;
-        vidsInList.insert(vid);
-
-        common::dataStructures::Tape tape;
-
-        tape.vid = vid;
-        tape.mediaType = rset.columnString("MEDIA_TYPE");
-        tape.vendor = rset.columnString("VENDOR");
-        tape.logicalLibraryName = rset.columnString("LOGICAL_LIBRARY_NAME");
-        tape.tapePoolName = rset.columnString("TAPE_POOL_NAME");
-        tape.vo = rset.columnString("VO");
-        tape.encryptionKeyName = rset.columnOptionalString("ENCRYPTION_KEY_NAME");
-        tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-        tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES");
-        tape.nbMasterFiles = rset.columnUint64("NB_MASTER_FILES");
-        tape.masterDataInBytes = rset.columnUint64("MASTER_DATA_IN_BYTES");
-        tape.lastFSeq = rset.columnUint64("LAST_FSEQ");
-        tape.full = rset.columnBool("IS_FULL");
-        tape.dirty = rset.columnBool("DIRTY");
-        tape.isFromCastor = rset.columnBool("IS_FROM_CASTOR");
-
-        tape.labelFormat = common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"), "[RdbmsCatalogue::getTapes()]");
-
-        tape.labelLog = getTapeLogFromRset(rset, "LABEL_DRIVE", "LABEL_TIME");
-        tape.lastReadLog = getTapeLogFromRset(rset, "LAST_READ_DRIVE", "LAST_READ_TIME");
-        tape.lastWriteLog = getTapeLogFromRset(rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME");
-
-        tape.readMountCount = rset.columnUint64("READ_MOUNT_COUNT");
-        tape.writeMountCount = rset.columnUint64("WRITE_MOUNT_COUNT");
-
-        tape.verificationStatus =  rset.columnOptionalString("VERIFICATION_STATUS");
-
-        auto optionalComment = rset.columnOptionalString("USER_COMMENT");
-        tape.comment = optionalComment ? optionalComment.value() : "";
-
-        tape.setState(rset.columnString("TAPE_STATE"));
-        tape.stateReason = rset.columnOptionalString("STATE_REASON");
-        tape.stateUpdateTime = rset.columnUint64("STATE_UPDATE_TIME");
-        tape.stateModifiedBy = rset.columnString("STATE_MODIFIED_BY");
-
-        tape.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-        tape.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-        tape.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-        tape.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-        tape.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-        tape.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-        tapes.push_back(tape);
-      }
-    }
-    if(searchCriteria.diskFileIds) {
-      // When searching by diskFileId, results are not guaranteed to be in sorted order
-      tapes.sort([](const common::dataStructures::Tape &a, const common::dataStructures::Tape &b) { return a.vid < b.vid; });
-    }
-
-    return tapes;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapesByVid (single VID)
-//------------------------------------------------------------------------------
-common::dataStructures::VidToTapeMap RdbmsCatalogue::getTapesByVid(const std::string& vid) const {
-  try {
-    const char* const sql =
-    "SELECT "
-      "TAPE.VID AS VID,"
-      "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
-      "TAPE.VENDOR AS VENDOR,"
-      "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
-      "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-      "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
-      "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME,"
-      "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
-      "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
-      "TAPE.LAST_FSEQ AS LAST_FSEQ,"
-      "TAPE.IS_FULL AS IS_FULL,"
-      "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR,"
-      "TAPE.LABEL_FORMAT AS LABEL_FORMAT,"
-      "TAPE.LABEL_DRIVE AS LABEL_DRIVE,"
-      "TAPE.LABEL_TIME AS LABEL_TIME,"
-      "TAPE.LAST_READ_DRIVE AS LAST_READ_DRIVE,"
-      "TAPE.LAST_READ_TIME AS LAST_READ_TIME,"
-      "TAPE.LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE,"
-      "TAPE.LAST_WRITE_TIME AS LAST_WRITE_TIME,"
-      "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
-      "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
-      "TAPE.USER_COMMENT AS USER_COMMENT,"
-      "TAPE.TAPE_STATE AS TAPE_STATE,"
-      "TAPE.STATE_REASON AS STATE_REASON,"
-      "TAPE.STATE_UPDATE_TIME AS STATE_UPDATE_TIME,"
-      "TAPE.STATE_MODIFIED_BY AS STATE_MODIFIED_BY,"
-      "TAPE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-      "TAPE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-      "TAPE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-      "TAPE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-      "TAPE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-      "TAPE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-    "FROM "
-      "TAPE "
-    "INNER JOIN TAPE_POOL ON "
-      "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-    "INNER JOIN LOGICAL_LIBRARY ON "
-      "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
-    "INNER JOIN MEDIA_TYPE ON "
-      "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
-    "INNER JOIN VIRTUAL_ORGANIZATION ON "
-      "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-    "WHERE "
-      "VID = :VID";
-
-    common::dataStructures::VidToTapeMap vidToTapeMap;
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    executeGetTapesByVidStmtAndCollectResults(stmt, vidToTapeMap);
-
-    if(vidToTapeMap.size() != 1) {
-      exception::Exception ex;
-      ex.getMessage() << "Not all tapes were found: expected=1 actual=" << vidToTapeMap.size();
-      throw ex;
-    }
-    return vidToTapeMap;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapesByVid (set of VIDs)
-//------------------------------------------------------------------------------
-common::dataStructures::VidToTapeMap RdbmsCatalogue::getTapesByVid(const std::set<std::string> &vids) const {
-  try {
-    common::dataStructures::VidToTapeMap vidToTapeMap;
-
-    if(vids.empty()) return vidToTapeMap;
-
-    static const std::string selectTapesBy100VidsSql = getSelectTapesBy100VidsSql();
-
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(selectTapesBy100VidsSql);
-    uint64_t vidNb = 1;
-
-    for(const auto &vid: vids) {
-      // Bind the current tape VID
-      std::ostringstream paramName;
-      paramName << ":V" << vidNb;
-      stmt.bindString(paramName.str(), vid);
-
-      // If the 100th tape VID has not yet been reached
-      if(100 > vidNb) {
-        vidNb++;
-      } else { // The 100th VID has been reached
-        vidNb = 1;
-
-        // Execute the query and collect the results
-        executeGetTapesByVidStmtAndCollectResults(stmt, vidToTapeMap);
-
-        // Create a new statement
-        stmt = conn.createStmt(selectTapesBy100VidsSql);
-      }
-    }
-
-    // If there is a statement under construction
-    if(1 != vidNb) {
-      // Bind the remaining parameters with last tape VID.  This has no effect
-      // on the search results but makes the statement valid.
-      const std::string &lastVid = *vids.rbegin();
-      while(100 >= vidNb) {
-        std::ostringstream paramName;
-        paramName << ":V" << vidNb;
-        stmt.bindString(paramName.str(), lastVid);
-        vidNb++;
-      }
-
-      // Execute the query and collect the results
-      executeGetTapesByVidStmtAndCollectResults(stmt, vidToTapeMap);
-    }
-
-    if(vids.size() != vidToTapeMap.size()) {
-      exception::Exception ex;
-      ex.getMessage() << "Not all tapes were found: expected=" << vids.size() << " actual=" << vidToTapeMap.size();
-      throw ex;
-    }
-
-    return vidToTapeMap;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getSelectTapesBy100VidsSql
-//------------------------------------------------------------------------------
-std::string RdbmsCatalogue::getSelectTapesBy100VidsSql() const {
-  std::stringstream sql;
-
-  sql <<
-    "SELECT "
-      "TAPE.VID AS VID,"
-      "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
-      "TAPE.VENDOR AS VENDOR,"
-      "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
-      "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-      "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
-      "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME,"
-      "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
-      "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
-      "TAPE.LAST_FSEQ AS LAST_FSEQ,"
-      "TAPE.IS_FULL AS IS_FULL,"
-      "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR,"
-
-      "TAPE.LABEL_FORMAT AS LABEL_FORMAT,"
-
-      "TAPE.LABEL_DRIVE AS LABEL_DRIVE,"
-      "TAPE.LABEL_TIME AS LABEL_TIME,"
-
-      "TAPE.LAST_READ_DRIVE AS LAST_READ_DRIVE,"
-      "TAPE.LAST_READ_TIME AS LAST_READ_TIME,"
-
-      "TAPE.LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE,"
-      "TAPE.LAST_WRITE_TIME AS LAST_WRITE_TIME,"
-
-      "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
-      "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
-
-      "TAPE.USER_COMMENT AS USER_COMMENT,"
-
-      "TAPE.TAPE_STATE AS TAPE_STATE,"
-      "TAPE.STATE_REASON AS STATE_REASON,"
-      "TAPE.STATE_UPDATE_TIME AS STATE_UPDATE_TIME,"
-      "TAPE.STATE_MODIFIED_BY AS STATE_MODIFIED_BY,"
-
-      "TAPE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-      "TAPE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-      "TAPE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-      "TAPE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-      "TAPE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-      "TAPE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-    "FROM "
-      "TAPE "
-    "INNER JOIN TAPE_POOL ON "
-      "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-    "INNER JOIN LOGICAL_LIBRARY ON "
-      "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
-    "INNER JOIN MEDIA_TYPE ON "
-      "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
-    "INNER JOIN VIRTUAL_ORGANIZATION ON "
-      "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-    "WHERE "
-      "VID IN (:V1";
-
-  for(uint32_t i=2; i<=100; i++) {
-    sql << ",:V" << i;
-  }
-
-  sql << ")";
-
-  return sql.str();
-}
-
-//------------------------------------------------------------------------------
-// executeGetTapesByVidStmtAndCollectResults
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::executeGetTapesByVidStmtAndCollectResults(rdbms::Stmt &stmt,
-  common::dataStructures::VidToTapeMap &vidToTapeMap) const {
-  auto rset = stmt.executeQuery();
-  while (rset.next()) {
-    common::dataStructures::Tape tape;
-
-    tape.vid = rset.columnString("VID");
-    tape.mediaType = rset.columnString("MEDIA_TYPE");
-    tape.vendor = rset.columnString("VENDOR");
-    tape.logicalLibraryName = rset.columnString("LOGICAL_LIBRARY_NAME");
-    tape.tapePoolName = rset.columnString("TAPE_POOL_NAME");
-    tape.vo = rset.columnString("VO");
-    tape.encryptionKeyName = rset.columnOptionalString("ENCRYPTION_KEY_NAME");
-    tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-    tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES");
-    tape.lastFSeq = rset.columnUint64("LAST_FSEQ");
-    tape.full = rset.columnBool("IS_FULL");
-    tape.isFromCastor = rset.columnBool("IS_FROM_CASTOR");
-
-    tape.labelFormat = common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"), "[RdbmsCatalogue::executeGetTapesByVidsStmtAndCollectResults()]");
-
-    tape.labelLog = getTapeLogFromRset(rset, "LABEL_DRIVE", "LABEL_TIME");
-    tape.lastReadLog = getTapeLogFromRset(rset, "LAST_READ_DRIVE", "LAST_READ_TIME");
-    tape.lastWriteLog = getTapeLogFromRset(rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME");
-    tape.readMountCount = rset.columnUint64("READ_MOUNT_COUNT");
-    tape.writeMountCount = rset.columnUint64("WRITE_MOUNT_COUNT");
-    auto optionalComment = rset.columnOptionalString("USER_COMMENT");
-    tape.comment = optionalComment ? optionalComment.value() : "";
-
-    tape.setState(rset.columnString("TAPE_STATE"));
-    tape.stateReason = rset.columnOptionalString("STATE_REASON");
-    tape.stateUpdateTime = rset.columnUint64("STATE_UPDATE_TIME");
-    tape.stateModifiedBy = rset.columnString("STATE_MODIFIED_BY");
-
-    tape.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-    tape.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-    tape.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-    tape.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-    tape.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-    tape.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-    vidToTapeMap[tape.vid] = tape;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getVidToLogicalLibrary
-//------------------------------------------------------------------------------
-std::map<std::string, std::string> RdbmsCatalogue::getVidToLogicalLibrary(const std::set<std::string> &vids) const {
-  try {
-    std::map<std::string, std::string> vidToLogicalLibrary;
-
-    if(vids.empty()) return vidToLogicalLibrary;
-
-    static const std::string sql = getSelectVidToLogicalLibraryBy100Sql();
-
-    auto conn = m_connPool.getConn();
-
-    auto stmt = conn.createStmt(sql);
-    uint64_t vidNb = 1;
-
-    for(const auto &vid: vids) {
-      // Bind the current tape VID
-      std::ostringstream paramName;
-      paramName << ":V" << vidNb;
-      stmt.bindString(paramName.str(), vid);
-
-      // If the 100th tape VID has not yet been reached
-      if(100 > vidNb) {
-        vidNb++;
-      } else { // The 100th VID has been reached
-        vidNb = 1;
-
-        // Execute the query and collect the results
-        executeGetVidToLogicalLibraryBy100StmtAndCollectResults(stmt, vidToLogicalLibrary);
-
-        // Create a new statement
-        stmt = conn.createStmt(sql);
-      }
-    }
-
-    // If there is a statement under construction
-    if(1 != vidNb) {
-      // Bind the remaining parameters with last tape VID.  This has no effect
-      // on the search results but makes the statement valid.
-      const std::string &lastVid = *vids.rbegin();
-      while(100 >= vidNb) {
-        std::ostringstream paramName;
-        paramName << ":V" << vidNb;
-        stmt.bindString(paramName.str(), lastVid);
-        vidNb++;
-      }
-
-      // Execute the query and collect the results
-      executeGetVidToLogicalLibraryBy100StmtAndCollectResults(stmt, vidToLogicalLibrary);
-    }
-
-    if(vids.size() != vidToLogicalLibrary.size()) {
-      exception::Exception ex;
-      ex.getMessage() << "Not all tapes were found: expected=" << vids.size() << " actual=" <<
-        vidToLogicalLibrary.size();
-      throw ex;
-    }
-
-    return vidToLogicalLibrary;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// getSelectVidToLogicalLibraryBy100Sql
-//------------------------------------------------------------------------------
-std::string RdbmsCatalogue::getSelectVidToLogicalLibraryBy100Sql() const {
-  std::stringstream sql;
-
-  sql <<
-    "SELECT"                                                         "\n"
-      "TAPE.VID AS VID,"                                             "\n"
-      "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME" "\n"
-    "FROM"                                                           "\n"
-      "TAPE"                                                         "\n"
-    "INNER JOIN LOGICAL_LIBRARY ON"                                  "\n"
-      "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID" "\n"
-    "WHERE"                                                          "\n"
-      "VID IN (:V1";
-
-  for(uint32_t i=2; i<=100; i++) {
-    sql << ",:V" << i;
-  }
-
-  sql << ")";
-
-  return sql.str();
-}
-
-//------------------------------------------------------------------------------
-// executeGetVidToLogicalLibraryBy100StmtAndCollectResults
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::executeGetVidToLogicalLibraryBy100StmtAndCollectResults(rdbms::Stmt &stmt,
-std::map<std::string, std::string> &vidToLogicalLibrary) const {
-  auto rset = stmt.executeQuery();
-  while (rset.next()) {
-    vidToLogicalLibrary[rset.columnString("VID")] = rset.columnString("LOGICAL_LIBRARY_NAME");
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNbFilesOnTape
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::getNbFilesOnTape(const std::string& vid) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getNbFilesOnTape(conn, vid);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-//getNbFilesOnTape
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::getNbFilesOnTape(rdbms::Conn& conn, const std::string& vid) const {
-  try {
-    const char *const sql =
-    "SELECT COUNT(*) AS NB_FILES FROM TAPE_FILE "
-    "WHERE VID = :VID ";
-
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    rset.next();
-    return rset.columnUint64("NB_FILES");
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-//deleteTapeFiles
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteTapeFiles(rdbms::Conn& conn, const std::string& vid) const {
-  try {
-    const char * const sql =
-    "DELETE FROM TAPE_FILE WHERE VID = :VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-    setTapeDirty(conn,vid);
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-//setTapeDirty
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeDirty(rdbms::Conn& conn, const std::string& vid) const {
-  try {
-    const char * const sql =
-    "UPDATE TAPE SET DIRTY='1' WHERE VID = :VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-//setTapeDirty
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeDirty(rdbms::Conn& conn, const uint64_t & archiveFileId) const {
-  try {
-    const char * const sql =
-    "UPDATE TAPE SET DIRTY='1' "
-    "WHERE VID IN "
-    "  (SELECT DISTINCT TAPE_FILE.VID AS VID FROM TAPE_FILE WHERE TAPE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID)";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    stmt.executeNonQuery();
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-//resetTapeCounters
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::resetTapeCounters(rdbms::Conn& conn, const common::dataStructures::SecurityIdentity& admin, const std::string& vid) const {
-  try {
-    const time_t now = time(nullptr);
-    const char * const sql =
-    "UPDATE TAPE SET "
-        "DATA_IN_BYTES = 0,"
-        "MASTER_DATA_IN_BYTES = 0,"
-        "LAST_FSEQ = 0,"
-        "NB_MASTER_FILES = 0,"
-        "NB_COPY_NB_1 = 0,"
-        "COPY_NB_1_IN_BYTES = 0,"
-        "NB_COPY_NB_GT_1 = 0,"
-        "COPY_NB_GT_1_IN_BYTES = 0,"
-        "IS_FULL = '0',"
-        "IS_FROM_CASTOR = '0',"
-        "VERIFICATION_STATUS = :VERIFICATION_STATUS,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME,"
-        "DIRTY = '0' "
-      "WHERE "
-        "VID = :VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VERIFICATION_STATUS", std::nullopt);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-  } catch (exception::Exception &ex){
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// reclaimTape
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, cta::log::LogContext & lc) {
-  using namespace common::dataStructures;
-  try{
-    log::TimingList tl;
-    utils::Timer t;
-    auto conn = m_connPool.getConn();
-
-    TapeSearchCriteria searchCriteria;
-    searchCriteria.vid = vid;
-    const auto tapes = getTapes(conn, searchCriteria);
-    tl.insertAndReset("getTapesTime", t);
-
-    if (tapes.empty()) {
-      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it does not exist");
-    } else if (tapes.front().state != Tape::State::ACTIVE && tapes.front().state != Tape::State::DISABLED) {
-      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it is not on ACTIVE or DISABLED state");
-    } else if (!tapes.front().full) {
-      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it is not FULL");
-    }
-
-    // The tape exists and is full, we can try to reclaim it
-    if (this->getNbFilesOnTape(conn, vid) == 0) {
-      tl.insertAndReset("getNbFilesOnTape", t);
-      // There is no files on the tape, we can reclaim it : delete the files and reset the counters
-      deleteFilesFromRecycleLog(conn, vid, lc);
-      tl.insertAndReset("deleteFileFromRecycleLogTime", t);
-      resetTapeCounters(conn, admin, vid);
-      tl.insertAndReset("resetTapeCountersTime", t);
-      log::ScopedParamContainer spc(lc);
-      spc.add("vid", vid);
-      spc.add("host", admin.host);
-      spc.add("username", admin.username);
-      tl.addToLog(spc);
-      lc.log(log::INFO, "In RdbmsCatalogue::reclaimTape(), tape reclaimed.");
-    } else {
-      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because there is at least one tape"
-            " file in the catalogue that is on the tape");
-    }
-  } catch (exception::UserError& ue) {
-    throw;
-  }
-  catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// checkTapeForLabel
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkTapeForLabel(const std::string &vid) {
-   try{
-    auto conn = m_connPool.getConn();
-
-    TapeSearchCriteria searchCriteria;
-    searchCriteria.vid = vid;
-    const auto tapes = getTapes(conn, searchCriteria);
-
-    if(tapes.empty()) {
-      throw exception::UserError(std::string("Cannot label tape ") + vid +
-                                             " because it does not exist");
-    }
-    //The tape exists checks any files on it
-    const uint64_t nbFilesOnTape = getNbFilesOnTape(conn, vid);
-    if( 0 != nbFilesOnTape) {
-      throw exception::UserError(std::string("Cannot label tape ") + vid +
-                                             " because it has " +
-                                             std::to_string(nbFilesOnTape) +
-                                             " file(s)");
-    }
-  } catch (exception::UserError& ue) {
-    throw;
-  }
-  catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapeLogFromRset
-//------------------------------------------------------------------------------
-std::optional<common::dataStructures::TapeLog> RdbmsCatalogue::getTapeLogFromRset(const rdbms::Rset &rset,
-  const std::string &driveColName, const std::string &timeColName) const {
-  try {
-    const std::optional<std::string> drive = rset.columnOptionalString(driveColName);
-    const std::optional<uint64_t> time = rset.columnOptionalUint64(timeColName);
-
-    if(!drive && !time) {
-      return std::nullopt;
-    }
-
-    if(drive && !time) {
-      throw exception::Exception(std::string("Database column ") + driveColName + " contains " + drive.value() +
-        " but column " + timeColName + " is nullptr");
-    }
-
-    if(time && !drive) {
-      throw exception::Exception(std::string("Database column ") + timeColName + " contains " +
-        std::to_string(time.value()) + " but column " + driveColName + " is nullptr");
-    }
-
-    common::dataStructures::TapeLog tapeLog;
-    tapeLog.drive = drive.value();
-    tapeLog.time = time.value();
-
-    return tapeLog;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapeMediaType
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string &mediaType) {
-  try {
-    auto conn = m_connPool.getConn();
-    if(!mediaTypeExists(conn, mediaType)){
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because the media type " + mediaType + " does not exist");
-    }
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "MEDIA_TYPE_ID = (SELECT MEDIA_TYPE_ID FROM MEDIA_TYPE WHERE MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE),"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MEDIA_TYPE", mediaType);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("mediaType", mediaType)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - mediaType");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapeVendor
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string &vendor) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "VENDOR = :VENDOR,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VENDOR", vendor);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("vendor", vendor)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - vendor");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapeLogicalLibraryName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string &logicalLibraryName) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "LOGICAL_LIBRARY_ID = "
-          "(SELECT LOGICAL_LIBRARY_ID FROM LOGICAL_LIBRARY WHERE LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME),"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    if(!logicalLibraryExists(conn,logicalLibraryName)){
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because the logical library " + logicalLibraryName + " does not exist");
-    }
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because either it or logical library " +
-        logicalLibraryName + " does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("logicalLibraryName", logicalLibraryName)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - logicalLibraryName");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapeTapePoolName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string &tapePoolName) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "TAPE_POOL_ID = (SELECT TAPE_POOL_ID FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME),"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    if(!tapePoolExists(conn,tapePoolName)){
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because the tape pool " + tapePoolName + " does not exist");
-    }
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because either it or tape pool " +
-        tapePoolName + " does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("tapePoolName", tapePoolName)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - tapePoolName");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapeEncryptionKeyName
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string &encryptionKeyName) {
-  try {
-    std::optional<std::string> optionalEncryptionKeyName;
-    if(!encryptionKeyName.empty()) {
-      optionalEncryptionKeyName = encryptionKeyName;
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "ENCRYPTION_KEY_NAME = :ENCRYPTION_KEY_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":ENCRYPTION_KEY_NAME", optionalEncryptionKeyName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("encryptionKeyName", optionalEncryptionKeyName ? optionalEncryptionKeyName.value() : "NULL")
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - encryptionKeyName");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyTapeVerificationStatus
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string &verificationStatus) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "VERIFICATION_STATUS = :VERIFICATION_STATUS,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    if (verificationStatus.empty()) {
-      stmt.bindString(":VERIFICATION_STATUS", std::nullopt);
-    } else {
-      stmt.bindString(":VERIFICATION_STATUS", verificationStatus);
-    }
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("verificationStatus", verificationStatus)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - verificationStatus");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// modifyTapeState
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const std::optional<common::dataStructures::Tape::State> & prev_state, const std::optional<std::string> & stateReason){
-  try {
-    using namespace common::dataStructures;
-    const time_t now = time(nullptr);
-
-    const std::optional<std::string> stateReasonCopy = stateReason && cta::utils::trimString(stateReason.value()).empty() ? std::nullopt : stateReason;
-    checkCommentOrReasonMaxLength(stateReasonCopy);
-
-    std::string stateStr;
-    try {
-      stateStr = cta::common::dataStructures::Tape::stateToString(state);
-    } catch(cta::exception::Exception & ex){
-      std::string errorMsg = "The state provided in parameter (" + std::to_string(state) + ") is not known or has not been initialized existing states are:" + common::dataStructures::Tape::getAllPossibleStates();
-      throw UserSpecifiedANonExistentTapeState(errorMsg);
-    }
-
-    std::string prevStateStr;
-    if (prev_state.has_value()) {
-      try {
-        prevStateStr = cta::common::dataStructures::Tape::stateToString(prev_state.value());
-      } catch (cta::exception::Exception &ex) {
-        std::string errorMsg = "The previous state provided in parameter (" + std::to_string(prev_state.value()) + ") is not known or has not been initialized existing states are:" + common::dataStructures::Tape::getAllPossibleStates();
-        throw UserSpecifiedANonExistentTapeState(errorMsg);
-      }
-    }
-
-    //Check the reason is set for all the status except the ACTIVE one, this is the only state that allows the reason to be set to null.
-    if(state != Tape::State::ACTIVE){
-      if(!stateReasonCopy){
-        throw UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive(std::string("Cannot modify the state of the tape ") + vid + " to " + stateStr + " because the reason has not been provided.");
-      }
-    }
-
-    std::string sql =
-      "UPDATE TAPE SET "
-        "TAPE_STATE = :TAPE_STATE,"
-        "STATE_REASON = :STATE_REASON,"
-        "STATE_UPDATE_TIME = :STATE_UPDATE_TIME,"
-        "STATE_MODIFIED_BY = :STATE_MODIFIED_BY "
-      "WHERE "
-        "VID = :VID";
-
-    if (prev_state.has_value()) {
-      sql += " AND TAPE_STATE = :PREV_TAPE_STATE";
-    }
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":TAPE_STATE", stateStr);
-    stmt.bindString(":STATE_REASON", stateReasonCopy);
-    stmt.bindUint64(":STATE_UPDATE_TIME", now);
-    stmt.bindString(":STATE_MODIFIED_BY",generateTapeStateModifiedBy(admin));
-    stmt.bindString(":VID",vid);
-    if (prev_state.has_value()) {
-      stmt.bindString(":PREV_TAPE_STATE",prevStateStr);
-    }
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentTape(std::string("Cannot modify the state of the tape ") + vid + " because it does not exist or because a recent state change has been detected");
-    }
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::string RdbmsCatalogue::generateTapeStateModifiedBy(const common::dataStructures::SecurityIdentity & admin){
-  return admin.username + "@" + admin.host;
-}
-
-//------------------------------------------------------------------------------
-// tapeMountedForArchive
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::tapeMountedForArchive(const std::string &vid, const std::string &drive) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE,"
-        "LAST_WRITE_TIME = :LAST_WRITE_TIME, "
-        "WRITE_MOUNT_COUNT = WRITE_MOUNT_COUNT + 1 "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LAST_WRITE_DRIVE", drive);
-    stmt.bindUint64(":LAST_WRITE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("lastWriteDrive", drive)
-       .add("lastWriteTime", now);
-    lc.log(log::INFO, "Catalogue - system modified tape - mountedForArchive");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapeMountedForRetrieve
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::tapeMountedForRetrieve(const std::string &vid, const std::string &drive) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "LAST_READ_DRIVE = :LAST_READ_DRIVE,"
-        "LAST_READ_TIME = :LAST_READ_TIME, "
-        "READ_MOUNT_COUNT = READ_MOUNT_COUNT + 1 "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LAST_READ_DRIVE", drive);
-    stmt.bindUint64(":LAST_READ_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("lastReadDrive", drive)
-       .add("lastReadTime", now);
-    lc.log(log::INFO, "Catalogue - system modified tape - mountedForRetrieve");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapeFull
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
-  const bool fullValue) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "IS_FULL = :IS_FULL,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindBool(":IS_FULL", fullValue);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("isFull", fullValue ? 1 : 0)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - isFull");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapeDirty
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
-  const bool dirtyValue) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "DIRTY = :DIRTY,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindBool(":DIRTY", dirtyValue);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("dirty", dirtyValue ? 1 : 0)
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - dirty");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// noSpaceLeftOnTape
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::noSpaceLeftOnTape(const std::string &vid) {
-  try {
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "IS_FULL = '1' "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw exception::Exception(std::string("Tape ") + vid + " does not exist");
-    }
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("isFull", 1)
-       .add("method", "noSpaceLeftOnTape");
-    lc.log(log::INFO, "Catalogue - system modified tape - isFull");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapeIsFromCastorInUnitTests
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeIsFromCastorInUnitTests(const std::string &vid) {
-  try {
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "IS_FROM_CASTOR = '1' "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw exception::Exception(std::string("Tape ") + vid + " does not exist");
-    }
-
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("isFromCastor", 1)
-       .add("method", "setTapeIsFromCastorInUnitTests");
-    lc.log(log::INFO, "Catalogue - system modified tape - isFromCastor");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapeDisabled
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeDisabled(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::string & reason) {
-
-  try {
-    modifyTapeState(admin,vid,common::dataStructures::Tape::DISABLED,std::nullopt,reason);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapeRepackingDisabled
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin,
-                                     const std::string &vid, const std::string & reason) {
-
-  try {
-    modifyTapeState(admin,vid,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt,reason);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::setTapeDirty(const std::string& vid) {
-  try {
-    auto conn = m_connPool.getConn();
-    setTapeDirty(conn,vid);
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// modifyTapeComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyTapeComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &vid, const std::optional<std::string> &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", vid)
-       .add("userComment", comment ? comment.value() : "")
-       .add("lastUpdateUserName", admin.username)
-       .add("lastUpdateHostName", admin.host)
-       .add("lastUpdateTime", now);
-    lc.log(log::INFO, "Catalogue - user modified tape - userComment");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyRequesterActivityMountRulePolicy
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex, const std::string &mountPolicy) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE REQUESTER_ACTIVITY_MOUNT_RULE SET "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME AND "
-        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicy);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify requester  activity mount rule ") + instanceName + ":" +
-        requesterName + "for activities matching " + activityRegex + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyRequesterActivityMountRuleComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE REQUESTER_ACTIVITY_MOUNT_RULE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME AND "
-        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify requester  activity mount rule ") + instanceName + ":" +
-        requesterName + "for activities matching " + activityRegex + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyRequesterMountRulePolicy
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE REQUESTER_MOUNT_RULE SET "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicy);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify requester mount rule ") + instanceName + ":" +
-        requesterName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyRequesteMountRuleComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &instanceName, const std::string &requesterName, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE REQUESTER_MOUNT_RULE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify requester mount rule ") + instanceName + ":" +
-        requesterName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyRequesterGroupMountRulePolicy
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE REQUESTER_GROUP_MOUNT_RULE SET "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicy);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify requester group mount rule ") + instanceName + ":" +
-        requesterGroupName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyRequesterGroupMountRuleComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE REQUESTER_GROUP_MOUNT_RULE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify requester group mount rule ") + instanceName + ":" +
-        requesterGroupName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createMountPolicy
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createMountPolicy(const common::dataStructures::SecurityIdentity &admin, const CreateMountPolicyAttributes & mountPolicy) {
-  std::string name = mountPolicy.name;
-  try {
-    checkCommentOrReasonMaxLength(mountPolicy.comment);
-    auto conn = m_connPool.getConn();
-    if (mountPolicyExists(conn, name)) {
-      throw exception::UserError(std::string("Cannot create mount policy ") + name +
-        " because a mount policy with the same name already exists");
-    }
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO MOUNT_POLICY("
-        "MOUNT_POLICY_NAME,"
-
-        "ARCHIVE_PRIORITY,"
-        "ARCHIVE_MIN_REQUEST_AGE,"
-
-        "RETRIEVE_PRIORITY,"
-        "RETRIEVE_MIN_REQUEST_AGE,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":MOUNT_POLICY_NAME,"
-
-        ":ARCHIVE_PRIORITY,"
-        ":ARCHIVE_MIN_REQUEST_AGE,"
-
-        ":RETRIEVE_PRIORITY,"
-        ":RETRIEVE_MIN_REQUEST_AGE,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-
-    stmt.bindUint64(":ARCHIVE_PRIORITY", mountPolicy.archivePriority);
-    stmt.bindUint64(":ARCHIVE_MIN_REQUEST_AGE", mountPolicy.minArchiveRequestAge);
-
-    stmt.bindUint64(":RETRIEVE_PRIORITY", mountPolicy.retrievePriority);
-    stmt.bindUint64(":RETRIEVE_MIN_REQUEST_AGE", mountPolicy.minRetrieveRequestAge);
-
-    stmt.bindString(":USER_COMMENT", mountPolicy.comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// createRequesterActivityMountRule
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createRequesterActivityMountRule(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &mountPolicyName,
-  const std::string &diskInstanceName,
-  const std::string &requesterName,
-  const std::string &activityRegex,
-  const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    auto conn = m_connPool.getConn();
-    if(requesterActivityMountRuleExists(conn, diskInstanceName, requesterName, activityRegex)) {
-      throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName +
-        " to requester " + diskInstanceName + ":" + requesterName + " for activities matching " + activityRegex +
-        " because that requester-activity mount rule already exists");
-    }
-    if(!mountPolicyExists(conn, mountPolicyName)) {
-      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
-        " to requester " + diskInstanceName + ":" + requesterName + " for activities matching " + activityRegex +
-        " because mount-policy " + mountPolicyName + " does not exist");
-    }
-    if(!diskInstanceExists(conn, diskInstanceName)) {
-      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
-        " to requester " + diskInstanceName + ":" + requesterName + " for activities matching " + activityRegex +
-        " because disk-instance " + diskInstanceName + " does not exist");
-    }
-
-    const uint64_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO REQUESTER_ACTIVITY_MOUNT_RULE("
-        "DISK_INSTANCE_NAME,"
-        "REQUESTER_NAME,"
-        "MOUNT_POLICY_NAME,"
-        "ACTIVITY_REGEX,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":DISK_INSTANCE_NAME,"
-        ":REQUESTER_NAME,"
-        ":MOUNT_POLICY_NAME,"
-        ":ACTIVITY_REGEX,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
-    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_userMountPolicyCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// getRequesterMountRules
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::RequesterActivityMountRule> RdbmsCatalogue::getRequesterActivityMountRules() const {
-  try {
-    std::list<common::dataStructures::RequesterActivityMountRule> rules;
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "REQUESTER_NAME AS REQUESTER_NAME,"
-        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-        "ACTIVITY_REGEX AS ACTIVITY_REGEX,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_ACTIVITY_MOUNT_RULE "
-      "ORDER BY "
-        "DISK_INSTANCE_NAME, REQUESTER_NAME, ACTIVITY_REGEX, MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while(rset.next()) {
-      common::dataStructures::RequesterActivityMountRule rule;
-
-      rule.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      rule.name = rset.columnString("REQUESTER_NAME");
-      rule.mountPolicy = rset.columnString("MOUNT_POLICY_NAME");
-      rule.activityRegex = rset.columnString("ACTIVITY_REGEX");
-      rule.comment = rset.columnString("USER_COMMENT");
-      rule.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      rule.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      rule.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      rule.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      rule.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      rule.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      rules.push_back(rule);
-    }
-
-    return rules;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteRequesterActivityMountRule
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName, const std::string &activityRegex) {
-  try {
-    const char *const sql =
-      "DELETE FROM "
-        "REQUESTER_ACTIVITY_MOUNT_RULE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME AND "
-        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete mount rule for requester ") + diskInstanceName + ":" + requesterName +
-        " and activity regex " + activityRegex + " because the rule does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_userMountPolicyCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// createRequesterMountRule
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createRequesterMountRule(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &mountPolicyName,
-  const std::string &diskInstanceName,
-  const std::string &requesterName,
-  const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const auto user = User(diskInstanceName, requesterName);
-    auto conn = m_connPool.getConn();
-    const auto mountPolicy = getRequesterMountPolicy(conn, user);
-    if(mountPolicy) {
-      throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName +
-        " to requester " + diskInstanceName + ":" + requesterName +
-        " because the requester is already assigned to mount-policy " + mountPolicy->name);
-    }
-    if(!mountPolicyExists(conn, mountPolicyName)) {
-      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
-        " to requester " + diskInstanceName + ":" + requesterName + " because mount-policy " + mountPolicyName +
-        " does not exist");
-    }
-    if(!diskInstanceExists(conn, diskInstanceName)) {
-      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
-        " to requester " + diskInstanceName + ":" + requesterName + " because disk-instance " + diskInstanceName +
-        " does not exist");
-    }
-
-    const uint64_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO REQUESTER_MOUNT_RULE("
-        "DISK_INSTANCE_NAME,"
-        "REQUESTER_NAME,"
-        "MOUNT_POLICY_NAME,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":DISK_INSTANCE_NAME,"
-        ":REQUESTER_NAME,"
-        ":MOUNT_POLICY_NAME,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_userMountPolicyCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// getRequesterMountRules
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::RequesterMountRule> RdbmsCatalogue::getRequesterMountRules() const {
-  try {
-    std::list<common::dataStructures::RequesterMountRule> rules;
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "REQUESTER_NAME AS REQUESTER_NAME,"
-        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_MOUNT_RULE "
-      "ORDER BY "
-        "DISK_INSTANCE_NAME, REQUESTER_NAME, MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while(rset.next()) {
-      common::dataStructures::RequesterMountRule rule;
-
-      rule.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      rule.name = rset.columnString("REQUESTER_NAME");
-      rule.mountPolicy = rset.columnString("MOUNT_POLICY_NAME");
-      rule.comment = rset.columnString("USER_COMMENT");
-      rule.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      rule.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      rule.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      rule.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      rule.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      rule.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      rules.push_back(rule);
-    }
-
-    return rules;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteRequesterMountRule
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) {
-  try {
-    const char *const sql =
-      "DELETE FROM "
-        "REQUESTER_MOUNT_RULE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete mount rule for requester ") + diskInstanceName + ":" + requesterName +
-        " because the rule does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_userMountPolicyCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// createRequesterGroupMountRule
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createRequesterGroupMountRule(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &mountPolicyName,
-  const std::string &diskInstanceName,
-  const std::string &requesterGroupName,
-  const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    auto conn = m_connPool.getConn();
-    {
-      const auto group = Group(diskInstanceName, requesterGroupName);
-      const auto mountPolicy = getRequesterGroupMountPolicy(conn, group);
-      if (mountPolicy) {
-        throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName +
-                                   " to requester-group " + diskInstanceName + ":" + requesterGroupName +
-                                   " because a rule already exists assigning the requester-group to mount-policy " +
-                                   mountPolicy->name);
-      }
-    }
-    if(!mountPolicyExists(conn, mountPolicyName)) {
-      throw exception::UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester-group " +
-        diskInstanceName + ":" + requesterGroupName + " because mount-policy " + mountPolicyName + " does not exist");
-    }
-    if(!diskInstanceExists(conn, diskInstanceName)) {
-      throw exception::UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester-group " +
-        diskInstanceName + ":" + requesterGroupName + " because disk-instance " + diskInstanceName + " does not exist");
-    }
-
-    const uint64_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO REQUESTER_GROUP_MOUNT_RULE("
-        "DISK_INSTANCE_NAME,"
-        "REQUESTER_GROUP_NAME,"
-        "MOUNT_POLICY_NAME,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":DISK_INSTANCE_NAME,"
-        ":REQUESTER_GROUP_NAME,"
-        ":MOUNT_POLICY_NAME,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
-
-    stmt.bindString(":USER_COMMENT", comment);
-
-    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-    stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// getCachedRequesterGroupMountPolicy
-//------------------------------------------------------------------------------
-ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy> > RdbmsCatalogue::getCachedRequesterGroupMountPolicy(const Group &group)
-  const {
-  try {
-    auto getNonCachedValue = [&] {
-      auto conn = m_connPool.getConn();
-      return getRequesterGroupMountPolicy(conn, group);
-    };
-    return m_groupMountPolicyCache.getCachedValue(group, getNonCachedValue);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getRequesterGroupMountPolicy
-//------------------------------------------------------------------------------
-std::optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getRequesterGroupMountPolicy(
-  rdbms::Conn &conn, const Group &group) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "MOUNT_POLICY "
-      "INNER JOIN "
-        "REQUESTER_GROUP_MOUNT_RULE "
-      "ON "
-        "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", group.diskInstanceName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", group.groupName);
-    auto rset = stmt.executeQuery();
-    if(rset.next()) {
-      common::dataStructures::MountPolicy policy;
-
-      policy.name = rset.columnString("MOUNT_POLICY_NAME");
-
-      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
-
-      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
-
-      policy.comment = rset.columnString("USER_COMMENT");
-      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      return policy;
-    } else {
-      return std::nullopt;
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getRequesterGroupMountRules
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::RequesterGroupMountRule> RdbmsCatalogue::getRequesterGroupMountRules() const {
-  try {
-    std::list<common::dataStructures::RequesterGroupMountRule> rules;
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME,"
-        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_GROUP_MOUNT_RULE "
-      "ORDER BY "
-        "DISK_INSTANCE_NAME, REQUESTER_GROUP_NAME, MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while(rset.next()) {
-      common::dataStructures::RequesterGroupMountRule rule;
-
-      rule.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      rule.name = rset.columnString("REQUESTER_GROUP_NAME");
-      rule.mountPolicy = rset.columnString("MOUNT_POLICY_NAME");
-
-      rule.comment = rset.columnString("USER_COMMENT");
-      rule.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      rule.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      rule.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      rule.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      rule.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      rule.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      rules.push_back(rule);
-    }
-
-    return rules;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteRequesterGroupMountRule
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteRequesterGroupMountRule(const std::string &diskInstanceName,
-  const std::string &requesterGroupName) {
-  try {
-    const char *const sql =
-      "DELETE FROM "
-        "REQUESTER_GROUP_MOUNT_RULE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete the mount rule for requester group ") + diskInstanceName + ":" +
-        requesterGroupName + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// mountPolicyExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::mountPolicyExists(rdbms::Conn &conn, const std::string &mountPolicyName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME "
-      "FROM "
-        "MOUNT_POLICY "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// requesterMountRuleExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::requesterMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-  const std::string &requesterName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "REQUESTER_NAME AS REQUESTER_NAME "
-      "FROM "
-        "REQUESTER_MOUNT_RULE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getCachedRequesterMountPolicy
-//------------------------------------------------------------------------------
-ValueAndTimeBasedCacheInfo <std::optional <common::dataStructures::MountPolicy> > RdbmsCatalogue::getCachedRequesterMountPolicy(const User &user) const {
-  try {
-    auto getNonCachedValue = [&] {
-      auto conn = m_connPool.getConn();
-      return getRequesterMountPolicy(conn, user);
-    };
-    return m_userMountPolicyCache.getCachedValue(user, getNonCachedValue);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getRequesterMountPolicy
-//------------------------------------------------------------------------------
-std::optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getRequesterMountPolicy(rdbms::Conn &conn,
-  const User &user) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "MOUNT_POLICY "
-      "INNER JOIN "
-        "REQUESTER_MOUNT_RULE "
-      "ON "
-        "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", user.diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", user.username);
-    auto rset = stmt.executeQuery();
-    if(rset.next()) {
-      common::dataStructures::MountPolicy policy;
-
-      policy.name = rset.columnString("MOUNT_POLICY_NAME");
-
-      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
-
-      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
-
-      policy.comment = rset.columnString("USER_COMMENT");
-
-      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-
-      common::dataStructures::EntryLog updateLog;
-      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      return policy;
-    } else {
-      return std::nullopt;
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// requesterActivityMountRuleExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::requesterActivityMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName, const std::string &requesterName, const std::string &activityRegex) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
-        "REQUESTER_NAME AS REQUESTER_NAME, "
-        "ACTIVITY_REGEX AS ACTIVITY_REGEX "
-      "FROM "
-        "REQUESTER_ACTIVITY_MOUNT_RULE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_NAME = :REQUESTER_NAME AND "
-        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// requesterGroupMountRuleExists
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::requesterGroupMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-  const std::string &requesterGroupName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
-        "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME "
-      "FROM "
-        "REQUESTER_GROUP_MOUNT_RULE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteMountPolicy
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteMountPolicy(const std::string &name) {
-  try {
-    const char *const sql = "DELETE FROM MOUNT_POLICY WHERE MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete mount policy ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// getMountPolicies
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::MountPolicy> RdbmsCatalogue::getMountPolicies() const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getMountPolicies(conn);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMountPolicies
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::MountPolicy> RdbmsCatalogue::getMountPolicies(rdbms::Conn & conn) const {
-try {
-    std::list<common::dataStructures::MountPolicy> policies;
-    const char *const sql =
-      "SELECT "
-        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-
-        "ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-
-        "RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "MOUNT_POLICY "
-      "ORDER BY "
-        "MOUNT_POLICY_NAME";
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::MountPolicy policy;
-
-      policy.name = rset.columnString("MOUNT_POLICY_NAME");
-
-      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
-
-      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
-
-      policy.comment = rset.columnString("USER_COMMENT");
-
-      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-
-      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      policies.push_back(policy);
-    }
-    return policies;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMountPolicy
-//------------------------------------------------------------------------------
-std::optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getMountPolicy(const std::string &mountPolicyName) const {
-  try {
-    auto conn = m_connPool.getConn();
-    return getMountPolicy(conn, mountPolicyName);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMountPolicy
-//------------------------------------------------------------------------------
-std::optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getMountPolicy(rdbms::Conn &conn, const std::string &mountPolicyName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-
-        "ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-
-        "RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-
-        "USER_COMMENT AS USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "MOUNT_POLICY "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
-    auto rset = stmt.executeQuery();
-    if (rset.next()) {
-      common::dataStructures::MountPolicy policy;
-
-      policy.name = rset.columnString("MOUNT_POLICY_NAME");
-
-      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
-
-      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
-
-      policy.comment = rset.columnString("USER_COMMENT");
-
-      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-
-      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      return policy;
-    }
-    return std::nullopt;
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getCachedMountPolicies
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedMountPolicies() const {
-  try {
-    auto getNonCachedValue = [&] {
-      auto conn = m_connPool.getConn();
-      return getMountPolicies(conn);
-    };
-    return m_allMountPoliciesCache.getCachedValue("all",getNonCachedValue).value;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// modifyMountPolicyArchivePriority
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t archivePriority) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MOUNT_POLICY SET "
-        "ARCHIVE_PRIORITY = :ARCHIVE_PRIORITY,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_PRIORITY", archivePriority);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// modifyMountPolicyArchiveMinRequestAge
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t minArchiveRequestAge) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MOUNT_POLICY SET "
-        "ARCHIVE_MIN_REQUEST_AGE = :ARCHIVE_MIN_REQUEST_AGE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_MIN_REQUEST_AGE", minArchiveRequestAge);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// modifyMountPolicyRetrievePriority
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t retrievePriority) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MOUNT_POLICY SET "
-        "RETRIEVE_PRIORITY = :RETRIEVE_PRIORITY,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":RETRIEVE_PRIORITY", retrievePriority);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// modifyMountPolicyRetrieveMinRequestAge
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t minRetrieveRequestAge) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MOUNT_POLICY SET "
-        "RETRIEVE_MIN_REQUEST_AGE = :RETRIEVE_MIN_REQUEST_AGE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":RETRIEVE_MIN_REQUEST_AGE", minRetrieveRequestAge);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-//------------------------------------------------------------------------------
-// modifyMountPolicyComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE MOUNT_POLICY SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":MOUNT_POLICY_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-
-  m_groupMountPolicyCache.invalidate();
-  m_userMountPolicyCache.invalidate();
-  m_allMountPoliciesCache.invalidate();
-}
-
-
-//------------------------------------------------------------------------------
-// createDiskSystem
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createDiskSystem(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name,
-  const std::string &diskInstanceName,
-  const std::string &diskInstanceSpaceName,
-  const std::string &fileRegexp,
-  const uint64_t targetedFreeSpace,
-  const time_t sleepTime,
-  const std::string &comment) {
- try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot create disk system because the name is an empty string");
-    }
-    if(fileRegexp.empty()) {
-      throw UserSpecifiedAnEmptyStringFileRegexp("Cannot create disk system because the file regexp is an empty string");
-    }
-    if(diskInstanceName.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create disk system because the disk instance name is an empty string");
-    }
-    if(diskInstanceSpaceName.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceSpaceName("Cannot create disk system because the disk instance space name is an empty string");
-    }
-    if(0 == targetedFreeSpace) {
-      throw UserSpecifiedAZeroTargetedFreeSpace("Cannot create disk system because the targeted free space is zero");
-    }
-    if (0 == sleepTime) {
-      throw UserSpecifiedAZeroSleepTime("Cannot create disk system because the sleep time is zero");
-    }
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create disk system because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    auto conn = m_connPool.getConn();
-    if(diskSystemExists(conn, name)) {
-      throw exception::UserError(std::string("Cannot create disk system ") + name +
-        " because a disk system with the same name identifier already exists");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO DISK_SYSTEM("
-        "DISK_SYSTEM_NAME,"
-        "DISK_INSTANCE_NAME,"
-        "DISK_INSTANCE_SPACE_NAME,"
-        "FILE_REGEXP,"
-        "TARGETED_FREE_SPACE,"
-        "SLEEP_TIME,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":DISK_SYSTEM_NAME,"
-        ":DISK_INSTANCE_NAME,"
-        ":DISK_INSTANCE_SPACE_NAME,"
-        ":FILE_REGEXP,"
-        ":TARGETED_FREE_SPACE,"
-        ":SLEEP_TIME,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-   stmt.bindString(":DISK_SYSTEM_NAME", name);
-   stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-   stmt.bindString(":DISK_INSTANCE_SPACE_NAME", diskInstanceSpaceName);
-   stmt.bindString(":FILE_REGEXP", fileRegexp);
-   stmt.bindUint64(":TARGETED_FREE_SPACE", targetedFreeSpace);
-   stmt.bindUint64(":SLEEP_TIME", sleepTime);
-
-   stmt.bindString(":USER_COMMENT", comment);
-
-   stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-   stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-   stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-   stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-   stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-   stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteDiskSystem
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteDiskSystem(const std::string &name) {
-    try {
-    const char *const delete_sql =
-      "DELETE "
-      "FROM "
-        "DISK_SYSTEM "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(delete_sql);
-      stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    // The delete statement will effect no rows and will not raise an error if
-    // either the tape does not exist or if it still has tape files
-    if(0 == stmt.getNbAffectedRows()) {
-      if(diskSystemExists(conn, name)) {
-        throw UserSpecifiedANonEmptyDiskSystemAfterDelete(std::string("Cannot delete disk system ") + name + " for unknown reason");
-      } else {
-        throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot delete disk system ") + name + " because it does not exist");
-      }
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getAllDiskSystems
-//------------------------------------------------------------------------------
-disk::DiskSystemList RdbmsCatalogue::getAllDiskSystems() const {
-  try {
-    disk::DiskSystemList diskSystemList;
-    const std::string sql =
-      "SELECT "
-        "DISK_SYSTEM.DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME,"
-        "DISK_SYSTEM.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "DISK_SYSTEM.DISK_INSTANCE_SPACE_NAME AS DISK_INSTANCE_SPACE_NAME,"
-        "DISK_SYSTEM.FILE_REGEXP AS FILE_REGEXP,"
-        "DISK_SYSTEM.TARGETED_FREE_SPACE AS TARGETED_FREE_SPACE,"
-        "DISK_SYSTEM.SLEEP_TIME AS SLEEP_TIME,"
-
-        "DISK_SYSTEM.USER_COMMENT AS USER_COMMENT,"
-
-        "DISK_SYSTEM.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "DISK_SYSTEM.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "DISK_SYSTEM.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "DISK_SYSTEM.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "DISK_SYSTEM.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "DISK_SYSTEM.LAST_UPDATE_TIME AS LAST_UPDATE_TIME,"
-
-        "DISK_INSTANCE_SPACE.FREE_SPACE_QUERY_URL AS FREE_SPACE_QUERY_URL,"
-        "DISK_INSTANCE_SPACE.REFRESH_INTERVAL AS REFRESH_INTERVAL,"
-        "DISK_INSTANCE_SPACE.LAST_REFRESH_TIME AS LAST_REFRESH_TIME,"
-        "DISK_INSTANCE_SPACE.FREE_SPACE AS FREE_SPACE "
-      "FROM "
-        "DISK_SYSTEM "
-      "INNER JOIN DISK_INSTANCE_SPACE ON "
-        "DISK_SYSTEM.DISK_INSTANCE_NAME = DISK_INSTANCE_SPACE.DISK_INSTANCE_NAME "
-      "AND "
-        "DISK_SYSTEM.DISK_INSTANCE_SPACE_NAME = DISK_INSTANCE_SPACE.DISK_INSTANCE_SPACE_NAME"
-      ;
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      disk::DiskSystem diskSystem;
-      diskSystem.name = rset.columnString("DISK_SYSTEM_NAME");
-      diskSystem.fileRegexp = rset.columnString("FILE_REGEXP");
-      diskSystem.targetedFreeSpace =  rset.columnUint64("TARGETED_FREE_SPACE");
-      diskSystem.sleepTime =  rset.columnUint64("SLEEP_TIME");
-      diskSystem.comment = rset.columnString("USER_COMMENT");
-      diskSystem.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      diskSystem.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      diskSystem.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      diskSystem.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      diskSystem.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      diskSystem.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-      diskSystem.diskInstanceSpace.freeSpaceQueryURL = rset.columnString("FREE_SPACE_QUERY_URL");
-      diskSystem.diskInstanceSpace.refreshInterval = rset.columnUint64("REFRESH_INTERVAL");
-      diskSystem.diskInstanceSpace.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      diskSystem.diskInstanceSpace.name = rset.columnString("DISK_INSTANCE_SPACE_NAME");
-      diskSystem.diskInstanceSpace.lastRefreshTime = rset.columnUint64("LAST_REFRESH_TIME");
-      diskSystem.diskInstanceSpace.freeSpace = rset.columnUint64("FREE_SPACE");
-      diskSystemList.push_back(diskSystem);
-    }
-    return diskSystemList;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &fileRegexp) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
-        " because the disk system name is an empty string");
-    }
-    if(fileRegexp.empty()) {
-      throw UserSpecifiedAnEmptyStringFileRegexp("Cannot modify disk system "
-        "because the new fileRegexp is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_SYSTEM SET "
-        "FILE_REGEXP = :FILE_REGEXP,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":FILE_REGEXP", fileRegexp);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const uint64_t targetedFreeSpace) {
-      try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
-        " because the disk system name is an empty string");
-    }
-    if(0 == targetedFreeSpace) {
-      throw UserSpecifiedAZeroTargetedFreeSpace("Cannot modify disk system "
-        "because the new targeted free space has zero value");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_SYSTEM SET "
-        "TARGETED_FREE_SPACE = :TARGETED_FREE_SPACE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-        stmt.bindUint64(":TARGETED_FREE_SPACE", targetedFreeSpace);
-        stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-        stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-        stmt.bindUint64(":LAST_UPDATE_TIME", now);
-        stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
-        " because the disk system name is an empty string");
-    }
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot modify disk system "
-        "because the new comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_SYSTEM SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &diskInstanceName) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
-        " because the disk system name is an empty string");
-    }
-    if(diskInstanceName.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot modify disk system "
-        "because the new comment is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_SYSTEM SET "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &diskInstanceSpaceName) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
-        " because the disk system name is an empty string");
-    }
-    if(diskInstanceSpaceName.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceSpaceName("Cannot modify disk system "
-        "because the new comment is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_SYSTEM SET "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", diskInstanceSpaceName);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-
-//------------------------------------------------------------------------------
-// modifyDiskSystemSleepTime
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin, const std::string& name,
-    const uint64_t sleepTime) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
-        " because the disk system name is an empty string");
-    }
-    if(sleepTime == 0) {
-      throw UserSpecifiedAZeroSleepTime("Cannot modify disk system "
-        "because the new sleep time is zero");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_SYSTEM SET "
-        "SLEEP_TIME = :SLEEP_TIME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":SLEEP_TIME", sleepTime);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_SYSTEM_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createDiskInstance
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::createDiskInstance(
-  const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name,
-  const std::string &comment) {
- try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create disk system because the name is an empty string");
-    }
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create disk system because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    auto conn = m_connPool.getConn();
-    if(diskInstanceExists(conn, name)) {
-      throw exception::UserError(std::string("Cannot create disk instance ") + name +
-        " because a disk instance with the same name identifier already exists");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO DISK_INSTANCE("
-        "DISK_INSTANCE_NAME,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":DISK_INSTANCE_NAME,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-   stmt.bindString(":DISK_INSTANCE_NAME", name);
-
-   stmt.bindString(":USER_COMMENT", comment);
-
-   stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-   stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-   stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-   stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-   stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-   stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getAllDiskInstances
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::DiskInstance> RdbmsCatalogue::getAllDiskInstances() const {
-  try {
-    std::list<common::dataStructures::DiskInstance> diskInstanceList;
-    std::string sql =
-      "SELECT "
-        "DISK_INSTANCE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-
-        "DISK_INSTANCE.USER_COMMENT AS USER_COMMENT,"
-
-        "DISK_INSTANCE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "DISK_INSTANCE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "DISK_INSTANCE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "DISK_INSTANCE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "DISK_INSTANCE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "DISK_INSTANCE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "DISK_INSTANCE";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::DiskInstance diskInstance;
-      diskInstance.name = rset.columnString("DISK_INSTANCE_NAME");
-      diskInstance.comment = rset.columnString("USER_COMMENT");
-      diskInstance.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      diskInstance.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      diskInstance.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      diskInstance.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      diskInstance.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      diskInstance.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-      diskInstanceList.push_back(diskInstance);
-    }
-    return diskInstanceList;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteDiskInstance
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteDiskInstance(const std::string &name) {
-    try {
-    const char *const delete_sql =
-      "DELETE "
-      "FROM "
-        "DISK_INSTANCE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(delete_sql);
-      stmt.bindString(":DISK_INSTANCE_NAME", name);
-    stmt.executeNonQuery();
-
-    // The delete statement will effect no rows and will not raise an error if
-    // either the tape does not exist or if it still has tape files
-    if(0 == stmt.getNbAffectedRows()) {
-      if(diskInstanceExists(conn, name)) {
-        throw UserSpecifiedANonEmptyDiskInstanceAfterDelete(std::string("Cannot delete disk instance ") + name + " for unknown reason");
-      } else {
-        throw UserSpecifiedANonExistentDiskInstance(std::string("Cannot delete disk instance ") + name + " because it does not exist");
-      }
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyDiskInstanceComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
-  const std::string &name, const std::string &comment) {
-  try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot modify disk instance"
-        " because the disk instance name is an empty string");
-    }
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot modify disk instance "
-        "because the new comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_INSTANCE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskInstance(std::string("Cannot modify disk instance ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-//------------------------------------------------------------------------------
-// createDiskInstanceSpace
-//------------------------------------------------------------------------------
- void RdbmsCatalogue::createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
-      const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL,
-      const uint64_t refreshInterval, const std::string &comment) {
-
- try {
-    if(name.empty()) {
-      throw UserSpecifiedAnEmptyStringDiskInstanceSpaceName("Cannot create disk instance space because the name is an empty string");
-    }
-    if(freeSpaceQueryURL.empty()) {
-      throw UserSpecifiedAnEmptyStringFreeSpaceQueryURL("Cannot create disk instance space because the free space query URL is an empty string");
-    }
-    if(0 == refreshInterval) {
-      throw UserSpecifiedAZeroRefreshInterval("Cannot create disk instance space because the refresh interval is zero");
-    }
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot create disk instance space because the comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-
-    auto conn = m_connPool.getConn();
-    if(!diskInstanceExists(conn, diskInstance)) {
-      throw exception::UserError(std::string("Cannot create disk instance space ") + name +
-        " for disk instance " + diskInstance +
-        " because the disk instance does not exist");
-    }
-
-    if (diskInstanceSpaceExists(conn, name, diskInstance)) {
-      throw exception::UserError(std::string("Cannot create disk instance space ") + name +
-        " for disk instance " + diskInstance +
-        " because a disk instance space with the same name and disk instance already exists");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO DISK_INSTANCE_SPACE("
-        "DISK_INSTANCE_NAME,"
-        "DISK_INSTANCE_SPACE_NAME,"
-        "FREE_SPACE_QUERY_URL,"
-        "REFRESH_INTERVAL,"
-        "LAST_REFRESH_TIME,"
-        "FREE_SPACE,"
-
-        "USER_COMMENT,"
-
-        "CREATION_LOG_USER_NAME,"
-        "CREATION_LOG_HOST_NAME,"
-        "CREATION_LOG_TIME,"
-
-        "LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME)"
-      "VALUES("
-        ":DISK_INSTANCE_NAME,"
-        ":DISK_INSTANCE_SPACE_NAME,"
-        ":FREE_SPACE_QUERY_URL,"
-        ":REFRESH_INTERVAL,"
-        ":LAST_REFRESH_TIME,"
-        ":FREE_SPACE,"
-
-        ":USER_COMMENT,"
-
-        ":CREATION_LOG_USER_NAME,"
-        ":CREATION_LOG_HOST_NAME,"
-        ":CREATION_LOG_TIME,"
-
-        ":LAST_UPDATE_USER_NAME,"
-        ":LAST_UPDATE_HOST_NAME,"
-        ":LAST_UPDATE_TIME)";
-    auto stmt = conn.createStmt(sql);
-
-   stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-   stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-   stmt.bindString(":FREE_SPACE_QUERY_URL", freeSpaceQueryURL);
-   stmt.bindUint64(":REFRESH_INTERVAL", refreshInterval);
-   stmt.bindUint64(":LAST_REFRESH_TIME", 0);
-   stmt.bindUint64(":FREE_SPACE", 0);
-
-   stmt.bindString(":USER_COMMENT", comment);
-
-   stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
-   stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
-   stmt.bindUint64(":CREATION_LOG_TIME", now);
-
-   stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-   stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-   stmt.bindUint64(":LAST_UPDATE_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getAllDiskInstanceSpaces
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::DiskInstanceSpace> RdbmsCatalogue::getAllDiskInstanceSpaces() const {
-  try {
-    std::list<common::dataStructures::DiskInstanceSpace> diskInstanceSpaceList;
-    std::string sql =
-      "SELECT "
-        "DISK_INSTANCE_SPACE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "DISK_INSTANCE_SPACE.DISK_INSTANCE_SPACE_NAME AS DISK_INSTANCE_SPACE_NAME,"
-        "DISK_INSTANCE_SPACE.FREE_SPACE_QUERY_URL AS FREE_SPACE_QUERY_URL,"
-        "DISK_INSTANCE_SPACE.REFRESH_INTERVAL AS REFRESH_INTERVAL,"
-        "DISK_INSTANCE_SPACE.LAST_REFRESH_TIME AS LAST_REFRESH_TIME,"
-        "DISK_INSTANCE_SPACE.FREE_SPACE AS FREE_SPACE,"
-
-        "DISK_INSTANCE_SPACE.USER_COMMENT AS USER_COMMENT,"
-
-        "DISK_INSTANCE_SPACE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "DISK_INSTANCE_SPACE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "DISK_INSTANCE_SPACE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-
-        "DISK_INSTANCE_SPACE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "DISK_INSTANCE_SPACE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "DISK_INSTANCE_SPACE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "DISK_INSTANCE_SPACE";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      common::dataStructures::DiskInstanceSpace diskInstanceSpace;
-      diskInstanceSpace.name = rset.columnString("DISK_INSTANCE_SPACE_NAME");
-      diskInstanceSpace.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      diskInstanceSpace.freeSpaceQueryURL = rset.columnString("FREE_SPACE_QUERY_URL");
-      diskInstanceSpace.refreshInterval =  rset.columnUint64("REFRESH_INTERVAL");
-      diskInstanceSpace.freeSpace =  rset.columnUint64("FREE_SPACE");
-      diskInstanceSpace.lastRefreshTime =  rset.columnUint64("LAST_REFRESH_TIME");
-      diskInstanceSpace.comment = rset.columnString("USER_COMMENT");
-      diskInstanceSpace.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      diskInstanceSpace.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      diskInstanceSpace.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      diskInstanceSpace.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      diskInstanceSpace.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      diskInstanceSpace.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-      diskInstanceSpaceList.push_back(diskInstanceSpace);
-    }
-    return diskInstanceSpaceList;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteDiskInstanceSpace
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) {
-    try {
-    const char *const delete_sql =
-      "DELETE "
-      "FROM "
-        "DISK_INSTANCE_SPACE "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-      "AND "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(delete_sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-    stmt.executeNonQuery();
-
-    // The delete statement will effect no rows and will not raise an error if
-    // either the tape does not exist or if it still has tape files
-    if(0 == stmt.getNbAffectedRows()) {
-      if(diskInstanceSpaceExists(conn, name, diskInstance)) {
-        throw UserSpecifiedANonEmptyDiskInstanceSpaceAfterDelete(std::string("Cannot delete disk instance space") + name + " for unknown reason");
-      } else {
-        throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot delete disk instance space ") + name + " because it does not exist");
-      }
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyDiskInstanceSpaceQueryURL
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) {
-  try {
-    if(freeSpaceQueryURL.empty()) {
-      throw UserSpecifiedAnEmptyStringFreeSpaceQueryURL("Cannot modify disk instance space "
-        "because the new freeSpaceQueryURL is an empty string");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_INSTANCE_SPACE SET "
-        "FREE_SPACE_QUERY_URL = :FREE_SPACE_QUERY_URL,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-      "AND "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":FREE_SPACE_QUERY_URL", freeSpaceQueryURL);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyDiskInstanceSpaceComment
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const std::string &comment) {
-  try {
-    if(comment.empty()) {
-      throw UserSpecifiedAnEmptyStringComment("Cannot modify disk instance space "
-        "because the new comment is an empty string");
-    }
-    checkCommentOrReasonMaxLength(comment);
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_INSTANCE_SPACE SET "
-        "USER_COMMENT = :USER_COMMENT,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-      "AND "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":USER_COMMENT", comment);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// modifyDiskInstanceSpaceRefreshInterval
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) {
-  try {
-    if(0 == refreshInterval) {
-      throw UserSpecifiedAZeroRefreshInterval("Cannot modify disk instance space "
-        "because the new refreshInterval is zero");
-    }
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_INSTANCE_SPACE SET "
-        "REFRESH_INTERVAL = :REFRESH_INTERVAL,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-      "AND "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":REFRESH_INTERVAL", refreshInterval);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyDiskInstanceSpaceFreeSpace(const std::string &name, const std::string &diskInstance, const uint64_t freeSpace) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE DISK_INSTANCE_SPACE SET "
-        "FREE_SPACE = :FREE_SPACE,"
-        "LAST_REFRESH_TIME = :LAST_REFRESH_TIME "
-      "WHERE "
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-      "AND "
-        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":FREE_SPACE", freeSpace);
-    stmt.bindUint64(":LAST_REFRESH_TIME", now);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// insertArchiveFile
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::insertArchiveFile(rdbms::Conn &conn, const ArchiveFileRowWithoutTimestamps &row) {
-  try {
-    if(!storageClassExists(conn, row.storageClassName)) {
-      throw exception::UserError(std::string("Storage class ") + row.diskInstance + ":" + row.storageClassName +
-        " does not exist");
-    }
-
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "INSERT INTO ARCHIVE_FILE("
-        "ARCHIVE_FILE_ID,"
-        "DISK_INSTANCE_NAME,"
-        "DISK_FILE_ID,"
-        "DISK_FILE_UID,"
-        "DISK_FILE_GID,"
-        "SIZE_IN_BYTES,"
-        "CHECKSUM_BLOB,"
-        "CHECKSUM_ADLER32,"
-        "STORAGE_CLASS_ID,"
-        "CREATION_TIME,"
-        "RECONCILIATION_TIME)"
-      "SELECT "
-        ":ARCHIVE_FILE_ID,"
-        ":DISK_INSTANCE_NAME,"
-        ":DISK_FILE_ID,"
-        ":DISK_FILE_UID,"
-        ":DISK_FILE_GID,"
-        ":SIZE_IN_BYTES,"
-        ":CHECKSUM_BLOB,"
-        ":CHECKSUM_ADLER32,"
-        "STORAGE_CLASS_ID,"
-        ":CREATION_TIME,"
-        ":RECONCILIATION_TIME "
-      "FROM "
-        "STORAGE_CLASS "
-      "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindUint64(":ARCHIVE_FILE_ID", row.archiveFileId);
-    stmt.bindString(":DISK_INSTANCE_NAME", row.diskInstance);
-    stmt.bindString(":DISK_FILE_ID", row.diskFileId);
-    stmt.bindUint64(":DISK_FILE_UID", row.diskFileOwnerUid);
-    stmt.bindUint64(":DISK_FILE_GID", row.diskFileGid);
-    stmt.bindUint64(":SIZE_IN_BYTES", row.size);
-    stmt.bindBlob  (":CHECKSUM_BLOB", row.checksumBlob.serialize());
-    // Keep transition ADLER32 checksum up-to-date if it exists
-    uint32_t adler32;
-    try {
-      std::string adler32hex = checksum::ChecksumBlob::ByteArrayToHex(row.checksumBlob.at(checksum::ADLER32));
-      adler32 = strtoul(adler32hex.c_str(), 0, 16);
-    } catch(exception::ChecksumTypeMismatch &ex) {
-      adler32 = 0;
-    }
-    stmt.bindUint64(":CHECKSUM_ADLER32", adler32);
-    stmt.bindString(":STORAGE_CLASS_NAME", row.storageClassName);
-    stmt.bindUint64(":CREATION_TIME", now);
-    stmt.bindUint64(":RECONCILIATION_TIME", now);
-
-    stmt.executeNonQuery();
-  } catch (exception::UserError &) {
-    throw;
-  } catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + " failed: archiveFileId=" + std::to_string(row.archiveFileId) +
-       ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// checkTapeFileSearchCriteria
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkTapeFileSearchCriteria(const TapeFileSearchCriteria &searchCriteria) const {
-  auto conn = m_connPool.getConn();
-  checkTapeFileSearchCriteria(conn, searchCriteria);
-
-}
-
-//------------------------------------------------------------------------------
-// checkTapeFileSearchCriteria
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkTapeFileSearchCriteria(rdbms::Conn &conn, const TapeFileSearchCriteria &searchCriteria) const {
-  if(searchCriteria.archiveFileId) {
-    if(!archiveFileIdExists(conn, searchCriteria.archiveFileId.value())) {
-      throw exception::UserError(std::string("Archive file with ID ") +
-        std::to_string(searchCriteria.archiveFileId.value()) + " does not exist");
-    }
-  }
-
-  if(searchCriteria.diskFileIds && !searchCriteria.diskInstance) {
-    throw exception::UserError(std::string("Disk file IDs are ambiguous without disk instance name"));
-  }
-
-  if (searchCriteria.fSeq && !searchCriteria.vid) {
-    throw exception::UserError(std::string("fSeq makes no sense without vid"));
-  }
-
-  if(searchCriteria.vid) {
-    if(!tapeExists(conn, searchCriteria.vid.value())) {
-      throw exception::UserError(std::string("Tape ") + searchCriteria.vid.value() + " does not exist");
-    }
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFilesItor
-//------------------------------------------------------------------------------
-Catalogue::ArchiveFileItor RdbmsCatalogue::getArchiveFilesItor(const TapeFileSearchCriteria &searchCriteria) const {
-
-  checkTapeFileSearchCriteria(searchCriteria);
-
-  // If this is the listing of the contents of a tape
-  if (!searchCriteria.archiveFileId && !searchCriteria.diskInstance && !searchCriteria.diskFileIds &&
-    !searchCriteria.fSeq && searchCriteria.vid) {
-    return getTapeContentsItor(searchCriteria.vid.value());
-  }
-
-  try {
-    // Create a connection to populate the temporary table (specialised by database type)
-    auto conn = m_archiveFileListingConnPool.getConn();
-    const auto tempDiskFxidsTableName = createAndPopulateTempTableFxid(conn, searchCriteria.diskFileIds);
-    // Pass ownership of the connection to the Iterator object
-    auto impl = new RdbmsCatalogueGetArchiveFilesItor(m_log, std::move(conn), searchCriteria, tempDiskFxidsTableName);
-    return ArchiveFileItor(impl);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFilesItor
-//------------------------------------------------------------------------------
-Catalogue::ArchiveFileItor RdbmsCatalogue::getArchiveFilesItor(rdbms::Conn &conn, const TapeFileSearchCriteria &searchCriteria) const {
-
-  checkTapeFileSearchCriteria(conn, searchCriteria);
-
-  // If this is the listing of the contents of a tape
-  if (!searchCriteria.archiveFileId && !searchCriteria.diskInstance && !searchCriteria.diskFileIds &&
-    searchCriteria.vid) {
-    return getTapeContentsItor(searchCriteria.vid.value());
-  }
-
-  try {
-    auto archiveListingConn = m_archiveFileListingConnPool.getConn();
-    const auto tempDiskFxidsTableName = createAndPopulateTempTableFxid(archiveListingConn, searchCriteria.diskFileIds);
-    // Pass ownership of the connection to the Iterator object
-    auto impl = new RdbmsCatalogueGetArchiveFilesItor(m_log, std::move(archiveListingConn), searchCriteria, tempDiskFxidsTableName);
-    return ArchiveFileItor(impl);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapeContentsItor
-//------------------------------------------------------------------------------
-Catalogue::ArchiveFileItor RdbmsCatalogue::getTapeContentsItor(const std::string &vid)
-  const {
-  try {
-    // Create a connection to populate the temporary table (specialised by database type)
-    auto impl = new RdbmsCatalogueTapeContentsItor(m_log, m_connPool, vid);
-    return ArchiveFileItor(impl);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// checkRecycleTapeFileSearchCriteria
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkRecycleTapeFileSearchCriteria(cta::rdbms::Conn &conn, const RecycleTapeFileSearchCriteria & searchCriteria) const {
-  if(searchCriteria.vid) {
-    if(!tapeExists(conn, searchCriteria.vid.value())) {
-      throw exception::UserError(std::string("Tape ") + searchCriteria.vid.value() + " does not exist");
-    }
-  }
-}
-
-Catalogue::FileRecycleLogItor RdbmsCatalogue::getFileRecycleLogItor(const RecycleTapeFileSearchCriteria & searchCriteria) const {
-  try {
-    auto conn = m_archiveFileListingConnPool.getConn();
-    checkRecycleTapeFileSearchCriteria(conn, searchCriteria);
-    const auto tempDiskFxidsTableName = createAndPopulateTempTableFxid(conn, searchCriteria.diskFileIds);
-    auto impl = new RdbmsCatalogueGetFileRecycleLogItor(m_log, std::move(conn), searchCriteria, tempDiskFxidsTableName);
-    return FileRecycleLogItor(impl);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreArchiveFileInRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::restoreArchiveFileInRecycleLog(rdbms::Conn &conn,
-  const cta::common::dataStructures::FileRecycleLog &fileRecycleLog, const std::string &newFid, log::LogContext & lc) {
-  cta::catalogue::ArchiveFileRowWithoutTimestamps row;
-  row.diskFileId = newFid;
-  row.archiveFileId = fileRecycleLog.archiveFileId;
-  row.checksumBlob = fileRecycleLog.checksumBlob;
-  row.diskFileOwnerUid = fileRecycleLog.diskFileUid;
-  row.diskFileGid = fileRecycleLog.diskFileGid;
-  row.diskInstance = fileRecycleLog.diskInstanceName;
-  row.size = fileRecycleLog.sizeInBytes;
-  row.storageClassName = fileRecycleLog.storageClassName;
-  insertArchiveFile(conn, row);
-}
-
-//------------------------------------------------------------------------------
-// restoreFilesInRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria, const std::string &newFid) {
-  try {
-    auto fileRecycleLogitor = getFileRecycleLogItor(searchCriteria);
-    auto conn = m_connPool.getConn();
-    log::LogContext lc(m_log);
-    restoreEntryInRecycleLog(conn, fileRecycleLogitor, newFid, lc);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getFilesForRepack
-//------------------------------------------------------------------------------
-std::list<common::dataStructures::ArchiveFile> RdbmsCatalogue::getFilesForRepack(
-  const std::string &vid,
-  const uint64_t startFSeq,
-  const uint64_t maxNbFiles) const {
-  try {
-    std::string sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "INNER JOIN TAPE ON "
-        "TAPE_FILE.VID = TAPE.VID "
-      "INNER JOIN TAPE_POOL ON "
-        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-      "WHERE "
-        "TAPE_FILE.VID = :VID AND "
-        "TAPE_FILE.FSEQ >= :START_FSEQ "
-       "ORDER BY FSEQ";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.bindUint64(":START_FSEQ", startFSeq);
-    auto rset = stmt.executeQuery();
-
-    std::list<common::dataStructures::ArchiveFile> archiveFiles;
-    while(rset.next()) {
-      common::dataStructures::ArchiveFile archiveFile;
-
-      archiveFile.archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
-      archiveFile.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      archiveFile.diskFileId = rset.columnString("DISK_FILE_ID");
-      archiveFile.diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
-      archiveFile.diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
-      archiveFile.fileSize = rset.columnUint64("SIZE_IN_BYTES");
-      archiveFile.checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-      archiveFile.storageClass = rset.columnString("STORAGE_CLASS_NAME");
-      archiveFile.creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-      archiveFile.reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
-
-      common::dataStructures::TapeFile tapeFile;
-      tapeFile.vid = rset.columnString("VID");
-      tapeFile.fSeq = rset.columnUint64("FSEQ");
-      tapeFile.blockId = rset.columnUint64("BLOCK_ID");
-      tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-      tapeFile.copyNb = rset.columnUint64("COPY_NB");
-      tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-      tapeFile.checksumBlob = archiveFile.checksumBlob; // Duplicated for convenience
-
-      archiveFile.tapeFiles.push_back(tapeFile);
-
-      archiveFiles.push_back(archiveFile);
-
-      if(maxNbFiles == archiveFiles.size()) break;
-    }
-    return archiveFiles;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileItorForRepack
-//------------------------------------------------------------------------------
-Catalogue::ArchiveFileItor RdbmsCatalogue::getArchiveFilesForRepackItor(const std::string &vid, const uint64_t startFSeq) const {
-  try {
-    auto impl = new RdbmsCatalogueGetArchiveFilesForRepackItor(m_log, m_archiveFileListingConnPool, vid, startFSeq);
-    return ArchiveFileItor(impl);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapeFileSummary
-//
-// NOTE: As "archivefile ls" has been deprecated, there is no longer a way for
-//       operators to request a tape file summary. (Use "tape ls" instead).
-//       This method is used exclusively by the unit tests.
-//------------------------------------------------------------------------------
-common::dataStructures::ArchiveFileSummary RdbmsCatalogue::getTapeFileSummary(
-  const TapeFileSearchCriteria &searchCriteria) const
-{
-  try {
-    auto conn = m_connPool.getConn();
-
-    std::string sql =
-      "SELECT "
-        "COALESCE(SUM(ARCHIVE_FILE.SIZE_IN_BYTES), 0) AS TOTAL_BYTES,"
-        "COUNT(ARCHIVE_FILE.ARCHIVE_FILE_ID) AS TOTAL_FILES "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "INNER JOIN TAPE ON "
-        "TAPE_FILE.VID = TAPE.VID "
-      "INNER JOIN TAPE_POOL ON "
-        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID";
-
-    const bool thereIsAtLeastOneSearchCriteria =
-      searchCriteria.archiveFileId  ||
-      searchCriteria.diskInstance   ||
-      searchCriteria.vid            ||
-      searchCriteria.diskFileIds;
-
-    if(thereIsAtLeastOneSearchCriteria) {
-      sql += " WHERE ";
-    }
-
-    bool addedAWhereConstraint = false;
-
-    if(searchCriteria.archiveFileId) {
-      sql += " ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.diskInstance) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += "ARCHIVE_FILE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.vid) {
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += "TAPE_FILE.VID = :VID";
-      addedAWhereConstraint = true;
-    }
-    if(searchCriteria.diskFileIds) {
-      const auto tempDiskFxidsTableName = createAndPopulateTempTableFxid(conn, searchCriteria.diskFileIds);
-
-      if(addedAWhereConstraint) sql += " AND ";
-      sql += "ARCHIVE_FILE.DISK_FILE_ID IN (SELECT DISK_FILE_ID FROM " + tempDiskFxidsTableName + ")";
-      addedAWhereConstraint = true;
-    }
-
-    auto stmt = conn.createStmt(sql);
-    if(searchCriteria.archiveFileId) {
-      stmt.bindUint64(":ARCHIVE_FILE_ID", searchCriteria.archiveFileId.value());
-    }
-    if(searchCriteria.diskInstance) {
-      stmt.bindString(":DISK_INSTANCE_NAME", searchCriteria.diskInstance.value());
-    }
-    if(searchCriteria.vid) {
-      stmt.bindString(":VID", searchCriteria.vid.value());
-    }
-    auto rset = stmt.executeQuery();
-
-    if(!rset.next()) {
-      throw exception::Exception("SELECT COUNT statement did not return a row");
-    }
-
-    common::dataStructures::ArchiveFileSummary summary;
-    summary.totalBytes = rset.columnUint64("TOTAL_BYTES");
-    summary.totalFiles = rset.columnUint64("TOTAL_FILES");
-    return summary;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileForDeletion
-//------------------------------------------------------------------------------
-common::dataStructures::ArchiveFile RdbmsCatalogue::getArchiveFileForDeletion(const TapeFileSearchCriteria &criteria) const {
-  if (!criteria.diskFileIds && !criteria.archiveFileId) {
-    throw exception::UserError("To delete a file copy either the diskFileId+diskInstanceName or archiveFileId must be specified");
-  }
-  if (criteria.diskFileIds && !criteria.diskInstance) {
-    throw exception::UserError("DiskFileId makes no sense without disk instance");
-  }
-  if (!criteria.vid) {
-    throw exception::UserError("Vid must be specified");
-  }
-
-  auto vid = criteria.vid.value();
-  TapeFileSearchCriteria searchCriteria = criteria;
-  searchCriteria.vid = std::nullopt; //unset vid, we want to get all copies of the archive file so we can check that it is not a one copy file
-  auto itor = getArchiveFilesItor(searchCriteria);
-
-  // itor should have at most one archive file since we always search on unique attributes
-  if (!itor.hasMore()) {
-    if (criteria.archiveFileId) {
-      throw exception::UserError(std::string("Cannot delete a copy of the file with archiveFileId ") +
-                                std::to_string(criteria.archiveFileId.value()) +
-                                 " because the file does not exist");
-    } else {
-      throw exception::UserError(std::string("Cannot delete a copy of the file with eosFxid ") +
-                                criteria.diskFileIds.value().front() + " and diskInstance " +
-                                criteria.diskInstance.value() + " because the file does not exist");
-    }
-  }
-
-  cta::common::dataStructures::ArchiveFile af = itor.next();
-
-  if (af.tapeFiles.size() == 1) {
-    if (criteria.archiveFileId) {
-      throw exception::UserError(std::string("Cannot delete a copy of the file with archiveFileId ") +
-                                std::to_string(criteria.archiveFileId.value()) +
-                                " because it is the only copy");
-    } else {
-      throw exception::UserError(std::string("Cannot delete a copy of the file with eosFxid ") +
-                                criteria.diskFileIds.value().front() + " and diskInstance " +
-                                criteria.diskInstance.value() + " because it is the only copy");
-    }
-  }
-  af.tapeFiles.removeAllVidsExcept(vid); // assume there is only one copy per vid, this should return a list with at most one item
-  if (af.tapeFiles.empty()) {
-    if (criteria.archiveFileId) {
-      throw exception::UserError(std::string("No copy of the file with archiveFileId ") +
-                                std::to_string(criteria.archiveFileId.value()) +
-                                 " on vid " + vid);
-    } else {
-      throw exception::UserError(std::string("No copy of the file with eosFxid ") +
-                                criteria.diskFileIds.value().front() + " and diskInstance " +
-                                criteria.diskInstance.value() + " on vid " + vid);
-    }
-  }
-  if (af.tapeFiles.size() > 1){
-    if (criteria.archiveFileId) {
-      throw exception::UserError(std::string("Error: More than one copy of the file with archiveFileId ") +
-                                std::to_string(criteria.archiveFileId.value()) +
-                                 " on vid " + vid);
-    } else {
-      throw exception::UserError(std::string("Error: More than one copy of the file with eosFxid ") +
-                                criteria.diskFileIds.value().front() + " and diskInstance " +
-                                criteria.diskInstance.value() + " on vid " + vid);
-    }
-  }
-  return af;
-}
-
-
-//------------------------------------------------------------------------------
-// deleteTapeFileCopy
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) {
-
-  log::LogContext lc(m_log);
-  auto conn = m_connPool.getConn();
-  copyTapeFileToFileRecyleLogAndDelete(conn, file, reason, lc);
-}
-
-
-//------------------------------------------------------------------------------
-// getArchiveFileById
-//------------------------------------------------------------------------------
-common::dataStructures::ArchiveFile RdbmsCatalogue::getArchiveFileById(const uint64_t id) const {
-  try {
-    auto conn = m_connPool.getConn();
-    const auto archiveFile = getArchiveFileById(conn, id);
-
-    // Throw an exception if the archive file does not exist
-    if(nullptr == archiveFile.get()) {
-      exception::Exception ex;
-      ex.getMessage() << "No such archive file with ID " << id;
-      throw (ex);
-    }
-
-    return *archiveFile;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileById
-//------------------------------------------------------------------------------
-std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsCatalogue::getArchiveFileById(rdbms::Conn &conn,
-  const uint64_t id) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "WHERE "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID "
-      "ORDER BY "
-        "TAPE_FILE.CREATION_TIME ASC";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", id);
-    auto rset = stmt.executeQuery();
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
-    while (rset.next()) {
-      if(nullptr == archiveFile.get()) {
-        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
-
-        archiveFile->archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
-        archiveFile->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-        archiveFile->diskFileId = rset.columnString("DISK_FILE_ID");
-        archiveFile->diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
-        archiveFile->diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
-        archiveFile->fileSize = rset.columnUint64("SIZE_IN_BYTES");
-        archiveFile->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-        archiveFile->storageClass = rset.columnString("STORAGE_CLASS_NAME");
-        archiveFile->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-        archiveFile->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
-      }
-
-      // If there is a tape file
-      if(!rset.columnIsNull("VID")) {
-        // Add the tape file to the archive file's in-memory structure
-        common::dataStructures::TapeFile tapeFile;
-        tapeFile.vid = rset.columnString("VID");
-        tapeFile.fSeq = rset.columnUint64("FSEQ");
-        tapeFile.blockId = rset.columnUint64("BLOCK_ID");
-        tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-        tapeFile.copyNb = rset.columnUint64("COPY_NB");
-        tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
-
-        archiveFile->tapeFiles.push_back(tapeFile);
-      }
-    }
-
-    return archiveFile;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// tapeLabelled
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::tapeLabelled(const std::string &vid, const std::string &drive) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "LABEL_DRIVE = :LABEL_DRIVE,"
-        "LABEL_TIME = :LABEL_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LABEL_DRIVE", drive);
-    stmt.bindUint64(":LABEL_TIME", now);
-    stmt.bindString(":VID", vid);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// checkAndGetNextArchiveFileId
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::checkAndGetNextArchiveFileId(const std::string &diskInstanceName,
-  const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) {
-  try {
-    const auto storageClass = StorageClass(storageClassName);
-    const auto copyToPoolMap = getCachedTapeCopyToPoolMap(storageClass);
-    const auto expectedNbRoutes = getCachedExpectedNbArchiveRoutes(storageClass);
-
-    // Check that the number of archive routes is correct
-    if(copyToPoolMap.empty()) {
-      exception::UserError ue;
-      ue.getMessage() << "Storage class " << storageClassName << " has no archive routes";
-      throw ue;
-    }
-    if(copyToPoolMap.size() != expectedNbRoutes) {
-      exception::UserError ue;
-      ue.getMessage() << "Storage class " << storageClassName << " does not have the"
-        " expected number of archive routes routes: expected=" << expectedNbRoutes << ", actual=" <<
-        copyToPoolMap.size();
-      throw ue;
-    }
-
-    const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
-    const auto userMountPolicy = userMountPolicyAndCacheInfo.value;
-    // Only consider the requester's group if there is no user mount policy
-    if(!userMountPolicy) {
-      const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
-      const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value;
-
-      if(!groupMountPolicy) {
-
-        const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
-        const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value;
-
-        if(!defaultUserMountPolicy) {
-          exception::UserErrorWithCacheInfo ue(userMountPolicyAndCacheInfo.cacheInfo);
-          ue.getMessage() << "Failed to check and get next archive file ID: No mount rules: storageClass=" <<
-            storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group;
-          throw ue;
-        }
-      }
-    }
-
-    // Now that we have found both the archive routes and the mount policy it's
-    // safe to consume an archive file identifier
-    {
-      auto conn = m_connPool.getConn();
-      return getNextArchiveFileId(conn);
-    }
-  } catch(exception::UserErrorWithCacheInfo &ue) {
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("cacheInfo", ue.cacheInfo)
-       .add("userError", ue.getMessage().str());
-    lc.log(log::INFO, "Catalogue::checkAndGetNextArchiveFileId caught a UserErrorWithCacheInfo");
-    throw;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileQueueCriteria
-//------------------------------------------------------------------------------
-common::dataStructures::ArchiveFileQueueCriteria RdbmsCatalogue::getArchiveFileQueueCriteria(
-  const std::string &diskInstanceName,
-  const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) {
-  try {
-    const StorageClass storageClass = StorageClass(storageClassName);
-    const common::dataStructures::TapeCopyToPoolMap copyToPoolMap = getCachedTapeCopyToPoolMap(storageClass);
-    const uint64_t expectedNbRoutes = getCachedExpectedNbArchiveRoutes(storageClass);
-
-    // Check that the number of archive routes is correct
-    if(copyToPoolMap.empty()) {
-      exception::UserError ue;
-      ue.getMessage() << "Storage class " << diskInstanceName << ": " << storageClassName << " has no archive routes";
-      throw ue;
-    }
-    if(copyToPoolMap.size() != expectedNbRoutes) {
-      exception::UserError ue;
-      ue.getMessage() << "Storage class " << diskInstanceName << ": " << storageClassName << " does not have the"
-        " expected number of archive routes routes: expected=" << expectedNbRoutes << ", actual=" <<
-        copyToPoolMap.size();
-      throw ue;
-    }
-
-    // Get the mount policy - user mount policies overrule group ones
-    const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
-    const auto userMountPolicy = userMountPolicyAndCacheInfo.value;
-
-    if(userMountPolicy) {
-      return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *userMountPolicy);
-    } else {
-      const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
-      const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value;
-
-      if(groupMountPolicy) {
-        return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *groupMountPolicy);
-      } else {
-        const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
-        const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value;
-
-        if(defaultUserMountPolicy) {
-          return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *defaultUserMountPolicy);
-        } else {
-          exception::UserErrorWithCacheInfo ue(defaultUserMountPolicyAndCacheInfo.cacheInfo);
-          ue.getMessage() << "Failed to get archive file queue criteria: No mount rules: storageClass=" <<
-            storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group;
-          throw ue;
-        }
-      }
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getCachedTapeCopyToPoolMap
-//------------------------------------------------------------------------------
-common::dataStructures::TapeCopyToPoolMap RdbmsCatalogue::getCachedTapeCopyToPoolMap(const StorageClass &storageClass)
-  const {
-  try {
-    auto getNonCachedValue = [&] {
-      auto conn = m_connPool.getConn();
-      return getTapeCopyToPoolMap(conn, storageClass);
-    };
-    return m_tapeCopyToPoolCache.getCachedValue(storageClass, getNonCachedValue).value;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapeCopyToPoolMap
-//------------------------------------------------------------------------------
-common::dataStructures::TapeCopyToPoolMap RdbmsCatalogue::getTapeCopyToPoolMap(rdbms::Conn &conn,
-  const StorageClass &storageClass) const {
-  try {
-    common::dataStructures::TapeCopyToPoolMap copyToPoolMap;
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB,"
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME "
-      "FROM "
-        "ARCHIVE_ROUTE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_POOL ON "
-        "ARCHIVE_ROUTE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-      "WHERE "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.storageClassName);
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      const uint32_t copyNb = rset.columnUint64("COPY_NB");
-      const std::string tapePoolName = rset.columnString("TAPE_POOL_NAME");
-      copyToPoolMap[copyNb] = tapePoolName;
-    }
-
-    return copyToPoolMap;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getCachedExpectedNbArchiveRoutes
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::getCachedExpectedNbArchiveRoutes(const StorageClass &storageClass) const {
-  try {
-    auto getNonCachedValue = [&] {
-      auto conn = m_connPool.getConn();
-      return getExpectedNbArchiveRoutes(conn, storageClass);
-    };
-    return m_expectedNbArchiveRoutesCache.getCachedValue(storageClass, getNonCachedValue).value;
-  } catch (exception::LostDatabaseConnection &le) {
-    throw exception::LostDatabaseConnection(std::string(__FUNCTION__) + " failed: " + le.getMessage().str());
-  } catch(exception::UserError &) {
-    throw;
-  } catch (exception::Exception &ex) {
-    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// getExpectedNbArchiveRoutes
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::getExpectedNbArchiveRoutes(rdbms::Conn &conn, const StorageClass &storageClass) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "COUNT(*) AS NB_ROUTES "
-      "FROM "
-        "ARCHIVE_ROUTE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "WHERE "
-        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.storageClassName);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set of SELECT COUNT(*) is empty");
-    }
-    return rset.columnUint64("NB_ROUTES");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// updateTape
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::updateTape(
-  rdbms::Conn &conn,
-  const std::string &vid,
-  const uint64_t lastFSeq,
-  const uint64_t compressedBytesWritten,
-  const uint64_t filesWritten,
-  const std::string &tapeDrive) {
-  try {
-    const time_t now = time(nullptr);
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "LAST_FSEQ = :LAST_FSEQ,"
-        "DATA_IN_BYTES = DATA_IN_BYTES + :DATA_IN_BYTES,"
-        "MASTER_DATA_IN_BYTES = MASTER_DATA_IN_BYTES + :MASTER_DATA_IN_BYTES,"
-        "NB_MASTER_FILES = NB_MASTER_FILES + :MASTER_FILES,"
-        "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE,"
-        "LAST_WRITE_TIME = :LAST_WRITE_TIME "
-      "WHERE "
-        "VID = :VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.bindUint64(":LAST_FSEQ", lastFSeq);
-    stmt.bindUint64(":DATA_IN_BYTES", compressedBytesWritten);
-    stmt.bindUint64(":MASTER_FILES", filesWritten);
-    stmt.bindUint64(":MASTER_DATA_IN_BYTES", compressedBytesWritten);
-    stmt.bindString(":LAST_WRITE_DRIVE", tapeDrive);
-    stmt.bindUint64(":LAST_WRITE_TIME", now);
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// prepareToRetrieveFile
-//------------------------------------------------------------------------------
-common::dataStructures::RetrieveFileQueueCriteria RdbmsCatalogue::prepareToRetrieveFile(
-  const std::string &diskInstanceName,
-  const uint64_t archiveFileId,
-  const common::dataStructures::RequesterIdentity &user,
-  const std::optional<std::string>& activity,
-  log::LogContext &lc,
-  const std::optional<std::string> &mountPolicyName) {
-  try {
-    cta::utils::Timer t;
-    common::dataStructures::RetrieveFileQueueCriteria criteria;
-    {
-      auto conn = m_connPool.getConn();
-      const auto getConnTime = t.secs(utils::Timer::resetCounter);
-      auto archiveFile = getArchiveFileToRetrieveByArchiveFileId(conn, archiveFileId);
-      const auto getArchiveFileTime = t.secs(utils::Timer::resetCounter);
-      if(nullptr == archiveFile.get()) {
-        exception::UserError ex;
-        auto tapeFileStateList = getTapeFileStateListForArchiveFileId(conn, archiveFileId);
-        if (tapeFileStateList.empty()) {
-          ex.getMessage() << "File with archive file ID " << archiveFileId << " does not exist in CTA namespace";
-          throw ex;
-        }
-        const auto nonBrokenState = std::find_if(std::begin(tapeFileStateList), std::end(tapeFileStateList),
-          [](std::pair<std::string, std::string> state) {
-            return (state.second != "BROKEN")
-                   && (state.second != "BROKEN_PENDING")
-                   && (state.second != "EXPORTED")
-                   && (state.second != "EXPORTED_PENDING");
-        });
-
-        if (nonBrokenState != std::end(tapeFileStateList)) {
-          ex.getMessage() << "WARNING: File with archive file ID " << archiveFileId <<
-            " exits in CTA namespace but is temporarily unavailable on " << nonBrokenState->second << " tape " << nonBrokenState->first;
-          throw ex;
-        }
-        const auto brokenState = tapeFileStateList.front();
-        //All tape files are on broken tapes, just generate an error about the first
-        ex.getMessage() << "ERROR: File with archive file ID " << archiveFileId <<
-            " exits in CTA namespace but is permanently unavailable on " << brokenState.second << " tape " << brokenState.first;
-        throw ex;
-      }
-      if (mountPolicyName) {
-          std::optional<common::dataStructures::MountPolicy> mountPolicy = getMountPolicy(conn, mountPolicyName.value());
-          if (mountPolicy) {
-            criteria.archiveFile = *archiveFile;
-            criteria.mountPolicy = mountPolicy.value();
-            return criteria;
-          } else {
-            log::ScopedParamContainer spc(lc);
-            spc.add("mountPolicyName", mountPolicyName.value())
-               .add("archiveFileId", archiveFileId);
-            lc.log(log::WARNING, "Catalogue::prepareToRetrieve Could not find specified mount policy, falling back to querying mount rules");
-          }
-      }
-
-      if(diskInstanceName != archiveFile->diskInstance) {
-        exception::UserError ue;
-        ue.getMessage() << "Cannot retrieve file because the disk instance of the request does not match that of the"
-          " archived file: archiveFileId=" << archiveFileId <<
-          " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" << archiveFile->diskInstance;
-        throw ue;
-      }
-
-      t.reset();
-      RequesterAndGroupMountPolicies mountPolicies;
-      if (activity) {
-        mountPolicies = getMountPolicies(conn, diskInstanceName, user.name, user.group, activity.value());
-      } else {
-        mountPolicies = getMountPolicies(conn, diskInstanceName, user.name, user.group);
-      }
-
-      const auto getMountPoliciesTime = t.secs(utils::Timer::resetCounter);
-
-      log::ScopedParamContainer spc(lc);
-      spc.add("getConnTime", getConnTime)
-         .add("getArchiveFileTime", getArchiveFileTime)
-         .add("getMountPoliciesTime", getMountPoliciesTime);
-      lc.log(log::INFO, "Catalogue::prepareToRetrieve internal timings");
-
-      // Requester activity mount policies overrule requester mount policies
-      // Requester mount policies overrule requester group mount policies
-      common::dataStructures::MountPolicy mountPolicy;
-      if (!mountPolicies.requesterActivityMountPolicies.empty()) {
-        //More than one may match the activity, so choose the one with highest retrieve priority
-        mountPolicy = *std::max_element(mountPolicies.requesterActivityMountPolicies.begin(),
-          mountPolicies.requesterActivityMountPolicies.end(),
-          [](const  common::dataStructures::MountPolicy &p1,  common::dataStructures::MountPolicy &p2) {
-            return p1.retrievePriority < p2.retrievePriority;
-          });
-      } else if(!mountPolicies.requesterMountPolicies.empty()) {
-        mountPolicy = mountPolicies.requesterMountPolicies.front();
-      } else if(!mountPolicies.requesterGroupMountPolicies.empty()) {
-        mountPolicy = mountPolicies.requesterGroupMountPolicies.front();
-      } else {
-        mountPolicies = getMountPolicies(conn, diskInstanceName, "default", user.group);
-
-        if(!mountPolicies.requesterMountPolicies.empty()) {
-          mountPolicy = mountPolicies.requesterMountPolicies.front();
-        } else {
-          exception::UserError ue;
-          ue.getMessage()
-            << "Cannot retrieve file because there are no mount rules for the requester, activity or their group:"
-            << " archiveFileId=" << archiveFileId << " requester=" << diskInstanceName << ":" << user.name << ":"
-            << user.group;
-          if (activity) {
-            ue.getMessage() << " activity=" << activity.value();
-          }
-          throw ue;
-        }
-      }
-      criteria.archiveFile = *archiveFile;
-      criteria.mountPolicy = mountPolicy;
-    }
-    return criteria;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMountPolicies
-//------------------------------------------------------------------------------
-RequesterAndGroupMountPolicies RdbmsCatalogue::getMountPolicies(
-  rdbms::Conn &conn,
-  const std::string &diskInstanceName,
-  const std::string &requesterName,
-  const std::string &requesterGroupName,
-  const std::string &activity) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "'ACTIVITY' AS RULE_TYPE,"
-        "REQUESTER_ACTIVITY_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE,"
-        "REQUESTER_ACTIVITY_MOUNT_RULE.ACTIVITY_REGEX AS ACTIVITY_REGEX,"
-
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_ACTIVITY_MOUNT_RULE "
-      "INNER JOIN "
-        "MOUNT_POLICY "
-      "ON "
-        "REQUESTER_ACTIVITY_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_ACTIVITY_MOUNT_RULE.DISK_INSTANCE_NAME = :ACTIVITY_DISK_INSTANCE_NAME AND "
-        "REQUESTER_ACTIVITY_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_ACTIVITY_NAME "
-      "UNION "
-      "SELECT "
-        "'REQUESTER' AS RULE_TYPE,"
-        "REQUESTER_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE,"
-        "'' AS ACTIVITY_REGEX,"
-
-
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_MOUNT_RULE "
-      "INNER JOIN "
-        "MOUNT_POLICY "
-      "ON "
-        "REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :REQUESTER_DISK_INSTANCE_NAME AND "
-        "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME "
-      "UNION "
-      "SELECT "
-        "'REQUESTER_GROUP' AS RULE_TYPE,"
-        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME AS ASSIGNEE,"
-        "'' AS ACTIVITY_REGEX,"
-
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_GROUP_MOUNT_RULE "
-      "INNER JOIN "
-        "MOUNT_POLICY "
-      "ON "
-        "REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :GROUP_DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":ACTIVITY_DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":GROUP_DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_ACTIVITY_NAME", requesterName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    auto rset = stmt.executeQuery();
-
-    RequesterAndGroupMountPolicies policies;
-    while(rset.next()) {
-      common::dataStructures::MountPolicy policy;
-
-      policy.name = rset.columnString("MOUNT_POLICY_NAME");
-      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
-      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
-      policy.comment = rset.columnString("USER_COMMENT");
-      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      if(rset.columnString("RULE_TYPE") == "ACTIVITY") {
-        auto activityRegexString = rset.columnString("ACTIVITY_REGEX");
-        cta::utils::Regex activityRegex(activityRegexString);
-        if (activityRegex.has_match(activity)) {
-          policies.requesterActivityMountPolicies.push_back(policy);
-        }
-      } else if(rset.columnString("RULE_TYPE") == "REQUESTER") {
-        policies.requesterMountPolicies.push_back(policy);
-      } else {
-        policies.requesterGroupMountPolicies.push_back(policy);
-      }
-    }
-
-    return policies;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMountPolicies
-//------------------------------------------------------------------------------
-RequesterAndGroupMountPolicies RdbmsCatalogue::getMountPolicies(
-  rdbms::Conn &conn,
-  const std::string &diskInstanceName,
-  const std::string &requesterName,
-  const std::string &requesterGroupName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "'REQUESTER' AS RULE_TYPE,"
-        "REQUESTER_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE,"
-
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_MOUNT_RULE "
-      "INNER JOIN "
-        "MOUNT_POLICY "
-      "ON "
-        "REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :REQUESTER_DISK_INSTANCE_NAME AND "
-        "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME "
-      "UNION "
-      "SELECT "
-        "'REQUESTER_GROUP' AS RULE_TYPE,"
-        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME AS ASSIGNEE,"
-
-        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
-        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
-        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
-        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
-        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
-        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
-      "FROM "
-        "REQUESTER_GROUP_MOUNT_RULE "
-      "INNER JOIN "
-        "MOUNT_POLICY "
-      "ON "
-        "REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
-      "WHERE "
-        "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :GROUP_DISK_INSTANCE_NAME AND "
-        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":REQUESTER_DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":GROUP_DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":REQUESTER_NAME", requesterName);
-    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
-    auto rset = stmt.executeQuery();
-
-    RequesterAndGroupMountPolicies policies;
-    while(rset.next()) {
-      common::dataStructures::MountPolicy policy;
-
-      policy.name = rset.columnString("MOUNT_POLICY_NAME");
-      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
-      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
-      policy.comment = rset.columnString("USER_COMMENT");
-      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
-      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
-      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
-      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
-      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
-      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
-
-      if(rset.columnString("RULE_TYPE") == "REQUESTER") {
-        policies.requesterMountPolicies.push_back(policy);
-      } else {
-        policies.requesterGroupMountPolicies.push_back(policy);
-      }
-    }
-
-    return policies;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// isAdmin
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::isAdmin(const common::dataStructures::SecurityIdentity &admin) const {
-  try {
-    return isCachedAdmin(admin);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// isCachedAdmin
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::isCachedAdmin(const common::dataStructures::SecurityIdentity &admin)
-  const {
-  try {
-    auto getNonCachedValue = [&] {
-      return isNonCachedAdmin(admin);
-    };
-    return m_isAdminCache.getCachedValue(admin, getNonCachedValue).value;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// isNonCachedAdmin
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::isNonCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ADMIN_USER_NAME AS ADMIN_USER_NAME "
-      "FROM "
-        "ADMIN_USER "
-      "WHERE "
-        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":ADMIN_USER_NAME", admin.username);
-    auto rset = stmt.executeQuery();
-    return rset.next();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapesForWriting
-//------------------------------------------------------------------------------
-std::list<TapeForWriting> RdbmsCatalogue::getTapesForWriting(const std::string &logicalLibraryName) const {
-  try {
-    std::list<TapeForWriting> tapes;
-    const char *const sql =
-      "SELECT "
-        "TAPE.VID AS VID,"
-        "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
-        "TAPE.VENDOR AS VENDOR,"
-        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
-        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
-        "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
-        "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
-        "TAPE.LAST_FSEQ AS LAST_FSEQ,"
-        "TAPE.LABEL_FORMAT AS LABEL_FORMAT "
-      "FROM "
-        "TAPE "
-      "INNER JOIN TAPE_POOL ON "
-        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-      "INNER JOIN LOGICAL_LIBRARY ON "
-        "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
-      "INNER JOIN MEDIA_TYPE ON "
-        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
-      "INNER JOIN VIRTUAL_ORGANIZATION ON "
-        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
-      "WHERE "
-//      "TAPE.LABEL_DRIVE IS NOT NULL AND " // Set when the tape has been labelled
-//      "TAPE.LABEL_TIME IS NOT NULL AND "  // Set when the tape has been labelled
-        "TAPE.TAPE_STATE = :TAPE_STATE AND "
-        "TAPE.IS_FULL = '0' AND "
-        "TAPE.IS_FROM_CASTOR = '0' AND "
-        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME "
-      "ORDER BY TAPE.DATA_IN_BYTES DESC";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName);
-    stmt.bindString(":TAPE_STATE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
-    auto rset = stmt.executeQuery();
-    while (rset.next()) {
-      TapeForWriting tape;
-      tape.vid = rset.columnString("VID");
-      tape.mediaType = rset.columnString("MEDIA_TYPE");
-      tape.vendor = rset.columnString("VENDOR");
-      tape.tapePool = rset.columnString("TAPE_POOL_NAME");
-      tape.vo = rset.columnString("VO");
-      tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
-      tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES");
-      tape.lastFSeq = rset.columnUint64("LAST_FSEQ");
-      tape.labelFormat = common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"), "[RdbmsCatalogue::getTapesForWriting()]");
-
-      tapes.push_back(tape);
-    }
-    return tapes;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-common::dataStructures::Label::Format RdbmsCatalogue::getTapeLabelFormat(const std::string& vid) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "TAPE.LABEL_FORMAT AS LABEL_FORMAT "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :VID";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    if(rset.next()) {
-      return common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"), "[RdbmsCatalogue::getTapeLabelFormat()]");
-    } else {
-      throw exception::Exception(std::string("No such tape with vid=") + vid);
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// insertTapeFile
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::insertTapeFile(
-  rdbms::Conn &conn,
-  const common::dataStructures::TapeFile &tapeFile,
-  const uint64_t archiveFileId) {
-  rdbms::AutoRollback autoRollback(conn);
-  try{
-
-      std::list<InsertFileRecycleLog> insertedFilesRecycleLog = insertOldCopiesOfFilesIfAnyOnFileRecycleLog(conn,tapeFile,archiveFileId);
-      {
-        const time_t now = time(nullptr);
-        const char *const sql =
-          "INSERT INTO TAPE_FILE("
-            "VID,"
-            "FSEQ,"
-            "BLOCK_ID,"
-            "LOGICAL_SIZE_IN_BYTES,"
-            "COPY_NB,"
-            "CREATION_TIME,"
-            "ARCHIVE_FILE_ID)"
-          "VALUES("
-            ":VID,"
-            ":FSEQ,"
-            ":BLOCK_ID,"
-            ":LOGICAL_SIZE_IN_BYTES,"
-            ":COPY_NB,"
-            ":CREATION_TIME,"
-            ":ARCHIVE_FILE_ID)";
-        auto stmt = conn.createStmt(sql);
-
-        stmt.bindString(":VID", tapeFile.vid);
-        stmt.bindUint64(":FSEQ", tapeFile.fSeq);
-        stmt.bindUint64(":BLOCK_ID", tapeFile.blockId);
-        stmt.bindUint64(":LOGICAL_SIZE_IN_BYTES", tapeFile.fileSize);
-        stmt.bindUint64(":COPY_NB", tapeFile.copyNb);
-        stmt.bindUint64(":CREATION_TIME", now);
-        stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-        stmt.executeNonQuery();
-      }
-    {
-      for(auto& fileRecycleLog: insertedFilesRecycleLog){
-        const char *const sql =
-        "DELETE FROM "
-          "TAPE_FILE "
-        "WHERE "
-          "VID=:VID AND "
-          "FSEQ=:FSEQ";
-        auto stmt = conn.createStmt(sql);
-        stmt.bindString(":VID",fileRecycleLog.vid);
-        stmt.bindUint64(":FSEQ",fileRecycleLog.fSeq);
-        stmt.executeNonQuery();
-      }
-    }
-    conn.commit();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// setTapeLastFseq
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::setTapeLastFSeq(rdbms::Conn &conn, const std::string &vid, const uint64_t lastFSeq) {
-  try {
-    threading::MutexLocker locker(m_mutex);
-
-    const uint64_t currentValue = getTapeLastFSeq(conn, vid);
-    if(lastFSeq != currentValue + 1) {
-      exception::Exception ex;
-      ex.getMessage() << "The last FSeq MUST be incremented by exactly one: currentValue=" << currentValue <<
-        ",nextValue=" << lastFSeq;
-      throw ex;
-    }
-    const char *const sql =
-      "UPDATE TAPE SET "
-        "LAST_FSEQ = :LAST_FSEQ "
-      "WHERE "
-        "VID=:VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    stmt.bindUint64(":LAST_FSEQ", lastFSeq);
-    stmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapeLastFSeq
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "LAST_FSEQ AS LAST_FSEQ "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :VID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    if(rset.next()) {
-      return rset.columnUint64("LAST_FSEQ");
-    } else {
-      throw exception::Exception(std::string("No such tape with vid=") + vid);
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileRowByArchiveId
-//------------------------------------------------------------------------------
-std::unique_ptr<ArchiveFileRow> RdbmsCatalogue::getArchiveFileRowById(rdbms::Conn &conn, const uint64_t id) const {
-  try {
-    const char *const sql =
-      "SELECT"                                                           "\n"
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"               "\n"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"         "\n"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"                     "\n"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"                   "\n"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"                   "\n"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"                   "\n"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"                   "\n"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"             "\n"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"        "\n"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"      "\n"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME"        "\n"
-      "FROM"                                                             "\n"
-        "ARCHIVE_FILE"                                                   "\n"
-      "INNER JOIN STORAGE_CLASS ON"                                      "\n"
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID" "\n"
-      "WHERE"                                                            "\n"
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", id);
-    auto rset = stmt.executeQuery();
-
-    std::unique_ptr<ArchiveFileRow> row;
-    if (rset.next()) {
-      row = std::make_unique<ArchiveFileRow>();
-
-      row->archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
-      row->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-      row->diskFileId = rset.columnString("DISK_FILE_ID");
-      row->diskFileOwnerUid = rset.columnUint64("DISK_FILE_UID");
-      row->diskFileGid = rset.columnUint64("DISK_FILE_GID");
-      row->size = rset.columnUint64("SIZE_IN_BYTES");
-      row->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-      row->storageClassName = rset.columnString("STORAGE_CLASS_NAME");
-      row->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-      row->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
-    }
-
-    return row;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileToRetrieveByArchiveFileId
-//------------------------------------------------------------------------------
-std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsCatalogue::getArchiveFileToRetrieveByArchiveFileId(
-  rdbms::Conn &conn, const uint64_t archiveFileId) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "INNER JOIN TAPE ON "
-        "TAPE_FILE.VID = TAPE.VID "
-      "WHERE "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID AND "
-        "TAPE.TAPE_STATE IN ('ACTIVE', 'DISABLED') "
-      "ORDER BY "
-        "TAPE_FILE.CREATION_TIME ASC";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    auto rset = stmt.executeQuery();
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
-    while (rset.next()) {
-      if(nullptr == archiveFile.get()) {
-        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
-
-        archiveFile->archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
-        archiveFile->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-        archiveFile->diskFileId = rset.columnString("DISK_FILE_ID");
-        archiveFile->diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
-        archiveFile->diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
-        archiveFile->fileSize = rset.columnUint64("SIZE_IN_BYTES");
-        archiveFile->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-        archiveFile->storageClass = rset.columnString("STORAGE_CLASS_NAME");
-        archiveFile->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-        archiveFile->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
-      }
-
-      // If there is a tape file we add it to the archiveFile's list of tape files
-      if(!rset.columnIsNull("VID")) {
-        // Add the tape file to the archive file's in-memory structure
-        common::dataStructures::TapeFile tapeFile;
-        tapeFile.vid = rset.columnString("VID");
-        tapeFile.fSeq = rset.columnUint64("FSEQ");
-        tapeFile.blockId = rset.columnUint64("BLOCK_ID");
-        tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-        tapeFile.copyNb = rset.columnUint64("COPY_NB");
-        tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
-
-        archiveFile->tapeFiles.push_back(tapeFile);
-      }
-    }
-
-    //If there are no tape files that belong to the archive file, then return a nullptr.
-    if(archiveFile.get() != nullptr && archiveFile->tapeFiles.empty()){
-      archiveFile.reset(nullptr);
-    }
-
-    return archiveFile;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileToRetrieveByArchiveFileId
-//------------------------------------------------------------------------------
-const std::list<std::pair<std::string, std::string>> RdbmsCatalogue::getTapeFileStateListForArchiveFileId(
-  rdbms::Conn &conn, const uint64_t archiveFileId) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "TAPE_FILE.VID AS VID,"
-        "TAPE.TAPE_STATE AS STATE "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "INNER JOIN TAPE ON "
-        "TAPE_FILE.VID = TAPE.VID "
-      "WHERE "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID ";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    auto rset = stmt.executeQuery();
-    std::list<std::pair<std::string, std::string>> ret;
-    while (rset.next()) {
-      const auto &vid = rset.columnString("VID");
-      const auto &state = rset.columnString("STATE");
-      ret.push_back(std::pair<std::string, std::string>(vid, state));
-    }
-    return ret;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileByDiskFileId
-//------------------------------------------------------------------------------
-std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsCatalogue::getArchiveFileByDiskFileId(
-  rdbms::Conn &conn,
-  const std::string &diskInstanceName,
-  const std::string &diskFileId) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "WHERE "
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "ARCHIVE_FILE.DISK_FILE_ID = :DISK_FILE_ID "
-      "ORDER BY "
-        "TAPE_FILE.CREATION_TIME ASC";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":DISK_FILE_ID", diskFileId);
-    auto rset = stmt.executeQuery();
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
-    while (rset.next()) {
-      if(nullptr == archiveFile.get()) {
-        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
-
-        archiveFile->archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
-        archiveFile->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-        archiveFile->diskFileId = rset.columnString("DISK_FILE_ID");
-        archiveFile->diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
-        archiveFile->diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
-        archiveFile->fileSize = rset.columnUint64("SIZE_IN_BYTES");
-        archiveFile->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-        archiveFile->storageClass = rset.columnString("STORAGE_CLASS_NAME");
-        archiveFile->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-        archiveFile->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
-      }
-
-      // If there is a tape file
-      if(!rset.columnIsNull("VID")) {
-        // Add the tape file to the archive file's in-memory structure
-        common::dataStructures::TapeFile tapeFile;
-        tapeFile.vid = rset.columnString("VID");
-        tapeFile.fSeq = rset.columnUint64("FSEQ");
-        tapeFile.blockId = rset.columnUint64("BLOCK_ID");
-        tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-        tapeFile.copyNb = rset.columnUint64("COPY_NB");
-        tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
-
-        archiveFile->tapeFiles.push_back(tapeFile);
-      }
-    }
-
-    return archiveFile;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getArchiveFileToRetrieveByDiskFileId
-//------------------------------------------------------------------------------
-std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsCatalogue::getArchiveFileToRetrieveByDiskFileId(
-  rdbms::Conn &conn,
-  const std::string &diskInstanceName,
-  const std::string &diskFileId) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
-        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
-      "FROM "
-        "ARCHIVE_FILE "
-      "INNER JOIN STORAGE_CLASS ON "
-        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
-      "INNER JOIN TAPE_FILE ON "
-        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
-      "INNER JOIN TAPE ON "
-        "TAPE_FILE.VID = TAPE.VID "
-      "WHERE "
-        "ARCHIVE_FILE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
-        "ARCHIVE_FILE.DISK_FILE_ID = :DISK_FILE_ID AND "
-        "TAPE.TAPE_STATE = :TAPE_STATE "
-      "ORDER BY "
-        "TAPE_FILE.CREATION_TIME ASC";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
-    stmt.bindString(":DISK_FILE_ID", diskFileId);
-    stmt.bindString(":TAPE_STATE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
-    auto rset = stmt.executeQuery();
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
-    while (rset.next()) {
-      if(nullptr == archiveFile.get()) {
-        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
-
-        archiveFile->archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
-        archiveFile->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
-        archiveFile->diskFileId = rset.columnString("DISK_FILE_ID");
-        archiveFile->diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
-        archiveFile->diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
-        archiveFile->fileSize = rset.columnUint64("SIZE_IN_BYTES");
-        archiveFile->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
-        archiveFile->storageClass = rset.columnString("STORAGE_CLASS_NAME");
-        archiveFile->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
-        archiveFile->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
-      }
-
-      // If there is a tape file
-      if(!rset.columnIsNull("VID")) {
-        // Add the tape file to the archive file's in-memory structure
-        common::dataStructures::TapeFile tapeFile;
-        tapeFile.vid = rset.columnString("VID");
-        tapeFile.fSeq = rset.columnUint64("FSEQ");
-        tapeFile.blockId = rset.columnUint64("BLOCK_ID");
-        tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
-        tapeFile.copyNb = rset.columnUint64("COPY_NB");
-        tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
-
-        archiveFile->tapeFiles.push_back(tapeFile);
-      }
-    }
-
-    return archiveFile;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// ping
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::ping() {
-  try {
-    verifySchemaVersion();
-  } catch (WrongSchemaVersionException &){
-    throw;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// verifySchemaVersion
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::verifySchemaVersion() {
-  try {
-    SchemaVersion schemaVersion = getSchemaVersion();
-    auto schemaVersionMajorMinor = schemaVersion.getSchemaVersion<SchemaVersion::MajorMinor>();
-    if(schemaVersionMajorMinor.first != (uint64_t) CTA_CATALOGUE_SCHEMA_VERSION_MAJOR){
-      std::ostringstream exceptionMsg;
-      exceptionMsg << "Catalogue schema MAJOR version differ : Database schema version is " << schemaVersionMajorMinor.first << "." << schemaVersionMajorMinor.second << ", CTA schema version is "<< CTA_CATALOGUE_SCHEMA_VERSION_MAJOR << "." << CTA_CATALOGUE_SCHEMA_VERSION_MINOR;
-      throw WrongSchemaVersionException(exceptionMsg.str());
-    }
-    if(schemaVersion.getStatus<SchemaVersion::Status>() == SchemaVersion::Status::UPGRADING){
-      std::ostringstream exceptionMsg;
-      exceptionMsg << "Catalogue schema is in status "+schemaVersion.getStatus<std::string>()+", next schema version is "<<schemaVersion.getSchemaVersionNext<std::string>();
-    }
-  } catch (exception::UserError &ex) {
-    throw;
-  } catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getSchemaVersion
-//------------------------------------------------------------------------------
-SchemaVersion RdbmsCatalogue::getSchemaVersion() const {
-  try {
-    std::map<std::string, uint64_t> schemaVersion;
-    const char *const sql =
-      "SELECT "
-        "CTA_CATALOGUE.SCHEMA_VERSION_MAJOR AS SCHEMA_VERSION_MAJOR,"
-        "CTA_CATALOGUE.SCHEMA_VERSION_MINOR AS SCHEMA_VERSION_MINOR,"
-        "CTA_CATALOGUE.NEXT_SCHEMA_VERSION_MAJOR AS NEXT_SCHEMA_VERSION_MAJOR,"
-        "CTA_CATALOGUE.NEXT_SCHEMA_VERSION_MINOR AS NEXT_SCHEMA_VERSION_MINOR,"
-        "CTA_CATALOGUE.STATUS AS STATUS "
-      "FROM "
-        "CTA_CATALOGUE";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-
-    if(rset.next()) {
-      SchemaVersion::Builder schemaVersionBuilder;
-      schemaVersionBuilder.schemaVersionMajor(rset.columnUint64("SCHEMA_VERSION_MAJOR"))
-                          .schemaVersionMinor(rset.columnUint64("SCHEMA_VERSION_MINOR"))
-                          .status(rset.columnString("STATUS"));
-      auto schemaVersionMajorNext = rset.columnOptionalUint64("NEXT_SCHEMA_VERSION_MAJOR");
-      auto schemaVersionMinorNext = rset.columnOptionalUint64("NEXT_SCHEMA_VERSION_MINOR");
-      if(schemaVersionMajorNext && schemaVersionMinorNext){
-        schemaVersionBuilder.nextSchemaVersionMajor(schemaVersionMajorNext.value())
-                            .nextSchemaVersionMinor(schemaVersionMinorNext.value());
-      }
-      return schemaVersionBuilder.build();
-    } else {
-      throw exception::Exception("CTA_CATALOGUE does not contain any row");
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTableNames
-//------------------------------------------------------------------------------
-std::list<std::string> RdbmsCatalogue::getTableNames() const {
-  auto conn = m_connPool.getConn();
-  return conn.getTableNames();
-}
-
-//------------------------------------------------------------------------------
-// checkTapeFileWrittenFieldsAreSet
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkTapeFileWrittenFieldsAreSet(const std::string &callingFunc, const TapeFileWritten &event)
-  const {
-  try {
-    if(event.diskInstance.empty()) throw exception::Exception("diskInstance is an empty string");
-    if(event.diskFileId.empty()) throw exception::Exception("diskFileId is an empty string");
-    if(0 == event.diskFileOwnerUid) throw exception::Exception("diskFileOwnerUid is 0");
-    if(0 == event.size) throw exception::Exception("size is 0");
-    if(event.checksumBlob.length() == 0) throw exception::Exception("checksumBlob is an empty string");
-    if(event.storageClassName.empty()) throw exception::Exception("storageClassName is an empty string");
-    if(event.vid.empty()) throw exception::Exception("vid is an empty string");
-    if(0 == event.fSeq) throw exception::Exception("fSeq is 0");
-    if(0 == event.blockId && event.fSeq != 1) throw exception::Exception("blockId is 0 and fSeq is not 1");
-    if(0 == event.copyNb) throw exception::Exception("copyNb is 0");
-    if(event.tapeDrive.empty()) throw exception::Exception("tapeDrive is an empty string");
-  } catch (exception::Exception &ex) {
-    throw exception::Exception(callingFunc + " failed: TapeFileWrittenEvent is invalid: " + ex.getMessage().str());
-  }
-}
-
-//------------------------------------------------------------------------------
-// checkDeleteRequestConsistency
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkDeleteRequestConsistency(const cta::common::dataStructures::DeleteArchiveRequest deleteRequest, const cta::common::dataStructures::ArchiveFile& archiveFile) const {
-  if(deleteRequest.diskInstance != archiveFile.diskInstance){
-    std::ostringstream msg;
-    msg << "Failed to move archive file with ID " << deleteRequest.archiveFileID << " to the recycle-bin because the disk instance of "
-      "the request does not match that of the archived file: archiveFileId=" << archiveFile.archiveFileID << " requestDiskInstance=" << deleteRequest.diskInstance << " archiveFileDiskInstance=" <<
-      archiveFile.diskInstance;
-    throw cta::exception::Exception(msg.str());
-  }
-  if(deleteRequest.diskFilePath.empty()){
-    std::ostringstream msg;
-    msg << "Failed to move archive file with ID " << deleteRequest.archiveFileID << " to the recycle-bin because the disk file path has not been provided.";
-    throw cta::exception::Exception(msg.str());
-  }
-}
-
-//------------------------------------------------------------------------------
-// checkTapeItemWrittenFieldsAreSet
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::checkTapeItemWrittenFieldsAreSet(const std::string& callingFunc, const TapeItemWritten& event) const {
-  try {
-    if(event.vid.empty()) throw exception::Exception("vid is an empty string");
-    if(0 == event.fSeq) throw exception::Exception("fSeq is 0");
-    if(event.tapeDrive.empty()) throw exception::Exception("tapeDrive is an empty string");
-  } catch (exception::Exception &ex) {
-    throw exception::Exception(callingFunc + " failed: TapeItemWrittenEvent is invalid: " + ex.getMessage().str());
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNbTapesInPool
-//------------------------------------------------------------------------------
-uint64_t RdbmsCatalogue::getNbTapesInPool(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "COUNT(*) AS NB_TAPES "
-      "FROM "
-        "TAPE "
-      "INNER JOIN TAPE_POOL ON "
-        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
-      "WHERE "
-        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      throw exception::Exception("Result set of SELECT COUNT(*) is empty");
-    }
-    return rset.columnUint64("NB_TAPES");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// isSetAndEmpty
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::isSetAndEmpty(const std::optional<std::string> &optionalStr) const {
-  return optionalStr && optionalStr->empty();
-}
-
-//------------------------------------------------------------------------------
-// isSetAndEmpty
-//------------------------------------------------------------------------------
-bool RdbmsCatalogue::isSetAndEmpty(const std::optional<std::vector<std::string>> &optionalStrList) const {
-  return optionalStrList && optionalStrList->empty();
-}
-
-//------------------------------------------------------------------------------
-// getLogicalLibraryId
-//------------------------------------------------------------------------------
-std::optional<uint64_t> RdbmsCatalogue::getLogicalLibraryId(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT"                                                         "\n"
-        "LOGICAL_LIBRARY_ID AS LOGICAL_LIBRARY_ID"                     "\n"
-      "FROM"                                                           "\n"
-        "LOGICAL_LIBRARY"                                              "\n"
-      "WHERE"                                                          "\n"
-        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      return std::nullopt;
-    }
-    return rset.columnUint64("LOGICAL_LIBRARY_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapePoolId
-//------------------------------------------------------------------------------
-std::optional<uint64_t> RdbmsCatalogue::getTapePoolId(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT"                                       "\n"
-        "TAPE_POOL_ID AS TAPE_POOL_ID"               "\n"
-      "FROM"                                         "\n"
-        "TAPE_POOL"                                  "\n"
-      "WHERE"                                        "\n"
-        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      return std::nullopt;
-    }
-    return rset.columnUint64("TAPE_POOL_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getMediaTypeId
-//------------------------------------------------------------------------------
-std::optional<uint64_t> RdbmsCatalogue::getMediaTypeId(rdbms::Conn &conn, const std::string &name) const {
-  try {
-    const char *const sql =
-      "SELECT"                                       "\n"
-        "MEDIA_TYPE.MEDIA_TYPE_ID AS MEDIA_TYPE_ID"  "\n"
-      "FROM"                                         "\n"
-        "MEDIA_TYPE"                                 "\n"
-      "WHERE"                                        "\n"
-        "MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":MEDIA_TYPE_NAME", name);
-    auto rset = stmt.executeQuery();
-    if(!rset.next()) {
-      return std::nullopt;
-    }
-    return rset.columnUint64("MEDIA_TYPE_ID");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// updateDiskFileId
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::updateDiskFileId(const uint64_t archiveFileId, const std::string &diskInstance,
-  const std::string &diskFileId) {
-  try {
-    const char *const sql =
-      "UPDATE ARCHIVE_FILE SET"                     "\n"
-        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME," "\n"
-        "DISK_FILE_ID = :DISK_FILE_ID"              "\n"
-      "WHERE"                                       "\n"
-        "ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.bindString(":DISK_FILE_ID", diskFileId);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      std::ostringstream msg;
-      msg << "Cannot update the disk file ID of the archive file with archive file ID " << archiveFileId <<
-        " because the archive file does not exist";
-      throw exception::UserError(msg.str());
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// moveArchiveFileToRecycleBin
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
-  log::LogContext & lc) {
-  if(!request.archiveFile){
-    //The archive file does not exist in the catalogue, nothing to do with it
-    return;
-  }
-  cta::common::dataStructures::ArchiveFile archiveFile = request.archiveFile.value();
-  utils::Timer t, totalTime;
-  log::TimingList tl;
-  try {
-    checkDeleteRequestConsistency(request,archiveFile);
-    tl.insertAndReset("checkDeleteRequestConsistency",t);
-  } catch(const cta::exception::Exception & ex){
-    log::ScopedParamContainer spc(lc);
-    spc.add("fileId", std::to_string(request.archiveFileID))
-     .add("diskInstance", archiveFile.diskInstance)
-     .add("requestDiskInstance", request.diskInstance)
-     .add("diskFileId", archiveFile.diskFileId)
-     .add("diskFileInfo.owner_uid", archiveFile.diskFileInfo.owner_uid)
-     .add("diskFileInfo.gid", archiveFile.diskFileInfo.gid)
-     .add("fileSize", std::to_string(archiveFile.fileSize))
-     .add("creationTime", std::to_string(archiveFile.creationTime))
-     .add("reconciliationTime", std::to_string(archiveFile.reconciliationTime))
-     .add("diskFilePath",request.diskFilePath)
-     .add("errorMessage",ex.getMessageValue())
-     .add("storageClass", archiveFile.storageClass);
-    archiveFile.checksumBlob.addFirstChecksumToLog(spc);
-    for(auto it=archiveFile.tapeFiles.begin(); it!=archiveFile.tapeFiles.end(); it++) {
-      std::stringstream tapeCopyLogStream;
-      tapeCopyLogStream << "copy number: " << static_cast<int>(it->copyNb)
-        << " vid: " << it->vid
-        << " fSeq: " << it->fSeq
-        << " blockId: " << it->blockId
-        << " creationTime: " << it->creationTime
-        << " fileSize: " << it->fileSize;
-      spc.add("TAPE FILE", tapeCopyLogStream.str());
-    }
-    lc.log(log::WARNING, "Failed to move archive file to the file-recycle-log.");
-
-    exception::UserError ue;
-    ue.getMessage() << "Failed to move archive file with ID " << request.archiveFileID << " to the file-recycle-log. errorMessage=" << ex.getMessageValue();
-    throw ue;
-  }
-
-  try {
-    //All checks are good, we can move the file to the recycle-bin
-    auto conn = m_connPool.getConn();
-    rdbms::AutoRollback autoRollback(conn);
-    copyArchiveFileToFileRecyleLogAndDelete(conn,request,lc);
-    tl.insertAndReset("copyArchiveFileToFileRecyleLogAndDeleteTime",t);
-    tl.insertAndReset("totalTime",totalTime);
-    log::ScopedParamContainer spc(lc);
-    spc.add("fileId", std::to_string(request.archiveFileID))
-     .add("diskInstance", archiveFile.diskInstance)
-     .add("requestDiskInstance", request.diskInstance)
-     .add("diskFileId", archiveFile.diskFileId)
-     .add("diskFileInfo.owner_uid", archiveFile.diskFileInfo.owner_uid)
-     .add("diskFileInfo.gid", archiveFile.diskFileInfo.gid)
-     .add("fileSize", std::to_string(archiveFile.fileSize))
-     .add("creationTime", std::to_string(archiveFile.creationTime))
-     .add("reconciliationTime", std::to_string(archiveFile.reconciliationTime))
-     .add("storageClass", archiveFile.storageClass);
-    archiveFile.checksumBlob.addFirstChecksumToLog(spc);
-    for(auto it=archiveFile.tapeFiles.begin(); it!=archiveFile.tapeFiles.end(); it++) {
-      std::stringstream tapeCopyLogStream;
-      tapeCopyLogStream << "copy number: " << static_cast<int>(it->copyNb)
-        << " vid: " << it->vid
-        << " fSeq: " << it->fSeq
-        << " blockId: " << it->blockId
-        << " creationTime: " << it->creationTime
-        << " fileSize: " << it->fileSize;
-      spc.add("TAPE FILE", tapeCopyLogStream.str());
-    }
-    tl.addToLog(spc);
-    lc.log(log::INFO, "In RdbmsCatalogue::moveArchiveFileToRecycleLog(): ArchiveFile moved to the file-recycle-log.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// copyArchiveFileToFileRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::copyArchiveFileToFileRecycleLog(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest & request) {
-  try{
-    if(!request.archiveFile){
-      throw cta::exception::Exception("No archiveFile object has been set in the DeleteArchiveRequest object.");
-    }
-    const common::dataStructures::ArchiveFile & archiveFile = request.archiveFile.value();
-
-    for(auto &tapeFile: archiveFile.tapeFiles){
-      //Create one file recycle log entry per tape file
-      InsertFileRecycleLog fileRecycleLog;
-      fileRecycleLog.vid = tapeFile.vid;
-      fileRecycleLog.fSeq = tapeFile.fSeq;
-      fileRecycleLog.blockId = tapeFile.blockId;
-      fileRecycleLog.copyNb = tapeFile.copyNb;
-      fileRecycleLog.tapeFileCreationTime = tapeFile.creationTime;
-      fileRecycleLog.archiveFileId = archiveFile.archiveFileID;
-      fileRecycleLog.diskFilePath = request.diskFilePath;
-      fileRecycleLog.reasonLog = InsertFileRecycleLog::getDeletionReasonLog(request.requester.name,request.diskInstance);
-      fileRecycleLog.recycleLogTime = time(nullptr);
-      insertFileInFileRecycleLog(conn,fileRecycleLog);
-    }
-
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-//------------------------------------------------------------------------------
-// copyTapeFilesToFileRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::copyTapeFilesToFileRecycleLog(rdbms::Conn & conn, const common::dataStructures::ArchiveFile &archiveFile, const std::string &reason) {
-  try {
-    for(auto &tapeFile: archiveFile.tapeFiles) {
-      //Create one file recycle log entry per tape file
-      InsertFileRecycleLog fileRecycleLog;
-      fileRecycleLog.vid = tapeFile.vid;
-      fileRecycleLog.fSeq = tapeFile.fSeq;
-      fileRecycleLog.blockId = tapeFile.blockId;
-      fileRecycleLog.copyNb = tapeFile.copyNb;
-      fileRecycleLog.tapeFileCreationTime = tapeFile.creationTime;
-      fileRecycleLog.archiveFileId = archiveFile.archiveFileID;
-      fileRecycleLog.diskFilePath = archiveFile.diskFileInfo.path;
-      fileRecycleLog.reasonLog = "(Deleted using cta-admin tf rm) " + reason;
-      fileRecycleLog.recycleLogTime = time(nullptr);
-      insertFileInFileRecycleLog(conn,fileRecycleLog);
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// insertFileInFileRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::insertFileInFileRecycleLog(rdbms::Conn& conn, const InsertFileRecycleLog& fileRecycleLog){
-  try{
-    checkCommentOrReasonMaxLength(fileRecycleLog.reasonLog);
-    uint64_t fileRecycleLogId = getNextFileRecyleLogId(conn);
-    const char *const sql =
-    "INSERT INTO FILE_RECYCLE_LOG("
-      "FILE_RECYCLE_LOG_ID,"
-      "VID,"
-      "FSEQ,"
-      "BLOCK_ID,"
-      "COPY_NB,"
-      "TAPE_FILE_CREATION_TIME,"
-      "ARCHIVE_FILE_ID,"
-      "DISK_INSTANCE_NAME,"
-      "DISK_FILE_ID,"
-      "DISK_FILE_ID_WHEN_DELETED,"
-      "DISK_FILE_UID,"
-      "DISK_FILE_GID,"
-      "SIZE_IN_BYTES,"
-      "CHECKSUM_BLOB,"
-      "CHECKSUM_ADLER32,"
-      "STORAGE_CLASS_ID,"
-      "ARCHIVE_FILE_CREATION_TIME,"
-      "RECONCILIATION_TIME,"
-      "COLLOCATION_HINT,"
-      "DISK_FILE_PATH,"
-      "REASON_LOG,"
-      "RECYCLE_LOG_TIME"
-    ") SELECT "
-      ":FILE_RECYCLE_LOG_ID,"
-      ":VID,"
-      ":FSEQ,"
-      ":BLOCK_ID,"
-      ":COPY_NB,"
-      ":TAPE_FILE_CREATION_TIME,"
-      ":ARCHIVE_FILE_ID,"
-      "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
-      "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
-      "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID_2,"
-      "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
-      "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
-      "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
-      "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
-      "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
-      "ARCHIVE_FILE.STORAGE_CLASS_ID AS STORAGE_CLASS_ID,"
-      "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
-      "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
-      "ARCHIVE_FILE.COLLOCATION_HINT AS COLLOCATION_HINT,"
-      ":DISK_FILE_PATH,"
-      ":REASON_LOG,"
-      ":RECYCLE_LOG_TIME "
-    "FROM "
-      "ARCHIVE_FILE "
-    "WHERE "
-      "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID_2";
-    auto stmt = conn.createStmt(sql);
-    stmt.bindUint64(":FILE_RECYCLE_LOG_ID",fileRecycleLogId);
-    stmt.bindString(":VID",fileRecycleLog.vid);
-    stmt.bindUint64(":FSEQ",fileRecycleLog.fSeq);
-    stmt.bindUint64(":BLOCK_ID",fileRecycleLog.blockId);
-    stmt.bindUint8(":COPY_NB",fileRecycleLog.copyNb);
-    stmt.bindUint64(":TAPE_FILE_CREATION_TIME",fileRecycleLog.tapeFileCreationTime);
-    stmt.bindString(":DISK_FILE_PATH",fileRecycleLog.diskFilePath);
-    stmt.bindUint64(":ARCHIVE_FILE_ID",fileRecycleLog.archiveFileId);
-    stmt.bindString(":REASON_LOG",fileRecycleLog.reasonLog);
-    stmt.bindUint64(":RECYCLE_LOG_TIME",fileRecycleLog.recycleLogTime);
-    stmt.bindUint64(":ARCHIVE_FILE_ID_2",fileRecycleLog.archiveFileId);
-    stmt.executeNonQuery();
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::deleteTapeFiles(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest& request){
-  try {
-    //Delete the tape files after.
-    const char *const deleteTapeFilesSql =
-    "DELETE FROM "
-      "TAPE_FILE "
-    "WHERE TAPE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-
-    auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
-    deleteTapeFilesStmt.bindUint64(":ARCHIVE_FILE_ID",request.archiveFileID);
-    deleteTapeFilesStmt.executeNonQuery();
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::deleteTapeFiles(rdbms::Conn & conn, const common::dataStructures::ArchiveFile& file){
-  try {
-    for(auto &tapeFile: file.tapeFiles) {
-
-      //Delete the tape file.
-      const char *const deleteTapeFilesSql =
-      "DELETE FROM "
-        "TAPE_FILE "
-      "WHERE "
-       "TAPE_FILE.VID = :VID AND "
-       "TAPE_FILE.FSEQ = :FSEQ";
-
-      auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
-      deleteTapeFilesStmt.bindString(":VID", tapeFile.vid);
-      deleteTapeFilesStmt.bindUint64(":FSEQ", tapeFile.fSeq);
-      deleteTapeFilesStmt.executeNonQuery();
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-
-void RdbmsCatalogue::deleteArchiveFile(rdbms::Conn& conn, const common::dataStructures::DeleteArchiveRequest& request){
-  try{
-    const char *const deleteArchiveFileSql =
-    "DELETE FROM "
-      "ARCHIVE_FILE "
-    "WHERE ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-
-    auto deleteArchiveFileStmt = conn.createStmt(deleteArchiveFileSql);
-    deleteArchiveFileStmt.bindUint64(":ARCHIVE_FILE_ID",request.archiveFileID);
-    deleteArchiveFileStmt.executeNonQuery();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteFileFromRecycleBin
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteFileFromRecycleBin(const uint64_t archiveFileId, log::LogContext &lc) {
-  try {
-    auto conn = m_connPool.getConn();
-    rdbms::AutoRollback autoRollback(conn);
-    deleteTapeFilesAndArchiveFileFromRecycleBin(conn,archiveFileId,lc);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapeFilesFromRecycleBin
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteTapeFilesFromRecycleBin(cta::rdbms::Conn & conn, const uint64_t archiveFileId) {
-  try {
-    const char *const deleteTapeFilesSql =
-    "DELETE FROM "
-      "TAPE_FILE_RECYCLE_BIN "
-    "WHERE TAPE_FILE_RECYCLE_BIN.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-
-    auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
-    deleteTapeFilesStmt.bindUint64(":ARCHIVE_FILE_ID",archiveFileId);
-    deleteTapeFilesStmt.executeNonQuery();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapeFilesFromRecycleBin
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteArchiveFileFromRecycleBin(rdbms::Conn& conn, const uint64_t archiveFileId) {
-  try {
-    const char *const deleteArchiveFileSql =
-    "DELETE FROM "
-      "ARCHIVE_FILE_RECYCLE_BIN "
-    "WHERE ARCHIVE_FILE_RECYCLE_BIN.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-
-    auto deleteTapeFilesStmt = conn.createStmt(deleteArchiveFileSql);
-    deleteTapeFilesStmt.bindUint64(":ARCHIVE_FILE_ID",archiveFileId);
-    deleteTapeFilesStmt.executeNonQuery();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapeFileCopyFromRecycleBin
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteTapeFileCopyFromRecycleBin(cta::rdbms::Conn & conn, const common::dataStructures::FileRecycleLog fileRecycleLog) {
-  try {
-    const char *const deleteTapeFilesSql =
-    "DELETE FROM "
-      "FILE_RECYCLE_LOG "
-    "WHERE FILE_RECYCLE_LOG.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID AND FILE_RECYCLE_LOG.VID = :VID AND "
-    "FILE_RECYCLE_LOG.FSEQ = :FSEQ AND FILE_RECYCLE_LOG.COPY_NB = :COPY_NB AND "
-    "FILE_RECYCLE_LOG.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
-
-    auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
-    deleteTapeFilesStmt.bindUint64(":ARCHIVE_FILE_ID", fileRecycleLog.archiveFileId);
-    deleteTapeFilesStmt.bindString(":VID", fileRecycleLog.vid);
-    deleteTapeFilesStmt.bindUint64(":FSEQ", fileRecycleLog.fSeq);
-    deleteTapeFilesStmt.bindUint64(":COPY_NB", fileRecycleLog.copyNb);
-    deleteTapeFilesStmt.bindString(":DISK_INSTANCE_NAME", fileRecycleLog.diskInstanceName);
-    deleteTapeFilesStmt.executeNonQuery();
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// insertOldCopiesOfFilesIfAnyOnFileRecycleLog
-//------------------------------------------------------------------------------
-std::list<InsertFileRecycleLog> RdbmsCatalogue::insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn & conn,const common::dataStructures::TapeFile & tapefile, const uint64_t archiveFileId){
-  std::list<InsertFileRecycleLog> fileRecycleLogsToInsert;
-  try {
-    //First, get the file to insert on the FILE_RECYCLE_LOG table
-    {
-      const char *const sql =
-      "SELECT "
-        "TAPE_FILE.VID AS VID,"
-        "TAPE_FILE.FSEQ AS FSEQ,"
-        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
-        "TAPE_FILE.COPY_NB AS COPY_NB,"
-        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
-        "TAPE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
-      "FROM "
-        "TAPE_FILE "
-      "WHERE "
-        "TAPE_FILE.COPY_NB=:COPY_NB AND TAPE_FILE.ARCHIVE_FILE_ID=:ARCHIVE_FILE_ID AND (TAPE_FILE.VID<>:VID OR TAPE_FILE.FSEQ<>:FSEQ)";
-
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint8(":COPY_NB",tapefile.copyNb);
-      stmt.bindUint64(":ARCHIVE_FILE_ID",archiveFileId);
-      stmt.bindString(":VID",tapefile.vid);
-      stmt.bindUint64(":FSEQ",tapefile.fSeq);
-
-      auto rset = stmt.executeQuery();
-      while(rset.next()){
-        cta::catalogue::InsertFileRecycleLog fileRecycleLog;
-        fileRecycleLog.vid = rset.columnString("VID");
-        fileRecycleLog.fSeq = rset.columnUint64("FSEQ");
-        fileRecycleLog.blockId = rset.columnUint64("BLOCK_ID");
-        fileRecycleLog.copyNb = rset.columnUint8("COPY_NB");
-        fileRecycleLog.tapeFileCreationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
-        fileRecycleLog.archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
-        fileRecycleLog.reasonLog = InsertFileRecycleLog::getRepackReasonLog();
-        fileRecycleLog.recycleLogTime = time(nullptr);
-        fileRecycleLogsToInsert.push_back(fileRecycleLog);
-      }
-    }
-    {
-      for(auto & fileRecycleLog: fileRecycleLogsToInsert){
-        insertFileInFileRecycleLog(conn,fileRecycleLog);
-      }
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-  return fileRecycleLogsToInsert;
-}
-
-//------------------------------------------------------------------------------
-// deleteFilesFromRecycleBin
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteFilesFromRecycleBin(rdbms::Conn & conn, const std::string& vid, cta::log::LogContext & lc) {
-  try {
-    const char *const selectArchiveFileIdSql =
-    "SELECT "
-      "TAPE_FILE_RECYCLE_BIN.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
-    "FROM "
-      "TAPE_FILE_RECYCLE_BIN "
-    "WHERE "
-      "TAPE_FILE_RECYCLE_BIN.VID = :VID";
-
-    auto selectFileStmt = conn.createStmt(selectArchiveFileIdSql);
-    selectFileStmt.bindString(":VID",vid);
-    auto rset = selectFileStmt.executeQuery();
-    std::set<uint64_t> archiveFileIds;
-    while(rset.next()){
-      archiveFileIds.insert(rset.columnUint64("ARCHIVE_FILE_ID"));
-    }
-    rdbms::AutoRollback rollback(conn);
-    for(auto archiveFileId: archiveFileIds){
-      deleteTapeFilesAndArchiveFileFromRecycleBin(conn,archiveFileId,lc);
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteFilesFromRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) {
-  try {
-    auto conn = m_connPool.getConn();
-    deleteFilesFromRecycleLog(conn,vid,lc);
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteFilesFromRecycleLog
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteFilesFromRecycleLog(rdbms::Conn & conn, const std::string& vid, log::LogContext& lc) {
-  try {
-    const char *const deleteFilesFromRecycleLogSql =
-    "DELETE FROM "
-      "FILE_RECYCLE_LOG "
-    "WHERE "
-      "VID=:VID";
-
-    cta::utils::Timer t;
-    log::TimingList tl;
-    auto selectFileStmt = conn.createStmt(deleteFilesFromRecycleLogSql);
-    selectFileStmt.bindString(":VID",vid);
-    selectFileStmt.executeNonQuery();
-    uint64_t nbAffectedRows = selectFileStmt.getNbAffectedRows();
-    if(nbAffectedRows){
-      tl.insertAndReset("deleteFilesFromRecycleLogTime",t);
-      log::ScopedParamContainer spc(lc);
-      spc.add("vid",vid);
-      spc.add("nbFileRecycleLogDeleted",nbAffectedRows);
-      tl.addToLog(spc);
-      lc.log(cta::log::INFO,"In RdbmsCatalogue::deleteFilesFromRecycleLog(), file recycle log entries have been deleted.");
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) {
-  try {
-    auto conn = m_connPool.getConn();
-    const char *const sql =
-    "INSERT INTO DRIVE_STATE("        "\n"
-      "DRIVE_NAME,"                   "\n"
-      "HOST,"                         "\n"
-      "LOGICAL_LIBRARY,"              "\n"
-      "SESSION_ID,"                   "\n"
-
-      "BYTES_TRANSFERED_IN_SESSION,"  "\n"
-      "FILES_TRANSFERED_IN_SESSION,"  "\n"
-
-      "SESSION_START_TIME,"           "\n"
-      "SESSION_ELAPSED_TIME,"         "\n"
-      "MOUNT_START_TIME,"             "\n"
-      "TRANSFER_START_TIME,"          "\n"
-      "UNLOAD_START_TIME,"            "\n"
-      "UNMOUNT_START_TIME,"           "\n"
-      "DRAINING_START_TIME,"          "\n"
-      "DOWN_OR_UP_START_TIME,"        "\n"
-      "PROBE_START_TIME,"             "\n"
-      "CLEANUP_START_TIME,"           "\n"
-      "START_START_TIME,"             "\n"
-      "SHUTDOWN_TIME,"                "\n"
-
-      "MOUNT_TYPE,"                   "\n"
-      "DRIVE_STATUS,"                 "\n"
-      "DESIRED_UP,"                   "\n"
-      "DESIRED_FORCE_DOWN,"           "\n"
-      "REASON_UP_DOWN,"               "\n"
-
-      "CURRENT_VID,"                  "\n"
-      "CTA_VERSION,"                  "\n"
-      "CURRENT_PRIORITY,"             "\n"
-      "CURRENT_ACTIVITY,"             "\n"
-      "CURRENT_TAPE_POOL,"            "\n"
-      "NEXT_MOUNT_TYPE,"              "\n"
-      "NEXT_VID,"                     "\n"
-      "NEXT_TAPE_POOL,"               "\n"
-      "NEXT_PRIORITY,"                "\n"
-      "NEXT_ACTIVITY,"                "\n"
-
-      "DEV_FILE_NAME,"                "\n"
-      "RAW_LIBRARY_SLOT,"             "\n"
-
-      "CURRENT_VO,"                   "\n"
-      "NEXT_VO,"                      "\n"
-      "USER_COMMENT,"                 "\n"
-
-      "CREATION_LOG_USER_NAME,"       "\n"
-      "CREATION_LOG_HOST_NAME,"       "\n"
-      "CREATION_LOG_TIME,"            "\n"
-      "LAST_UPDATE_USER_NAME,"        "\n"
-      "LAST_UPDATE_HOST_NAME,"        "\n"
-      "LAST_UPDATE_TIME,"             "\n"
-
-      "DISK_SYSTEM_NAME,"             "\n"
-      "RESERVED_BYTES,"               "\n"
-      "RESERVATION_SESSION_ID)"       "\n"
-    "VALUES("                         "\n"
-      ":DRIVE_NAME,"                  "\n"
-      ":HOST,"                        "\n"
-      ":LOGICAL_LIBRARY,"             "\n"
-      ":SESSION_ID,"                  "\n"
-
-      ":BYTES_TRANSFERED_IN_SESSION," "\n"
-      ":FILES_TRANSFERED_IN_SESSION," "\n"
-
-      ":SESSION_START_TIME,"          "\n"
-      ":SESSION_ELAPSED_TIME,"        "\n"
-      ":MOUNT_START_TIME,"            "\n"
-      ":TRANSFER_START_TIME,"         "\n"
-      ":UNLOAD_START_TIME,"           "\n"
-      ":UNMOUNT_START_TIME,"          "\n"
-      ":DRAINING_START_TIME,"         "\n"
-      ":DOWN_OR_UP_START_TIME,"       "\n"
-      ":PROBE_START_TIME,"            "\n"
-      ":CLEANUP_START_TIME,"          "\n"
-      ":START_START_TIME,"            "\n"
-      ":SHUTDOWN_TIME,"               "\n"
-
-      ":MOUNT_TYPE,"                  "\n"
-      ":DRIVE_STATUS,"                "\n"
-      ":DESIRED_UP,"                  "\n"
-      ":DESIRED_FORCE_DOWN,"          "\n"
-      ":REASON_UP_DOWN,"              "\n"
-
-      ":CURRENT_VID,"                 "\n"
-      ":CTA_VERSION,"                 "\n"
-      ":CURRENT_PRIORITY,"            "\n"
-      ":CURRENT_ACTIVITY,"            "\n"
-      ":CURRENT_TAPE_POOL,"           "\n"
-      ":NEXT_MOUNT_TYPE,"             "\n"
-      ":NEXT_VID,"                    "\n"
-      ":NEXT_TAPE_POOL,"              "\n"
-      ":NEXT_PRIORITY,"               "\n"
-      ":NEXT_ACTIVITY,"               "\n"
-
-      ":DEV_FILE_NAME,"               "\n"
-      ":RAW_LIBRARY_SLOT,"            "\n"
-
-      ":CURRENT_VO,"                  "\n"
-      ":NEXT_VO,"                     "\n"
-      ":USER_COMMENT,"                "\n"
-
-      ":CREATION_LOG_USER_NAME,"      "\n"
-      ":CREATION_LOG_HOST_NAME,"      "\n"
-      ":CREATION_LOG_TIME,"           "\n"
-      ":LAST_UPDATE_USER_NAME,"       "\n"
-      ":LAST_UPDATE_HOST_NAME,"       "\n"
-      ":LAST_UPDATE_TIME,"            "\n"
-
-      ":DISK_SYSTEM_NAME,"            "\n"
-      ":RESERVED_BYTES,"              "\n"
-      ":RESERVATION_SESSION_ID"       "\n"
-    ")";
-
-    auto stmt = conn.createStmt(sql);
-
-    settingSqlTapeDriveValues(&stmt, tapeDrive);
-
-    stmt.executeNonQuery();
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("driveName", tapeDrive.driveName)
-      .add("host", tapeDrive.host)
-      .add("logicalLibrary", tapeDrive.logicalLibrary)
-      .add("sessionId", tapeDrive.sessionId ? tapeDrive.sessionId.value() : 0)
-
-      .add("bytesTransferedInSession", tapeDrive.bytesTransferedInSession
-        ? tapeDrive.bytesTransferedInSession.value() : 0)
-      .add("filesTransferedInSession", tapeDrive.filesTransferedInSession
-        ? tapeDrive.filesTransferedInSession.value() : 0)
-
-      .add("sessionStartTime", tapeDrive.sessionStartTime ? tapeDrive.sessionStartTime.value() : 0)
-      .add("sessionElapsedTime", tapeDrive.sessionElapsedTime ? tapeDrive.sessionElapsedTime.value() : 0)
-      .add("mountStartTime", tapeDrive.mountStartTime ? tapeDrive.mountStartTime.value() : 0)
-      .add("transferStartTime", tapeDrive.transferStartTime
-        ? tapeDrive.transferStartTime.value() : 0)
-      .add("unloadStartTime", tapeDrive.unloadStartTime ? tapeDrive.unloadStartTime.value() : 0)
-      .add("unmountStartTime", tapeDrive.unmountStartTime ? tapeDrive.unmountStartTime.value() : 0)
-      .add("drainingStartTime", tapeDrive.drainingStartTime
-        ? tapeDrive.drainingStartTime.value() : 0)
-      .add("downOrUpStartTime", tapeDrive.downOrUpStartTime
-        ? tapeDrive.downOrUpStartTime.value() : 0)
-      .add("probeStartTime", tapeDrive.probeStartTime ? tapeDrive.probeStartTime.value() : 0)
-      .add("cleanupStartTime", tapeDrive.cleanupStartTime ? tapeDrive.cleanupStartTime.value() : 0)
-      .add("startStartTime", tapeDrive.startStartTime ? tapeDrive.startStartTime.value() : 0)
-      .add("shutdownTime", tapeDrive.shutdownTime ? tapeDrive.shutdownTime.value() : 0)
-
-      .add("mountType", common::dataStructures::toString(tapeDrive.mountType))
-      .add("driveStatus", common::dataStructures::TapeDrive::stateToString(tapeDrive.driveStatus))
-
-      .add("desiredUp", tapeDrive.desiredUp ? 1 : 0)
-      .add("desiredForceDown", tapeDrive.desiredForceDown ? 1 : 0)
-      .add("reasonUpDown", tapeDrive.reasonUpDown ? tapeDrive.reasonUpDown.value() : "")
-
-      .add("currentVo", tapeDrive.currentVo ? tapeDrive.currentVo.value() : "")
-      .add("nextVo", tapeDrive.nextVo ? tapeDrive.nextVo.value() : "")
-      .add("userComment", tapeDrive.userComment ? tapeDrive.userComment.value() : "")
-
-      .add("creationLog_username", tapeDrive.creationLog
-        ? tapeDrive.creationLog.value().username : "")
-      .add("creationLog_host", tapeDrive.creationLog ? tapeDrive.creationLog.value().host : "")
-      .add("creationLog_time", tapeDrive.creationLog ? tapeDrive.creationLog.value().time : 0)
-      .add("lastModificationLog_username", tapeDrive.lastModificationLog
-        ? tapeDrive.lastModificationLog.value().username : "")
-      .add("lastModificationLog_username", tapeDrive.lastModificationLog
-        ? tapeDrive.lastModificationLog.value().host : "")
-      .add("lastModificationLog_username", tapeDrive.lastModificationLog
-        ? tapeDrive.lastModificationLog.value().time : 0)
-
-      .add("diskSystemName", tapeDrive.diskSystemName ? tapeDrive.diskSystemName.value() : "")
-      .add("reservedBytes", tapeDrive.reservedBytes ? tapeDrive.reservedBytes.value() : 0)
-      .add("reservationSessionId", tapeDrive.reservationSessionId ? tapeDrive.reservationSessionId.value() : 0);
-
-    lc.log(log::INFO, "Catalogue - created tape drive");
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::settingSqlTapeDriveValues(cta::rdbms::Stmt *stmt,
-  const common::dataStructures::TapeDrive &tapeDrive) const {
-  auto setOptionalString = [&stmt](const std::string& sqlField, const std::optional<std::string>& optionalField) {
-    if(optionalField && !optionalField.value().empty()) {
-      stmt->bindString(sqlField, optionalField.value());
-    } else {
-      stmt->bindString(sqlField, std::nullopt);
-    }
-  };
-  auto setOptionalTime = [&stmt](const std::string &sqlField, const std::optional<time_t>& optionalField) {
-    if(optionalField) {
-      stmt->bindUint64(sqlField, optionalField.value());
-    } else {
-      stmt->bindUint64(sqlField, std::nullopt);
-    }
-  };
-
-  stmt->bindString(":DRIVE_NAME", tapeDrive.driveName);
-  stmt->bindString(":HOST", tapeDrive.host);
-  stmt->bindString(":LOGICAL_LIBRARY", tapeDrive.logicalLibrary);
-  stmt->bindUint64(":SESSION_ID", tapeDrive.sessionId);
-
-  stmt->bindUint64(":BYTES_TRANSFERED_IN_SESSION", tapeDrive.bytesTransferedInSession);
-  stmt->bindUint64(":FILES_TRANSFERED_IN_SESSION", tapeDrive.filesTransferedInSession);
-
-  setOptionalTime(":SESSION_START_TIME", tapeDrive.sessionStartTime);
-  setOptionalTime(":SESSION_ELAPSED_TIME", tapeDrive.sessionElapsedTime);
-  setOptionalTime(":MOUNT_START_TIME", tapeDrive.mountStartTime);
-  setOptionalTime(":TRANSFER_START_TIME", tapeDrive.transferStartTime);
-  setOptionalTime(":UNLOAD_START_TIME", tapeDrive.unloadStartTime);
-  setOptionalTime(":UNMOUNT_START_TIME", tapeDrive.unmountStartTime);
-  setOptionalTime(":DRAINING_START_TIME", tapeDrive.drainingStartTime);
-  setOptionalTime(":DOWN_OR_UP_START_TIME", tapeDrive.downOrUpStartTime);
-  setOptionalTime(":PROBE_START_TIME", tapeDrive.probeStartTime);
-  setOptionalTime(":CLEANUP_START_TIME", tapeDrive.cleanupStartTime);
-  setOptionalTime(":START_START_TIME", tapeDrive.startStartTime);
-  setOptionalTime(":SHUTDOWN_TIME", tapeDrive.shutdownTime);
-
-  stmt->bindString(":MOUNT_TYPE", common::dataStructures::toString(tapeDrive.mountType));
-  stmt->bindString(":DRIVE_STATUS", common::dataStructures::TapeDrive::stateToString(tapeDrive.driveStatus));
-
-  stmt->bindBool(":DESIRED_UP", tapeDrive.desiredUp);
-  stmt->bindBool(":DESIRED_FORCE_DOWN", tapeDrive.desiredForceDown);
-  setOptionalString(":REASON_UP_DOWN", tapeDrive.reasonUpDown);
-
-  setOptionalString(":CURRENT_VID", tapeDrive.currentVid);
-  setOptionalString(":CTA_VERSION", tapeDrive.ctaVersion);
-  stmt->bindUint64(":CURRENT_PRIORITY", tapeDrive.currentPriority);
-  setOptionalString(":CURRENT_ACTIVITY", tapeDrive.currentActivity);
-  setOptionalString(":CURRENT_TAPE_POOL", tapeDrive.currentTapePool);
-  stmt->bindString(":NEXT_MOUNT_TYPE", common::dataStructures::toString(tapeDrive.nextMountType));
-  setOptionalString(":NEXT_VID", tapeDrive.nextVid);
-  setOptionalString(":NEXT_TAPE_POOL", tapeDrive.nextTapePool);
-  stmt->bindUint64(":NEXT_PRIORITY", tapeDrive.nextPriority);
-  setOptionalString(":NEXT_ACTIVITY", tapeDrive.nextActivity);
-
-  setOptionalString(":DEV_FILE_NAME", tapeDrive.devFileName);
-  setOptionalString(":RAW_LIBRARY_SLOT", tapeDrive.rawLibrarySlot);
-
-  setOptionalString(":CURRENT_VO", tapeDrive.currentVo);
-  setOptionalString(":NEXT_VO", tapeDrive.nextVo);
-  setOptionalString(":USER_COMMENT", tapeDrive.userComment);
-
-  auto setEntryLog = [stmt, setOptionalString, setOptionalTime](const std::string &field,
-    const std::optional<std::string> &username, const std::optional<std::string> &host, const std::optional<time_t> &time) {
-      setOptionalString(field + "_USER_NAME", username);
-      setOptionalString(field + "_HOST_NAME", host);
-      setOptionalTime(field + "_TIME", time);
-  };
-
-  if (tapeDrive.creationLog) {
-    setEntryLog(":CREATION_LOG", tapeDrive.creationLog.value().username,
-      tapeDrive.creationLog.value().host, tapeDrive.creationLog.value().time);
-  } else {
-    setEntryLog(":CREATION_LOG", std::nullopt, std::nullopt, std::nullopt);
-  }
-
-  if (tapeDrive.lastModificationLog) {
-    setEntryLog(":LAST_UPDATE", tapeDrive.lastModificationLog.value().username,
-      tapeDrive.lastModificationLog.value().host, tapeDrive.lastModificationLog.value().time);
-  } else {
-    setEntryLog(":LAST_UPDATE", std::nullopt, std::nullopt, std::nullopt);
-  }
-
-  setOptionalString(":DISK_SYSTEM_NAME", tapeDrive.diskSystemName);
-  stmt->bindUint64(":RESERVED_BYTES", tapeDrive.reservedBytes);
-  stmt->bindUint64(":RESERVATION_SESSION_ID", tapeDrive.reservationSessionId);
-
-}
-
-void RdbmsCatalogue::deleteTapeDrive(const std::string &tapeDriveName) {
-  try {
-    const char *const delete_sql =
-      "DELETE FROM "
-        "DRIVE_STATE "
-      "WHERE "
-        "DRIVE_NAME = :DELETE_DRIVE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(delete_sql);
-    stmt.bindString(":DELETE_DRIVE_NAME", tapeDriveName);
-    stmt.executeNonQuery();
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::list<std::string> RdbmsCatalogue::getTapeDriveNames() const {
-  try {
-    std::list<std::string> tapeDriveNames;
-    const char *const sql =
-      "SELECT "
-        "DRIVE_NAME AS DRIVE_NAME "
-      "FROM "
-        "DRIVE_STATE ";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-
-    while (rset.next()) {
-      const std::string driveName = rset.columnString("DRIVE_NAME");
-      tapeDriveNames.push_back(driveName);
-    }
-    return tapeDriveNames;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-common::dataStructures::TapeDrive RdbmsCatalogue::gettingSqlTapeDriveValues(cta::rdbms::Rset* rset) const {
-  common::dataStructures::TapeDrive tapeDrive;
-  auto getOptionalTime = [](const std::optional<uint64_t> &time) -> std::optional<time_t> {
-    if (!time) return std::nullopt;
-    return time.value();
-  };
-  tapeDrive.driveName = rset->columnString("DRIVE_NAME");
-  tapeDrive.host = rset->columnString("HOST");
-  tapeDrive.logicalLibrary = rset->columnString("LOGICAL_LIBRARY");
-  tapeDrive.sessionId = rset->columnOptionalUint64("SESSION_ID");
-  tapeDrive.logicalLibraryDisabled = rset->columnOptionalBool("IS_DISABLED") ? rset->columnOptionalBool("IS_DISABLED").value() : false;
-
-  tapeDrive.bytesTransferedInSession = rset->columnOptionalUint64("BYTES_TRANSFERED_IN_SESSION");
-  tapeDrive.filesTransferedInSession = rset->columnOptionalUint64("FILES_TRANSFERED_IN_SESSION");
-
-  tapeDrive.sessionStartTime = getOptionalTime(rset->columnOptionalUint64("SESSION_START_TIME"));
-  tapeDrive.sessionElapsedTime = getOptionalTime(rset->columnOptionalUint64("SESSION_ELAPSED_TIME"));
-  tapeDrive.mountStartTime = getOptionalTime(rset->columnOptionalUint64("MOUNT_START_TIME"));
-  tapeDrive.transferStartTime = getOptionalTime(rset->columnOptionalUint64("TRANSFER_START_TIME"));
-  tapeDrive.unloadStartTime = getOptionalTime(rset->columnOptionalUint64("UNLOAD_START_TIME"));
-  tapeDrive.unmountStartTime = getOptionalTime(rset->columnOptionalUint64("UNMOUNT_START_TIME"));
-  tapeDrive.drainingStartTime = getOptionalTime(rset->columnOptionalUint64("DRAINING_START_TIME"));
-  tapeDrive.downOrUpStartTime = getOptionalTime(rset->columnOptionalUint64("DOWN_OR_UP_START_TIME"));
-  tapeDrive.probeStartTime = getOptionalTime(rset->columnOptionalUint64("PROBE_START_TIME"));
-  tapeDrive.cleanupStartTime = getOptionalTime(rset->columnOptionalUint64("CLEANUP_START_TIME"));
-  tapeDrive.startStartTime = getOptionalTime(rset->columnOptionalUint64("START_START_TIME"));
-  tapeDrive.shutdownTime = getOptionalTime(rset->columnOptionalUint64("SHUTDOWN_TIME"));
-
-  tapeDrive.mountType = common::dataStructures::strToMountType(rset->columnString("MOUNT_TYPE"));
-  tapeDrive.driveStatus = common::dataStructures::TapeDrive::stringToState(rset->columnString("DRIVE_STATUS"));
-  tapeDrive.desiredUp = rset->columnBool("DESIRED_UP");
-  tapeDrive.desiredForceDown = rset->columnBool("DESIRED_FORCE_DOWN");
-  tapeDrive.reasonUpDown = rset->columnOptionalString("REASON_UP_DOWN");
-
-  tapeDrive.currentVid = rset->columnOptionalString("CURRENT_VID");
-  tapeDrive.ctaVersion = rset->columnOptionalString("CTA_VERSION");
-  tapeDrive.currentPriority = rset->columnOptionalUint64("CURRENT_PRIORITY");
-  tapeDrive.currentActivity = rset->columnOptionalString("CURRENT_ACTIVITY");
-  tapeDrive.currentTapePool = rset->columnOptionalString("CURRENT_TAPE_POOL");
-  tapeDrive.nextMountType = common::dataStructures::strToMountType(rset->columnString("NEXT_MOUNT_TYPE"));
-  tapeDrive.nextVid = rset->columnOptionalString("NEXT_VID");
-  tapeDrive.nextTapePool = rset->columnOptionalString("NEXT_TAPE_POOL");
-  tapeDrive.nextPriority = rset->columnOptionalUint64("NEXT_PRIORITY");
-  tapeDrive.nextActivity = rset->columnOptionalString("NEXT_ACTIVITY");
-
-  tapeDrive.devFileName = rset->columnOptionalString("DEV_FILE_NAME");
-  tapeDrive.rawLibrarySlot = rset->columnOptionalString("RAW_LIBRARY_SLOT");
-
-  tapeDrive.currentVo = rset->columnOptionalString("CURRENT_VO");
-  tapeDrive.nextVo = rset->columnOptionalString("NEXT_VO");
-
-  tapeDrive.diskSystemName = rset->columnOptionalString("DISK_SYSTEM_NAME");
-  tapeDrive.reservedBytes = rset->columnOptionalUint64("RESERVED_BYTES");
-  tapeDrive.reservationSessionId = rset->columnOptionalUint64("RESERVATION_SESSION_ID");
-
-  tapeDrive.userComment = rset->columnOptionalString("USER_COMMENT");
-  auto setOptionEntryLog = [&rset](const std::string &username, const std::string &host,
-    const std::string &time) -> std::optional<common::dataStructures::EntryLog> {
-    if (!rset->columnOptionalString(username)) {
-      return std::nullopt;
-    } else {
-      return common::dataStructures::EntryLog(
-        rset->columnString(username),
-        rset->columnString(host),
-        rset->columnUint64(time));
-    }
-  };
-  tapeDrive.creationLog = setOptionEntryLog("CREATION_LOG_USER_NAME", "CREATION_LOG_HOST_NAME",
-    "CREATION_LOG_TIME");
-  tapeDrive.lastModificationLog = setOptionEntryLog("LAST_UPDATE_USER_NAME", "LAST_UPDATE_HOST_NAME",
-    "LAST_UPDATE_TIME");
-
-  // Log warning for operators that tape drive logical library does not exist (cta/CTA#1163)
-  if (!rset->columnOptionalBool("IS_DISABLED")) {
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("driveName", tapeDrive.driveName)
-       .add("logicalLibrary", tapeDrive.logicalLibrary);
-    lc.log(log::WARNING, "RdbmsCatalogue::gettingSqlTapeDriveValues(): Logical library for tape drive does not exist in the catalogue");
-  }
-  return tapeDrive;
-}
-
-std::list<common::dataStructures::TapeDrive> RdbmsCatalogue::getTapeDrives() const {
-  try {
-    std::list<common::dataStructures::TapeDrive> tapeDrives;
-    const char *const sql =
-      "SELECT "
-        "DRIVE_STATE.DRIVE_NAME AS DRIVE_NAME,"
-        "DRIVE_STATE.HOST AS HOST,"
-        "DRIVE_STATE.LOGICAL_LIBRARY AS LOGICAL_LIBRARY,"
-        "DRIVE_STATE.SESSION_ID AS SESSION_ID,"
-
-        "DRIVE_STATE.BYTES_TRANSFERED_IN_SESSION AS BYTES_TRANSFERED_IN_SESSION,"
-        "DRIVE_STATE.FILES_TRANSFERED_IN_SESSION AS FILES_TRANSFERED_IN_SESSION,"
-
-        "DRIVE_STATE.SESSION_START_TIME AS SESSION_START_TIME,"
-        "DRIVE_STATE.SESSION_ELAPSED_TIME AS SESSION_ELAPSED_TIME,"
-        "DRIVE_STATE.MOUNT_START_TIME AS MOUNT_START_TIME,"
-        "DRIVE_STATE.TRANSFER_START_TIME AS TRANSFER_START_TIME,"
-        "DRIVE_STATE.UNLOAD_START_TIME AS UNLOAD_START_TIME,"
-        "DRIVE_STATE.UNMOUNT_START_TIME AS UNMOUNT_START_TIME,"
-        "DRIVE_STATE.DRAINING_START_TIME AS DRAINING_START_TIME,"
-        "DRIVE_STATE.DOWN_OR_UP_START_TIME AS DOWN_OR_UP_START_TIME,"
-        "DRIVE_STATE.PROBE_START_TIME AS PROBE_START_TIME,"
-        "DRIVE_STATE.CLEANUP_START_TIME AS CLEANUP_START_TIME,"
-        "DRIVE_STATE.START_START_TIME AS START_START_TIME,"
-        "DRIVE_STATE.SHUTDOWN_TIME AS SHUTDOWN_TIME,"
-
-        "DRIVE_STATE.MOUNT_TYPE AS MOUNT_TYPE,"
-        "DRIVE_STATE.DRIVE_STATUS AS DRIVE_STATUS,"
-        "DRIVE_STATE.DESIRED_UP AS DESIRED_UP,"
-        "DRIVE_STATE.DESIRED_FORCE_DOWN AS DESIRED_FORCE_DOWN,"
-        "DRIVE_STATE.REASON_UP_DOWN AS REASON_UP_DOWN,"
-
-        "DRIVE_STATE.CURRENT_VID AS CURRENT_VID,"
-        "DRIVE_STATE.CTA_VERSION AS CTA_VERSION,"
-        "DRIVE_STATE.CURRENT_PRIORITY AS CURRENT_PRIORITY,"
-        "DRIVE_STATE.CURRENT_ACTIVITY AS CURRENT_ACTIVITY,"
-        "DRIVE_STATE.CURRENT_TAPE_POOL AS CURRENT_TAPE_POOL,"
-        "DRIVE_STATE.NEXT_MOUNT_TYPE AS NEXT_MOUNT_TYPE,"
-        "DRIVE_STATE.NEXT_VID AS NEXT_VID,"
-        "DRIVE_STATE.NEXT_TAPE_POOL AS NEXT_TAPE_POOL,"
-        "DRIVE_STATE.NEXT_PRIORITY AS NEXT_PRIORITY,"
-        "DRIVE_STATE.NEXT_ACTIVITY AS NEXT_ACTIVITY,"
-
-        "DRIVE_STATE.DEV_FILE_NAME AS DEV_FILE_NAME,"
-        "DRIVE_STATE.RAW_LIBRARY_SLOT AS RAW_LIBRARY_SLOT,"
-
-        "DRIVE_STATE.CURRENT_VO AS CURRENT_VO,"
-        "DRIVE_STATE.NEXT_VO AS NEXT_VO,"
-        "DRIVE_STATE.USER_COMMENT AS USER_COMMENT,"
-
-        "DRIVE_STATE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "DRIVE_STATE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "DRIVE_STATE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "DRIVE_STATE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "DRIVE_STATE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "DRIVE_STATE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME,"
-
-        "DRIVE_STATE.DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME,"
-        "DRIVE_STATE.RESERVED_BYTES AS RESERVED_BYTES,"
-        "DRIVE_STATE.RESERVATION_SESSION_ID AS RESERVATION_SESSION_ID,"
-        "LOGICAL_LIBRARY.IS_DISABLED AS IS_DISABLED "
-      "FROM "
-        "DRIVE_STATE "
-      "LEFT JOIN "
-        "LOGICAL_LIBRARY "
-      "ON "
-        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = DRIVE_STATE.LOGICAL_LIBRARY "
-      "ORDER BY "
-        "DRIVE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-
-    while (rset.next()) {
-      auto tapeDrive = gettingSqlTapeDriveValues(&rset);
-      tapeDrives.push_back(tapeDrive);
-    }
-    return tapeDrives;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::optional<common::dataStructures::TapeDrive> RdbmsCatalogue::getTapeDrive(const std::string &tapeDriveName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DRIVE_STATE.DRIVE_NAME AS DRIVE_NAME,"
-        "DRIVE_STATE.HOST AS HOST,"
-        "DRIVE_STATE.LOGICAL_LIBRARY AS LOGICAL_LIBRARY,"
-        "DRIVE_STATE.SESSION_ID AS SESSION_ID,"
-
-        "DRIVE_STATE.BYTES_TRANSFERED_IN_SESSION AS BYTES_TRANSFERED_IN_SESSION,"
-        "DRIVE_STATE.FILES_TRANSFERED_IN_SESSION AS FILES_TRANSFERED_IN_SESSION,"
-
-        "DRIVE_STATE.SESSION_START_TIME AS SESSION_START_TIME,"
-        "DRIVE_STATE.SESSION_ELAPSED_TIME AS SESSION_ELAPSED_TIME,"
-        "DRIVE_STATE.MOUNT_START_TIME AS MOUNT_START_TIME,"
-        "DRIVE_STATE.TRANSFER_START_TIME AS TRANSFER_START_TIME,"
-        "DRIVE_STATE.UNLOAD_START_TIME AS UNLOAD_START_TIME,"
-        "DRIVE_STATE.UNMOUNT_START_TIME AS UNMOUNT_START_TIME,"
-        "DRIVE_STATE.DRAINING_START_TIME AS DRAINING_START_TIME,"
-        "DRIVE_STATE.DOWN_OR_UP_START_TIME AS DOWN_OR_UP_START_TIME,"
-        "DRIVE_STATE.PROBE_START_TIME AS PROBE_START_TIME,"
-        "DRIVE_STATE.CLEANUP_START_TIME AS CLEANUP_START_TIME,"
-        "DRIVE_STATE.START_START_TIME AS START_START_TIME,"
-        "DRIVE_STATE.SHUTDOWN_TIME AS SHUTDOWN_TIME,"
-
-        "DRIVE_STATE.MOUNT_TYPE AS MOUNT_TYPE,"
-        "DRIVE_STATE.DRIVE_STATUS AS DRIVE_STATUS,"
-        "DRIVE_STATE.DESIRED_UP AS DESIRED_UP,"
-        "DRIVE_STATE.DESIRED_FORCE_DOWN AS DESIRED_FORCE_DOWN,"
-        "DRIVE_STATE.REASON_UP_DOWN AS REASON_UP_DOWN,"
-
-        "DRIVE_STATE.CURRENT_VID AS CURRENT_VID,"
-        "DRIVE_STATE.CTA_VERSION AS CTA_VERSION,"
-        "DRIVE_STATE.CURRENT_PRIORITY AS CURRENT_PRIORITY,"
-        "DRIVE_STATE.CURRENT_ACTIVITY AS CURRENT_ACTIVITY,"
-        "DRIVE_STATE.CURRENT_TAPE_POOL AS CURRENT_TAPE_POOL,"
-        "DRIVE_STATE.NEXT_MOUNT_TYPE AS NEXT_MOUNT_TYPE,"
-        "DRIVE_STATE.NEXT_VID AS NEXT_VID,"
-        "DRIVE_STATE.NEXT_TAPE_POOL AS NEXT_TAPE_POOL,"
-        "DRIVE_STATE.NEXT_PRIORITY AS NEXT_PRIORITY,"
-        "DRIVE_STATE.NEXT_ACTIVITY AS NEXT_ACTIVITY,"
-
-        "DRIVE_STATE.DEV_FILE_NAME AS DEV_FILE_NAME,"
-        "DRIVE_STATE.RAW_LIBRARY_SLOT AS RAW_LIBRARY_SLOT,"
-
-        "DRIVE_STATE.CURRENT_VO AS CURRENT_VO,"
-        "DRIVE_STATE.NEXT_VO AS NEXT_VO,"
-        "DRIVE_STATE.USER_COMMENT AS USER_COMMENT,"
-
-        "DRIVE_STATE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
-        "DRIVE_STATE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
-        "DRIVE_STATE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
-        "DRIVE_STATE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
-        "DRIVE_STATE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
-        "DRIVE_STATE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME,"
-
-        "DRIVE_STATE.DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME,"
-        "DRIVE_STATE.RESERVED_BYTES AS RESERVED_BYTES,"
-        "DRIVE_STATE.RESERVATION_SESSION_ID as RESERVATION_SESSION_ID,"
-        "LOGICAL_LIBRARY.IS_DISABLED AS IS_DISABLED "
-
-      "FROM "
-        "DRIVE_STATE "
-      "LEFT JOIN "
-        "LOGICAL_LIBRARY "
-      "ON "
-        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = DRIVE_STATE.LOGICAL_LIBRARY "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    auto rset = stmt.executeQuery();
-
-    if (rset.next()) {
-      return gettingSqlTapeDriveValues(&rset);;
-    }
-    return std::nullopt;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::setDesiredTapeDriveState(const std::string& tapeDriveName,
-  const common::dataStructures::DesiredDriveState &desiredState) {
-  try {
-    checkCommentOrReasonMaxLength(desiredState.reason);
-    std::string sql =
-      "UPDATE DRIVE_STATE SET "
-        "DESIRED_UP = :DESIRED_UP,"
-        "DESIRED_FORCE_DOWN = :DESIRED_FORCE_DOWN,";
-    if(desiredState.reason) {
-      sql += "REASON_UP_DOWN = ";
-      sql += desiredState.reason.value().empty() ? "''," : ":REASON_UP_DOWN,";
-    }
-
-    // Remove last ',' character
-    sql.erase(sql.find_last_of(','), 1);
-    sql += " WHERE "
-      "DRIVE_NAME = :DRIVE_NAME";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql.c_str());
-
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    stmt.bindBool(":DESIRED_UP", desiredState.up);
-    stmt.bindBool(":DESIRED_FORCE_DOWN", desiredState.forceDown);
-    if(desiredState.reason && !desiredState.reason.value().empty()) {
-      stmt.bindString(":REASON_UP_DOWN", desiredState.reason.value());
-    }
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify Tape Drive: ") + tapeDriveName +
-        " because it doesn't exist");
-    }
-  } catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
-  const std::string &comment) {
-  try {
-    const char* const sql =
-      "UPDATE DRIVE_STATE "
-      "SET "
-        "USER_COMMENT = :USER_COMMENT "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    auto bindOptionalStringIfSet = [&stmt](const std::string& sqlField,
-      const std::optional<std::string>& optionalString) {
-      if (optionalString) {
-        if (optionalString.value().empty()) {
-          stmt.bindString(sqlField, std::nullopt);
-        } else {
-          stmt.bindString(sqlField, optionalString.value());
-        }
-      }
-    };
-
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    bindOptionalStringIfSet(":USER_COMMENT", comment);
-
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot modify Tape Drive: ") + tapeDriveName +
-        " because it doesn't exist");
-    }
-  } catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::updateTapeDriveStatistics(const std::string& tapeDriveName,
-  const std::string& host, const std::string& logicalLibrary,
-  const common::dataStructures::TapeDriveStatistics& statistics) {
-  try {
-    const char *const sql =
-      "UPDATE DRIVE_STATE "
-      "SET "
-        "HOST = :HOST,"
-        "LOGICAL_LIBRARY = :LOGICAL_LIBRARY,"
-        "BYTES_TRANSFERED_IN_SESSION = :BYTES_TRANSFERED_IN_SESSION,"
-        "FILES_TRANSFERED_IN_SESSION = :FILES_TRANSFERED_IN_SESSION,"
-        "SESSION_ELAPSED_TIME = :REPORT_TIME-SESSION_START_TIME,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME AND DRIVE_STATUS = 'TRANSFERING'";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    stmt.bindString(":HOST", host);
-    stmt.bindString(":LOGICAL_LIBRARY", logicalLibrary);
-    stmt.bindUint64(":BYTES_TRANSFERED_IN_SESSION", statistics.bytesTransferedInSession);
-    stmt.bindUint64(":FILES_TRANSFERED_IN_SESSION", statistics.filesTransferedInSession);
-    stmt.bindUint64(":REPORT_TIME", statistics.reportTime);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", statistics.lastModificationLog.username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", statistics.lastModificationLog.host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", statistics.lastModificationLog.time);
-
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      log::LogContext lc(m_log);
-      lc.log(log::DEBUG, "RdbmsCatalogue::updateTapeDriveStatistics(): It didn't update statistics");
-    }
-  } catch (exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) {
-  try {
-    const std::string driveStatusStr = common::dataStructures::TapeDrive::stateToString(tapeDrive.driveStatus);
-
-    // Case 1 : Drive status stays the same
-    std::string sql =
-      "UPDATE DRIVE_STATE SET "
-        "HOST = :HOST,"
-        "LOGICAL_LIBRARY = :LOGICAL_LIBRARY,";
-
-    if(tapeDrive.driveStatus == common::dataStructures::DriveStatus::Transferring) {
-      sql +=
-        "BYTES_TRANSFERED_IN_SESSION = :BYTES_TRANSFERED_IN_SESSION,"
-        "FILES_TRANSFERED_IN_SESSION = :FILES_TRANSFERED_IN_SESSION,"
-        "SESSION_ELAPSED_TIME = CASE WHEN SESSION_START_TIME IS NULL THEN 0 ELSE :REPORT_TIME - SESSION_START_TIME END,";
-    }
-    sql +=
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME AND DRIVE_STATUS = :DRIVE_STATUS";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql.c_str());
-
-    stmt.bindString(":DRIVE_NAME", tapeDrive.driveName);
-    stmt.bindString(":HOST", tapeDrive.host);
-    stmt.bindString(":LOGICAL_LIBRARY", tapeDrive.logicalLibrary);
-    stmt.bindString(":DRIVE_STATUS", driveStatusStr);
-    stmt.bindString(":LAST_UPDATE_USER_NAME", tapeDrive.lastModificationLog.value().username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", tapeDrive.lastModificationLog.value().host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", tapeDrive.lastModificationLog.value().time);
-
-    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Transferring) {
-      stmt.bindUint64(":BYTES_TRANSFERED_IN_SESSION", tapeDrive.bytesTransferedInSession.value());
-      stmt.bindUint64(":FILES_TRANSFERED_IN_SESSION", tapeDrive.filesTransferedInSession.value());
-      stmt.bindUint64(":REPORT_TIME", tapeDrive.transferStartTime.value());
-    }
-    stmt.executeNonQuery();
-
-    // If the update succeeded, we are done. Otherwise proceed to Case 2.
-    if(stmt.getNbAffectedRows() > 0) return;
-
-    // Case 2 : Drive status is changing
-    sql =
-      "UPDATE DRIVE_STATE SET "
-        "HOST = :HOST,"
-        "LOGICAL_LIBRARY = :LOGICAL_LIBRARY,"
-        "SESSION_ID = :SESSION_ID,"
-        "BYTES_TRANSFERED_IN_SESSION = :BYTES_TRANSFERED_IN_SESSION,"
-        "FILES_TRANSFERED_IN_SESSION = :FILES_TRANSFERED_IN_SESSION,"
-        "TRANSFER_START_TIME = :TRANSFER_START_TIME,"
-        "SESSION_ELAPSED_TIME = :SESSION_ELAPSED_TIME,"
-        "UNLOAD_START_TIME = :UNLOAD_START_TIME,"
-        "UNMOUNT_START_TIME = :UNMOUNT_START_TIME,"
-        "DRAINING_START_TIME = :DRAINING_START_TIME,"
-        "DOWN_OR_UP_START_TIME = :DOWN_OR_UP_START_TIME,"
-        "PROBE_START_TIME = :PROBE_START_TIME,"
-        "CLEANUP_START_TIME = :CLEANUP_START_TIME,"
-        "SHUTDOWN_TIME = :SHUTDOWN_TIME,"
-        "MOUNT_TYPE = :MOUNT_TYPE,"
-        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
-        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
-        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME,";
-
-    if(tapeDrive.driveStatus != common::dataStructures::DriveStatus::Transferring) {
-      if(tapeDrive.driveStatus != common::dataStructures::DriveStatus::Mounting) {
-        sql += "SESSION_START_TIME = :SESSION_START_TIME,";
-      }
-      sql += "MOUNT_START_TIME = :MOUNT_START_TIME,";
-    }
-    if(tapeDrive.driveStatus == common::dataStructures::DriveStatus::Starting) {
-      sql += "START_START_TIME = :START_START_TIME,";
-    }
-    if(tapeDrive.driveStatus == common::dataStructures::DriveStatus::Down) {
-      sql += "DESIRED_UP = :DESIRED_UP,"
-             "DESIRED_FORCE_DOWN = :DESIRED_FORCE_DOWN,";
-    }
-    // If the drive is a state incompatible with space reservation, make sure there is none:
-    if(tapeDrive.driveStatus == common::dataStructures::DriveStatus::Up) {
-      sql += "DISK_SYSTEM_NAME = NULL,";
-      sql += "RESERVED_BYTES = NULL,";
-      sql += "RESERVATION_SESSION_ID = NULL,";
-      sql += "DRIVE_STATUS = CASE WHEN DESIRED_UP = '0' THEN 'DOWN' ELSE 'UP' END,";
-    } else {
-      sql += "DRIVE_STATUS = '" + driveStatusStr + "',";
-    }
-    if(tapeDrive.reasonUpDown) {
-      sql += "REASON_UP_DOWN = :REASON_UP_DOWN,";
-    }
-    if(tapeDrive.currentVid) {
-      sql += "CURRENT_VID = :CURRENT_VID,";
-    }
-    if(tapeDrive.currentActivity) {
-      sql += "CURRENT_ACTIVITY = :CURRENT_ACTIVITY,";
-    }
-    if(tapeDrive.currentTapePool) {
-      sql += "CURRENT_TAPE_POOL = :CURRENT_TAPE_POOL,";
-    }
-    if(tapeDrive.currentVo) {
-      sql += "CURRENT_VO = :CURRENT_VO,";
-    }
-    if(tapeDrive.userComment) {
-      sql += "USER_COMMENT = :USER_COMMENT,";
-    }
-    // Remove last ',' character
-    sql.erase(sql.find_last_of(','), 1);
-    sql +=
-     " WHERE "
-        "DRIVE_NAME = :DRIVE_NAME";
-
-    stmt.reset();
-    stmt = conn.createStmt(sql.c_str());
-
-    auto setOptionalTime = [&stmt](const std::string& sqlField, const std::optional<time_t>& optionalField) {
-      if(optionalField) {
-        stmt.bindUint64(sqlField, optionalField.value());
-      } else {
-        stmt.bindUint64(sqlField, std::nullopt);
-      }
-    };
-    auto bindOptionalStringIfSet = [&stmt](const std::string& sqlField, const std::optional<std::string>& optionalString) {
-      if(optionalString) {
-        if(optionalString.value().empty()) {
-          stmt.bindString(sqlField, std::nullopt);
-        } else {
-          stmt.bindString(sqlField, optionalString.value());
-        }
-      }
-    };
-
-    stmt.bindString(":HOST", tapeDrive.host);
-    stmt.bindString(":LOGICAL_LIBRARY", tapeDrive.logicalLibrary);
-    stmt.bindUint64(":SESSION_ID", tapeDrive.sessionId);
-    stmt.bindUint64(":BYTES_TRANSFERED_IN_SESSION", tapeDrive.bytesTransferedInSession);
-    stmt.bindUint64(":FILES_TRANSFERED_IN_SESSION", tapeDrive.filesTransferedInSession);
-    setOptionalTime(":TRANSFER_START_TIME", tapeDrive.transferStartTime);
-    setOptionalTime(":SESSION_ELAPSED_TIME", tapeDrive.sessionElapsedTime);
-    setOptionalTime(":UNLOAD_START_TIME", tapeDrive.unloadStartTime);
-    setOptionalTime(":UNMOUNT_START_TIME", tapeDrive.unmountStartTime);
-    setOptionalTime(":DRAINING_START_TIME", tapeDrive.drainingStartTime);
-    setOptionalTime(":DOWN_OR_UP_START_TIME", tapeDrive.downOrUpStartTime);
-    setOptionalTime(":PROBE_START_TIME", tapeDrive.probeStartTime);
-    setOptionalTime(":CLEANUP_START_TIME", tapeDrive.cleanupStartTime);
-    setOptionalTime(":SHUTDOWN_TIME", tapeDrive.shutdownTime);
-    stmt.bindString(":MOUNT_TYPE", toString(tapeDrive.mountType));
-    stmt.bindString(":LAST_UPDATE_USER_NAME", tapeDrive.lastModificationLog.value().username);
-    stmt.bindString(":LAST_UPDATE_HOST_NAME", tapeDrive.lastModificationLog.value().host);
-    stmt.bindUint64(":LAST_UPDATE_TIME", tapeDrive.lastModificationLog.value().time);
-
-    if(tapeDrive.driveStatus != common::dataStructures::DriveStatus::Transferring) {
-      if(tapeDrive.driveStatus != common::dataStructures::DriveStatus::Mounting) {
-        setOptionalTime(":SESSION_START_TIME", tapeDrive.sessionStartTime);
-      }
-      setOptionalTime(":MOUNT_START_TIME", tapeDrive.mountStartTime);
-    }
-    if(tapeDrive.driveStatus == common::dataStructures::DriveStatus::Starting) {
-      setOptionalTime(":START_START_TIME", tapeDrive.startStartTime);
-    }
-    if(tapeDrive.driveStatus == common::dataStructures::DriveStatus::Down) {
-      stmt.bindBool(":DESIRED_UP", tapeDrive.desiredUp);
-      stmt.bindBool(":DESIRED_FORCE_DOWN", tapeDrive.desiredForceDown);
-    }
-    bindOptionalStringIfSet(":REASON_UP_DOWN", tapeDrive.reasonUpDown);
-    bindOptionalStringIfSet(":CURRENT_VID", tapeDrive.currentVid);
-    bindOptionalStringIfSet(":CURRENT_ACTIVITY", tapeDrive.currentActivity);
-    bindOptionalStringIfSet(":CURRENT_TAPE_POOL", tapeDrive.currentTapePool);
-    bindOptionalStringIfSet(":CURRENT_VO", tapeDrive.currentVo);
-    bindOptionalStringIfSet(":USER_COMMENT", tapeDrive.userComment);
-    stmt.bindString(":DRIVE_NAME", tapeDrive.driveName);
-
-    stmt.executeNonQuery();
-
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot update status for drive ") + tapeDrive.driveName + ". Drive not found.");
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
-  const std::string &keyName, const std::string &value, const std::string &source) {
-  try {
-    auto conn = m_connPool.getConn();
-    const char *const sql =
-    "INSERT INTO DRIVE_CONFIG(" "\n"
-      "DRIVE_NAME,"             "\n"
-      "CATEGORY,"               "\n"
-      "KEY_NAME,"               "\n"
-      "VALUE,"                  "\n"
-      "SOURCE)"                 "\n"
-    "VALUES("                   "\n"
-      ":DRIVE_NAME,"            "\n"
-      ":CATEGORY,"              "\n"
-      ":KEY_NAME,"              "\n"
-      ":VALUE,"                 "\n"
-      ":SOURCE"                 "\n"
-    ")";
-
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    stmt.bindString(":CATEGORY", category);
-    stmt.bindString(":KEY_NAME", keyName);
-    if (value.empty()) {
-      stmt.bindString(":VALUE", std::string("NULL"));
-    } else {
-      stmt.bindString(":VALUE", value);
-    }
-    if (source.empty()){
-      stmt.bindString(":SOURCE", std::string("NULL"));
-    } else {
-      stmt.bindString(":SOURCE", source);
-    }
-
-    stmt.executeNonQuery();
-
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("driveName", tapeDriveName)
-      .add("category", category)
-      .add("keyName", keyName)
-      .add("value", value)
-      .add("source", source);
-    lc.log(log::INFO, "Catalogue - created drive configuration");
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::list<std::pair<std::string, std::string>> RdbmsCatalogue::getTapeDriveConfigNamesAndKeys() const {
-  try {
-    std::list<std::pair<std::string, std::string>> namesAndKeys;
-    const char *const sql =
-      "SELECT "
-        "DRIVE_NAME AS DRIVE_NAME,"
-        "KEY_NAME AS KEY_NAME "
-      "FROM "
-        "DRIVE_CONFIG ";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-
-    while (rset.next()) {
-      const std::string driveName = rset.columnString("DRIVE_NAME");
-      const std::string key = rset.columnString("KEY_NAME");
-      namesAndKeys.push_back(std::make_pair(driveName, key));
-    }
-    return namesAndKeys;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::list<cta::catalogue::Catalogue::DriveConfig> RdbmsCatalogue::getTapeDriveConfigs() const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DRIVE_NAME AS DRIVE_NAME,"
-        "CATEGORY AS CATEGORY,"
-        "KEY_NAME AS KEY_NAME,"
-        "VALUE AS VALUE,"
-        "SOURCE AS SOURCE "
-      "FROM "
-        "DRIVE_CONFIG ";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    auto rset = stmt.executeQuery();
-    std::list<cta::catalogue::Catalogue::DriveConfig> drivesConfigs;
-    while (rset.next()) {
-      const std::string tapeDriveName = rset.columnString("DRIVE_NAME");
-      const std::string category = rset.columnString("CATEGORY");
-      const std::string keyName = rset.columnString("KEY_NAME");
-      std::string value = rset.columnString("VALUE");
-      std::string source = rset.columnString("SOURCE");
-      if (value == "NULL") value.clear();
-      if (source == "NULL") source.clear();
-      cta::catalogue::Catalogue::DriveConfig driveConfig = {tapeDriveName, category, keyName, value, source};
-      drivesConfigs.push_back(driveConfig);
-    }
-    return drivesConfigs;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-std::optional<std::tuple<std::string, std::string, std::string>> RdbmsCatalogue::getTapeDriveConfig(
-  const std::string &tapeDriveName, const std::string &keyName) const {
-  try {
-    const char *const sql =
-      "SELECT "
-        "DRIVE_NAME AS DRIVE_NAME,"
-        "CATEGORY AS CATEGORY,"
-        "KEY_NAME AS KEY_NAME,"
-        "VALUE AS VALUE,"
-        "SOURCE AS SOURCE "
-      "FROM "
-        "DRIVE_CONFIG "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME AND KEY_NAME = :KEY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    stmt.bindString(":KEY_NAME", keyName);
-    auto rset = stmt.executeQuery();
-    if (rset.next()) {
-      const std::string category = rset.columnString("CATEGORY");
-      std::string value = rset.columnString("VALUE");
-      std::string source = rset.columnString("SOURCE");
-      if (value == "NULL") value.clear();
-      if (source == "NULL") source.clear();
-      return std::make_tuple(category, value, source);
-    }
-    return std::nullopt;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
-  const std::string &keyName, const std::string &value, const std::string &source) {
-  try {
-    const char *const sql =
-      "UPDATE DRIVE_CONFIG "
-      "SET "
-        "CATEGORY = :CATEGORY,"
-        "VALUE = :VALUE,"
-        "SOURCE = :SOURCE "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME AND KEY_NAME = :KEY_NAME";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-
-    stmt.bindString(":DRIVE_NAME", tapeDriveName);
-    stmt.bindString(":CATEGORY", category);
-    stmt.bindString(":KEY_NAME", keyName);
-    if (value.empty()) {
-      stmt.bindString(":VALUE", std::string("NULL"));
-    } else {
-      stmt.bindString(":VALUE", value);
-    }
-    if (source.empty()){
-      stmt.bindString(":SOURCE", std::string("NULL"));
-    } else {
-      stmt.bindString(":SOURCE", source);
-    }
-
-    stmt.executeNonQuery();
-
-    if (0 == stmt.getNbAffectedRows()) {
-      throw exception::Exception(std::string("Cannot modify Config Drive with name: ") + tapeDriveName +
-        " and key" + keyName + " because it doesn't exist");
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) {
-  try {
-    const char *const delete_sql =
-      "DELETE "
-      "FROM "
-        "DRIVE_CONFIG "
-      "WHERE "
-        "DRIVE_NAME = :DELETE_DRIVE_NAME AND KEY_NAME = :DELETE_KEY_NAME";
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(delete_sql);
-    stmt.bindString(":DELETE_DRIVE_NAME", tapeDriveName);
-    stmt.bindString(":DELETE_KEY_NAME", keyName);
-    stmt.executeNonQuery();
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getDiskSpaceReservations
-//------------------------------------------------------------------------------
-std::map<std::string, uint64_t> RdbmsCatalogue::getDiskSpaceReservations() const {
-  std::map<std::string, uint64_t> ret;
-  const auto tdNames = getTapeDriveNames();
-  for (const auto& driveName : tdNames) {
-    const auto tdStatus = getTapeDrive(driveName);
-    if (tdStatus.value().diskSystemName) {
-      //no need to check key, operator[] initializes missing values at zero for scalar types
-      ret[tdStatus.value().diskSystemName.value()] += tdStatus.value().reservedBytes.value();
-    }
-  }
-  return ret;
-}
-
-//------------------------------------------------------------------------------
-// reserveDiskSpace
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::reserveDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
-  if(diskSpaceReservation.empty()) return;
-
-  try {
-    {
-      log::ScopedParamContainer params(lc);
-      params.add("driveName", driveName)
-            .add("diskSystem", diskSpaceReservation.begin()->first)
-            .add("reservationBytes", diskSpaceReservation.begin()->second)
-            .add("mountId", mountId);
-      lc.log(log::DEBUG, "In RetrieveMount::reserveDiskSpace(): reservation request.");
-    }
-
-    // Normally the disk system name will not change. It can change in some rare circumstances, e.g. the tape server is
-    // assigned to a new VO.
-    //
-    // The disk system name is allowed to be updated when RESERVED_BYTES is zero (initial disk reservation, or previous
-    // disk reservations have been released). Otherwise, to update RESERVED_BYTES, the disk system name has to match.
-    const char* const sql =
-      "UPDATE DRIVE_STATE SET "
-        "RESERVED_BYTES = RESERVED_BYTES + :BYTES_TO_ADD "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME "
-        "AND DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME "
-        "AND RESERVATION_SESSION_ID = :RESERVATION_SESSION_ID ";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DRIVE_NAME", driveName);
-    stmt.bindString(":DISK_SYSTEM_NAME", diskSpaceReservation.begin()->first);
-    stmt.bindUint64(":BYTES_TO_ADD", diskSpaceReservation.begin()->second);
-    stmt.bindUint64(":RESERVATION_SESSION_ID", mountId);
-    stmt.executeNonQuery();
-
-    // If the reservation does not match the <driveName, diskSystem> pair in the DRIVE_STATE table,
-    // log an error and drop the previous reservation
-    if(stmt.getNbAffectedRows() != 1) {
-      {
-        log::ScopedParamContainer params(lc);
-        params.add("driveName", driveName)
-              .add("diskSystem", diskSpaceReservation.begin()->first)
-              .add("reservationBytes", diskSpaceReservation.begin()->second)
-              .add("mountId", mountId);
-        lc.log(log::INFO, "In RetrieveMount::releaseDiskSpace(): creating reservation for new mount");
-      }
-      const char* const sql_reset =
-        "UPDATE DRIVE_STATE SET "
-          "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME,"
-          "RESERVED_BYTES = :BYTES_TO_ADD,"
-          "RESERVATION_SESSION_ID = :RESERVATION_SESSION_ID "
-        "WHERE "
-          "DRIVE_NAME = :DRIVE_NAME";
-      stmt.reset();
-      stmt = conn.createStmt(sql_reset);
-      stmt.bindString(":DRIVE_NAME", driveName);
-      stmt.bindString(":DISK_SYSTEM_NAME", diskSpaceReservation.begin()->first);
-      stmt.bindUint64(":BYTES_TO_ADD", diskSpaceReservation.begin()->second);
-      stmt.bindUint64(":RESERVATION_SESSION_ID", mountId);
-      stmt.executeNonQuery();
-      if(stmt.getNbAffectedRows() != 1) {
-        log::ScopedParamContainer params(lc);
-        params.add("driveName", driveName)
-              .add("diskSystem", diskSpaceReservation.begin()->first)
-              .add("reservationBytes", diskSpaceReservation.begin()->second)
-              .add("mountId", mountId);
-        lc.log(log::ERR, "In RetrieveMount::releaseDiskSpace(): failed to create disk reservation for new mount.");
-      }
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// releaseDiskSpace
-//------------------------------------------------------------------------------
-void RdbmsCatalogue::releaseDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
-  if(diskSpaceReservation.empty()) return;
-
-  try {
-    {
-      log::ScopedParamContainer params(lc);
-      params.add("driveName", driveName)
-            .add("diskSystem", diskSpaceReservation.begin()->first)
-            .add("reservationBytes", diskSpaceReservation.begin()->second)
-            .add("mountId", mountId);
-      lc.log(log::DEBUG, "In RetrieveMount::releaseDiskSpace(): reservation release request.");
-    }
-
-    // If the amount being released exceeds the amount of the reservation, set the reservation to zero
-    const char* const sql =
-      "UPDATE DRIVE_STATE SET "
-        "RESERVED_BYTES = CASE WHEN RESERVED_BYTES > :BYTES_TO_SUBTRACT1 THEN RESERVED_BYTES-:BYTES_TO_SUBTRACT2 ELSE 0 END "
-      "WHERE "
-        "DRIVE_NAME = :DRIVE_NAME "
-        "AND DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME "
-        "AND RESERVATION_SESSION_ID = :RESERVATION_SESSION_ID";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":DRIVE_NAME", driveName);
-    stmt.bindString(":DISK_SYSTEM_NAME", diskSpaceReservation.begin()->first);
-    stmt.bindUint64(":BYTES_TO_SUBTRACT1", diskSpaceReservation.begin()->second);
-    stmt.bindUint64(":BYTES_TO_SUBTRACT2", diskSpaceReservation.begin()->second);
-    stmt.bindUint64(":RESERVATION_SESSION_ID", mountId);
-    stmt.executeNonQuery();
-    if(stmt.getNbAffectedRows() != 1) {
-      // If the reservation does not match the <driveName, diskSystem> pair in the DRIVE_STATE table, log an error and carry on
-      log::ScopedParamContainer params(lc);
-      params.add("driveName", driveName)
-            .add("diskSystem", diskSpaceReservation.begin()->first)
-            .add("reservationBytes", diskSpaceReservation.begin()->second)
-            .add("mountId", mountId);
-      lc.log(log::ERR, "In RetrieveMount::releaseDiskSpace(): reservation release request failed, driveName, diskSystem and mountId do not match.");
-    }
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-void RdbmsCatalogue::checkCommentOrReasonMaxLength(const std::optional<std::string>& str) const {
-  const size_t MAX_CHAR_COMMENT = 1000;
-  if (!str.has_value()) return;
-  if (str.value().length() > MAX_CHAR_COMMENT) {
-    log::LogContext lc(m_log);
-    log::ScopedParamContainer spc(lc);
-    spc.add("Large_Message: ", str.value());
-    lc.log(log::ERR, "The reason or comment has more characters than the maximun allowed.");
-    throw CommentOrReasonWithMoreSizeThanMaximunAllowed(
-      "The comment or reason string value has more than 1000 characters");
-  }
-}
-
-void RdbmsCatalogue::modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId, const std::string &diskInstance) const {
-    const char *const sql =
-    "UPDATE ARCHIVE_FILE SET "
-      "DISK_FILE_ID = :FXID,"
-      "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
-    "WHERE "
-      "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
-
-    auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":FXID", fxId);
-    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveId);
-    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
-    stmt.executeNonQuery();
-}
-
-}  // namespace catalogue
-}  // namespace cta
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
deleted file mode 100644
index b46e7f9c5c..0000000000
--- a/catalogue/RdbmsCatalogue.hpp
+++ /dev/null
@@ -1,2460 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "catalogue/Catalogue.hpp"
-#include "catalogue/InsertFileRecycleLog.hpp"
-#include "catalogue/RequesterAndGroupMountPolicies.hpp"
-#include "catalogue/TimeBasedCache.hpp"
-#include "common/dataStructures/TapeCopyToPoolMap.hpp"
-#include "common/threading/Mutex.hpp"
-#include "rdbms/ConnPool.hpp"
-#include "rdbms/Login.hpp"
-
-namespace cta {
-namespace common {
-namespace dataStructures {
-class TapeFile;
-}  // namespace dataStructures
-}  // namespace common
-}  // namespace cta
-
-namespace cta {
-namespace catalogue {
-
-class ArchiveFileRow;
-class ArchiveFileRowWithoutTimestamps;
-class RdbmsCatalogueGetArchiveFilesItor;
-class RsetWrapper;
-
-/**
- * CTA catalogue implemented using a relational database backend.
- */
-class RdbmsCatalogue: public Catalogue {
-protected:
-
-  /**
-   * Protected constructor only to be called by sub-classes.
-   *
-   * @param log Object representing the API to the CTA logging system.
-   * @param login The database login details to be used to create new
-   * connections.
-   * @param nbConns The maximum number of concurrent connections to the
-   * underlying relational database for all operations accept listing archive
-   * files which can be relatively long operations.
-   * @param nbArchiveFileListingConns The maximum number of concurrent
-   * connections to the underlying relational database for the sole purpose of
-   * listing archive files.
-   */
-  RdbmsCatalogue(
-    log::Logger &log,
-    const rdbms::Login &login,
-    const uint64_t nbConns,
-    const uint64_t nbArchiveFileListingConns);
-
-public:
-
-  /**
-   * Destructor.
-   */
-  ~RdbmsCatalogue() override;
-
-  /////////////////////////////////////////////////////////////////////
-  // START OF METHODS DIRECTLY INVOLVED IN DATA TRANSFER AND SCHEDULING
-  /////////////////////////////////////////////////////////////////////
-
-  /**
-   * Notifies the catalogue that the specified tape was labelled.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param drive The name of tape drive that was used to label the tape.
-   */
-  void tapeLabelled(const std::string &vid, const std::string &drive) override;
-
-  /**
-   * Checks the specified archival could take place and returns a new and
-   * unique archive file identifier that can be used by a new archive file
-   * within the catalogue.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * storage class belongs.
-   * @param storageClassName The name of the storage class of the file to be
-   * archived.  The storage class name is only guaranteed to be unique within
-   * its disk instance.  The storage class name will be used by the Catalogue
-   * to determine the destination tape pool for each tape copy.
-   * @param user The user for whom the file is to be archived.  This will be
-   * used by the Catalogue to determine the mount policy to be used when
-   * archiving the file.
-   * @return The new archive file identifier.
-   * @throw UserErrorWithTimeBasedCacheInfo if there was a user error.
-   */
-  uint64_t checkAndGetNextArchiveFileId(
-    const std::string &diskInstanceName,
-    const std::string &storageClassName,
-    const common::dataStructures::RequesterIdentity &user) override;
-
-  /**
-   * Returns the information required to queue an archive request.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * storage class belongs.
-   * @param storageClassName The name of the storage class of the file to be
-   * archived.  The storage class name is only guaranteed to be unique within
-   * its disk instance.  The storage class name will be used by the Catalogue
-   * to determine the destination tape pool for each tape copy.
-   * @param user The user for whom the file is to be archived.  This will be
-   * used by the Catalogue to determine the mount policy to be used when
-   * archiving the file.
-   * @return The information required to queue an archive request.
-   */
-  common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(
-    const std::string &diskInstanceName,
-    const std::string &storageClassName,
-    const common::dataStructures::RequesterIdentity &user) override;
-
-  /**
-   * Returns the list of tapes that can be written to by a tape drive in the
-   * specified logical library, in other words tapes that are labelled, not
-   * disabled, not full, not read-only and are in the specified logical library.
-   *
-   * @param logicalLibraryName The name of the logical library.
-   * @return The list of tapes for writing.
-   */
-  std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const override;
-
-  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override;
-
-  /**
-   * Notifies the CTA catalogue that the specified tape has been mounted in
-   * order to archive files.
-   *
-   * The purpose of this method is to keep track of which drive mounted a given
-   * tape for archiving files last.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param drive The name of the drive where the tape was mounted.
-   */
-  void tapeMountedForArchive(const std::string &vid, const std::string &drive) override;
-
-  /**
-   * Prepares for a file retrieval by returning the information required to
-   * queue the associated retrieve request(s).
-   *
-   * @param diskInstanceName The name of the instance from where the retrieval
-   * request originated
-   * @param archiveFileId The unique identifier of the archived file that is
-   * to be retrieved.
-   * @param user The user for whom the file is to be retrieved.  This will be
-   * used by the Catalogue to determine the mount policy to be used when
-   * retrieving the file.
-   * @param activity The activity under which the user wants to start the retrieve
-   * The call will fail if the activity is set and unknown.
-   * @param lc The log context.
-   *
-   * @return The information required to queue the associated retrieve request(s).
-   */
-  common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(
-    const std::string &diskInstanceName,
-    const uint64_t archiveFileId,
-    const common::dataStructures::RequesterIdentity &user,
-    const std::optional<std::string> & activity,
-    log::LogContext &lc,
-    const std::optional<std::string> &mountPolicyName = std::nullopt) override;
-
-  /**
-   * Notifies the CTA catalogue that the specified tape has been mounted in
-   * order to retrieve files.
-   *
-   * The purpose of this method is to keep track of which drive mounted a given
-   * tape for retrieving files last.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param drive The name of the drive where the tape was mounted.
-   */
-  void tapeMountedForRetrieve(const std::string &vid, const std::string &drive) override;
-
-  /**
-   * This method notifies the CTA catalogue that there is no more free space on
-   * the specified tape.
-   *
-   * @param vid The volume identifier of the tape.
-   */
-  void noSpaceLeftOnTape(const std::string &vid) override;
-
-  ///////////////////////////////////////////////////////////////////
-  // END OF METHODS DIRECTLY INVOLVED IN DATA TRANSFER AND SCHEDULING
-  ///////////////////////////////////////////////////////////////////
-
-  void createAdminUser(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) override;
-  void deleteAdminUser(const std::string &username) override;
-  std::list<common::dataStructures::AdminUser> getAdminUsers() const override;
-  void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) override;
-
-  /**
-   * Creates the specified Virtual Organization
-   * @param admin The administrator.
-   * @param vo the Virtual Organization
-   */
-  void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override;
-
-  /**
-   * Deletes the specified Virtual Organization
-   * @param voName the name of the VirtualOrganization to delete
-   */
-  void deleteVirtualOrganization(const std::string &voName) override;
-
-  /**
-   * Get all the Virtual Organizations from the Catalogue
-   * @return the list of all the Virtual Organizations
-   */
-  std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const override;
-
-  /**
-   * Get the virtual organization corresponding to the tapepool passed in parameter
-   * @param tapepoolName the name of the tapepool which we want the virtual organization
-   * @return the VirtualOrganization associated to the tapepool passed in parameter
-   */
-  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(const std::string & tapepoolName) const override;
-
-  /**
-   * Get the virtual organization corresponding to the tapepool passed in parameter
-   * @param conn the database connection
-   * @param tapepoolName the name of the tapepool which we want the virtual organization
-   * @return the VirtualOrganization associated to the tapepool passed in parameter
-   */
-  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(rdbms::Conn & conn, const std::string & tapepoolName) const;
-
-  /**
-   * Get, from the cache, the virtual organization corresponding to the tapepool passed in parameter
-   * @param tapepoolName the name of the tapepool which we want the virtual organization
-   * @return the VirtualOrganization associated to the tapepool passed in parameter
-   */
-  common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(const std::string & tapepoolName) const override;
-
-  /**
-   * Modifies the name of the specified Virtual Organization.
-   *
-   * @param currentVoName The current name of the Virtual Organization.
-   * @param newVoName The new name of the Virtual Organization.
-   */
-  void modifyVirtualOrganizationName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName, const std::string &newVoName) override;
-
-  /**
-   * Modifies the max number of allocated drives for read for the specified Virtual Organization
-   *
-   * @param voName the VO name
-   * @param readMaxDrives the new max number of allocated drives for read for the specified Virtual Organization
-   */
-  void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) override;
-
-  /**
-   * Modifies the max number of allocated drives for write for the specified Virtual Organization
-   *
-   * @param voName the VO name
-   * @param writeMaxDrives the new max number of allocated drives for write for the specified Virtual Organization
-   */
-  void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) override;
-
-  /**
-   * Modifies the max file size for the specified Virtual Organization
-   *
-   * @param voName the VO name
-   * @param maxFileSize the new max file size for the specified Virtual Organization
-   */
-
-  void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) override;
-  /**
-   * Modifies the comment of the specified Virtual Organization
-   *
-   * @param voName The name of the Virtual Organization.
-   * @param comment The new comment of the Virtual Organization.
-   */
-  void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) override;
-
-  /**
-   * Modifies the Disk Instance name of the specified Virtual Organization
-   *
-   * @param voName The name of the Virtual Organization.
-   * @param diskInstance The new Disk Instance name of the Virtual Organization.
-   */
-  void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) override;
-
-  /**
-   * Creates the specified storage class.
-   *
-   * @param admin The administrator.
-   * @param storageClass The storage class.
-   */
-  void createStorageClass(
-    const common::dataStructures::SecurityIdentity &admin,
-    const common::dataStructures::StorageClass &storageClass) override;
-
-  /**
-   * Deletes the specified storage class.
-   *
-   * @param stoargeClassName The name of the storage class which is only
-   * guaranteed to be unique within its disk isntance.
-   */
-  void deleteStorageClass(const std::string &storageClassName) override;
-
-  std::list<common::dataStructures::StorageClass> getStorageClasses() const override;
-  common::dataStructures::StorageClass getStorageClass(const std::string &name) const override;
-
-
-  void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbCopies) override;
-  void modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-  void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) override;
-  /**
-   * Modifies the name of the specified storage class.
-   *
-   * @param currentName The current name of the storage class.
-   * @param newName The new name of the storage class.
-   */
-  void modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override;
-
-  /**
-   * Creates a tape media type.
-   *
-   * @param mediaType The tape media type.
-   */
-  void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) override;
-
-  /**
-   * Deletes the specified tape media type.
-   *
-   * @param name The name of the tape media type.
-   */
-  void deleteMediaType(const std::string &name) override;
-
-  /**
-   * Returns all tape media types.
-   *
-   * @return All tape media types.
-   */
-  std::list<MediaTypeWithLogs> getMediaTypes() const override;
-
-  /**
-   * Return the media type associated to the tape corresponding to the
-   * vid passed in parameter
-   *
-   * @param vid the vid of the tape to return its media type
-   * @return the media type associated to the tape corresponding to the vid passed in parameter
-   */
-  MediaType getMediaTypeByVid(const std::string & vid) const override;
-
-  /**
-   * Modifies the name of the specified tape media type.
-   *
-   * @param currentName The current name of the tape media type.
-   * @param newName The new name of the tape media type.
-   */
-  void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override;
-
-  /**
-   * Modifies the cartidge of the specified tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param cartridge The new cartidge.
-   */
-  void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &cartridge) override;
-
-  /**
-   * Modify the capacity in bytes of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param capacityInBytes The new capacity in bytes.
-   */
-  void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t capacityInBytes) override;
-
-  /**
-   * Modify the SCSI primary density code a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param primaryDensityCode The new SCSI primary density code.
-   */
-  void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t primaryDensityCode) override;
-
-  /**
-   * Modify the SCSI secondary density code a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param secondaryDensityCode The new SCSI secondary density code.
-   */
-  void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t secondaryDensityCode) override;
-
-  /**
-   * Modify the number of tape wraps of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param nbWraps The new number of tape wraps.
-   */
-  void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint32_t> &nbWraps) override;
-
-  /**
-   * Modify the minimum longitudinal tape position of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param minLPos The new minimum longitudinal tape position.
-   */
-  void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &minLPos) override;
-
-  /**
-   * Modify the maximum longitudinal tape position of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param maxLPos The new maximum longitudinal tape position.
-   */
-  void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::optional<std::uint64_t> &maxLPos) override;
-
-  /**
-   * Modify the comment of a tape media type.
-   *
-   * @param admin The administrator.
-   * @param name The name of the tape media type.
-   * @param comment The new comment.
-   */
-  void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-
-  void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue, const std::optional<std::string> &supply, const std::string &comment) override;
-  void deleteTapePool(const std::string &name) override;
-  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const override;
-
-  std::list<TapePool> getTapePools(rdbms::Conn &conn, const TapePoolSearchCriteria &searchCriteria) const;
-
-  /**
-   * @return The tape pool with the specified name.
-   * @param tapePoolName The name of the tape pool.
-   */
-  std::optional<TapePool> getTapePool(const std::string &tapePoolName) const override;
-
-  void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) override;
-  void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbPartialTapes) override;
-  void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-  void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool encryptionValue) override;
-  void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &supply) override;
-
-  /**
-   * Modifies the name of the specified tape pool.
-   *
-   * @param admin The administrator.
-   * @param currentName The current name of the tape pool.
-   * @param newName The new name of the tape pool.
-   */
-  void modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override;
-
-  void createArchiveRoute(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &storageClassName,
-    const uint32_t copyNb,
-    const std::string &tapePoolName,
-    const std::string &comment) override;
-
-
-  /**
-   * Deletes the specified archive route.
-   *
-   * @param storageClassName The name of the storage class which is unique
-   * @param copyNb The copy number of the tape file.
-   */
-  void deleteArchiveRoute(
-    const std::string &storageClassName,
-    const uint32_t copyNb) override;
-
-  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const override;
-
-  /**
-   * @return the archive routes of the given storage class and destination tape
-   * pool.
-   *
-   * Under normal circumstances this method should return either 0 or 1 route.
-   * For a given storage class there should be no more than one route to any
-   * given tape pool.
-   *
-   * @param storageClassName The name of the storage class which is unique
-   * @param tapePoolName The name of the tape pool.
-   */
-  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(
-    const std::string &storageClassName,
-    const std::string &tapePoolName) const override;
-
-  void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) override;
-  void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) override;
-
-  void createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool isDisabled, const std::string &comment) override;
-  void deleteLogicalLibrary(const std::string &name) override;
-  std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const override;
-
-  /**
-   * Modifies the name of the specified logical library.
-   *
-   * @param admin The administrator.
-   * @param currentName The current name of the logical library.
-   * @param newName The new name of the logical library.
-   */
-  void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override;
-
-  void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-  void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &disabledReason) override;
-  virtual void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool disabledValue) override;
-
-  /**
-   * Creates a tape which is assumed to have isFromCastor disabled.
-   *
-   * @param admin The administrator.
-   * @param tape The attributes of the tape to be created.
-   */
-  void createTape(
-    const common::dataStructures::SecurityIdentity &admin,
-    const CreateTapeAttributes & tape) override;
-
-  void deleteTape(const std::string &vid) override;
-
-  /**
-   * Returns the list of tapes that meet the specified search criteria.
-   *
-   * @param searchCriteria The search criteria.
-   * @return The list of tapes.
-   */
-  std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria &searchCriteria) const override;
-
-  /**
-   * Returns the tape with the specified volume identifier.
-   *
-   * This method will throw an exception if it cannot find the specified tape.
-   *
-   * @param vid The tape volume identifier (VIDs).
-   * @return Map from tape volume identifier to tape.
-   */
-  common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const override;
-
-  /**
-   * Returns the tapes with the specified volume identifiers.
-   *
-   * This method will throw an exception if it cannot find ALL of the specified
-   * tapes.
-   *
-   * @param vids The tape volume identifiers (VIDs).
-   * @return Map from tape volume identifier to tape.
-   */
-  common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string> &vids) const override;
-
-  /**
-   * Returns map from VID to logical library name for specified set of VIDs.
-   *
-   * @param vids The tape volume identifiers (VIDs).
-   * @return map from VID to logical library name.
-   */
-  std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override;
-
-  /**
-   * Reclaims the specified tape.
-   *
-   * This method will throw an exception if the specified tape does not exist.
-   *
-   * This method will throw an exception if the specified tape is not FULL.
-   *
-   * This method will throw an exception if there is still at least one tape
-   * file recorded in the catalogue as being on the specified tape.
-   *
-   * @param admin The administrator.
-   * @param vid The volume identifier of the tape to be reclaimed.
-   * @param lc the logContext
-   */
-  void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, cta::log::LogContext & lc) override;
-
-  /**
-   * Checks the specified tape for the tape label command.
-   *
-   * This method checks if the tape is safe to be labeled and will throw an
-   * exception if the specified tape does not ready to be labeled.
-   *
-   * @param vid The volume identifier of the tape to be checked.
-   */
-  void checkTapeForLabel(const std::string &vid) override;
-
-  /**
-   * Returns the number of any files contained in the tape identified by its vid
-   * @param vid the vid in which we will count the number of files
-   * @return the number of files on the tape
-   */
-  uint64_t getNbFilesOnTape(const std::string &vid) const override;
-
-  /**
-   * Returns the number of any files contained in the tape identified by its vid
-   * @param conn the database connection
-   * @param vid the vid in which we will count the number of files
-   * @return the number of files on the tape
-   */
-  uint64_t getNbFilesOnTape(rdbms::Conn &conn, const std::string &vid) const;
-
-  /**
-   * Delete all the tape files of the VID passed in parameter
-   * @param conn the database connection
-   * @param vid the vid in which we want to remove all the tape files
-   */
-  void deleteTapeFiles(rdbms::Conn &conn, const std::string& vid) const;
-
-  /**
-   * Set the DIRTY flag to true
-   * @param conn the database connection
-   * @param vid	the vid in which we want to set it as dirty
-   */
-  void setTapeDirty(rdbms::Conn &conn, const std::string &vid) const;
-
-  /**
-   * Reset the counters of a tape
-   * @param conn the database connection
-   * @param admin the administrator
-   * @param vid the vid to reset the counters
-   */
-  void resetTapeCounters(rdbms::Conn &conn, const common::dataStructures::SecurityIdentity &admin ,const std::string& vid) const;
-  void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &mediaType) override;
-  void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &vendor) override;
-  void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &logicalLibraryName) override;
-  void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &tapePoolName) override;
-  void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &encryptionKeyName) override;
-  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &verificationStatus) override;
-  void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const std::optional<common::dataStructures::Tape::State> & prev_state, const std::optional<std::string> & stateReason) override;
-  static std::string generateTapeStateModifiedBy(const common::dataStructures::SecurityIdentity & admin);
-  /**
-   * Sets the full status of the specified tape.
-   *
-   * Please note that this method is to be called by the CTA front-end in
-   * response to a command from the CTA command-line interface (CLI).
-   *
-   * @param admin The administrator.
-   * @param vid The volume identifier of the tape to be marked as full.
-   * @param fullValue Set to true if the tape is full.
-   */
-  void setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const bool fullValue) override;
-
-  /**
-   * Sets the dirty status of the specified tape.
-   *
-   * Please note that this method is to be called by the CTA front-end in
-   * response to a command from the CTA command-line interface (CLI).
-   *
-   * @param admin The administrator.
-   * @param vid The volume identifier of the tape to be marked as full.
-   * @param dirtyValue Set to true if the tape is dirty.
-   */
-  void setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const bool dirtyValue) override;
-
-  /**
-   * This method notifies the CTA catalogue to set the specified tape is from CASTOR.
-   * This method only for unitTests and MUST never be called in CTA!!!
-   *
-   * @param vid The volume identifier of the tape.
-   */
-  void setTapeIsFromCastorInUnitTests(const std::string &vid) override;
-
-  void setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string & reason) override;
-  void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string & reason) override;
-  void setTapeDirty(const std::string & vid) override;
-  void modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,  const std::optional<std::string> &comment) override;
-
-  void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex, const std::string &mountPolicy) override;
-  void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex, const std::string &comment) override;
-  void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) override;
-  void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterName, const std::string &comment) override;
-  void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) override;
-  void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) override;
-
-  void createMountPolicy(const common::dataStructures::SecurityIdentity &admin, const CreateMountPolicyAttributes & mountPolicy) override;
-
-  /**
-   * Returns the list of all existing mount policies.
-   *
-   * @return the list of all existing mount policies.
-   */
-  std::list<common::dataStructures::MountPolicy> getMountPolicies() const override;
-
-  /**
-   * Returns the mount policy with the specified name.
-   *
-   * @return the specified mount policy
-   */
-  std::optional<common::dataStructures::MountPolicy> getMountPolicy(const std::string &mountPolicyName) const override;
-
-  /**
-   * Returns the mount policy with the specified name.
-   *
-   * @return the specified mount policy
-   */
-  std::optional<common::dataStructures::MountPolicy> getMountPolicy(rdbms::Conn &conn, const std::string &mountPolicyName) const;
-
-  /**
-   * Returns the list of all existing mount policies.
-   *
-   * @param conn the database connection
-   * @return the list of all existing mount policies.
-   */
-  std::list<common::dataStructures::MountPolicy> getMountPolicies(rdbms::Conn & conn) const;
-
-  /**
-   * Returns the cached list of all existing mount policies.
-   *
-   * @return the list of all existing mount policies.
-   */
-  std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const override;
-
-  /**
-   * Deletes the specified mount policy.
-   *
-   * @param name The name of the mount policy.
-   */
-  void deleteMountPolicy(const std::string &name) override;
-
-  /**
-   * Creates the rule that the specified mount policy will be used for the
-   * specified requester+matching activities.
-   *
-   * Please note that requester-activity mount-rules overrule requester
-   * mount-rules.
-   *
-   * @param admin The administrator.
-   * @param mountPolicyName The name of the mount policy.
-   * @param diskInstance The name of the disk instance to which the requester
-   * belongs.
-   * @param activityRegex The regex to match request activities
-   * @param requesterName The name of the requester which is only guarantted to
-   * be unique within its disk instance.
-   * @param comment Comment.
-   */
-  void createRequesterActivityMountRule(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &mountPolicyName,
-    const std::string &diskInstance,
-    const std::string &requesterName,
-    const std::string &activityRegex,
-    const std::string &comment) override;
-
-  /**
-   * Returns the rules that specify which mount policy is be used for which
-   * requester + activity.
-   *
-   * @return the rules that specify which mount policy is be used for which
-   * requester + activity.
-   */
-  std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const override;
-
-    /**
-   * Deletes the specified mount rule.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester belongs.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   * @param activityRegex The regex to match request activities
-   */
-  void deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName, const std::string &activityRegex) override;
-
-  /**
-   * Creates the rule that the specified mount policy will be used for the
-   * specified requester.
-   *
-   * Please note that requester mount-rules overrule requester-group
-   * mount-rules.
-   *
-   * @param admin The administrator.
-   * @param mountPolicyName The name of the mount policy.
-   * @param diskInstance The name of the disk instance to which the requester
-   * belongs.
-   * @param requesterName The name of the requester which is only guarantted to
-   * be unique within its disk instance.
-   * @param comment Comment.
-   */
-  void createRequesterMountRule(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &mountPolicyName,
-    const std::string &diskInstance,
-    const std::string &requesterName,
-    const std::string &comment) override;
-
-  /**
-   * Returns the rules that specify which mount policy is be used for which
-   * requester.
-   *
-   * @return the rules that specify which mount policy is be used for which
-   * requester.
-   */
-  std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const override;
-
-  /**
-   * Deletes the specified mount rule.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester belongs.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   */
-  void deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) override;
-
-  /**
-   * Creates the rule that the specified mount policy will be used for the
-   * specified requester group.
-   *
-   * Please note that requester mount-rules overrule requester-group
-   * mount-rules.
-   *
-   * @param admin The administrator.
-   * @param mountPolicyName The name of the mount policy.
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester group belongs.
-   * @param requesterGroupName The name of the requester group which is only
-   * guarantted to be unique within its disk instance.
-   * @param comment Comment.
-   */
-  void createRequesterGroupMountRule(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &mountPolicyName,
-    const std::string &diskInstanceName,
-    const std::string &requesterGroupName,
-    const std::string &comment) override;
-
-  /**
-   * Returns the rules that specify which mount policy is be used for which
-   * requester group.
-   *
-   * @return the rules that specify which mount policy is be used for which
-   * requester group.
-   */
-  std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const override;
-
-  /**
-   * Deletes the specified mount rule.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester group belongs.
-   * @param requesterGroupName The name of the requester group which is only
-   * guaranteed to be unique within its disk instance.
-   */
-  void deleteRequesterGroupMountRule(
-    const std::string &diskInstanceName,
-    const std::string &requesterGroupName) override;
-
-  void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t archivePriority) override;
-  void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t minArchiveRequestAge) override;
-  void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t retrievePriority) override;
-  void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t minRetrieveRequestAge) override;
-  void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override;
-
-  /**
-   * Returns all the disk systems within the CTA catalogue.
-   *
-   * @return The disk systems.
-   * requester group.
-   */
-  disk::DiskSystemList getAllDiskSystems() const override;
-
-  /**
-   * Creates a disk system.
-   *
-   * @param admin The administrator.
-   * @param name The name of the disk system.
-   * @param fileRegexp The regular expression allowing matching destination URLs
-   * for this disk system.
-   * @param freeSpaceQueryURL The query URL that describes a method to query the
-   * free space from the disk system.
-   * @param refreshInterval The refresh interval (seconds) defining how long do
-   * we use a free space value.
-   * @param targetedFreeSpace The targeted free space (margin) based on the free
-   * space update latency (inherent to the file system and induced by the refresh
-   * interval), and the expected external bandwidth from sources external to CTA.
-   * @param comment Comment.
-   */
-  void createDiskSystem(
-    const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name,
-    const std::string &diskInstanceName,
-    const std::string &diskInstanceSpaceName,
-    const std::string &fileRegexp,
-    const uint64_t targetedFreeSpace,
-    const time_t sleepTime,
-    const std::string &comment) override;
-
-  /**
-   * Deletes a disk system.
-   *
-   * @param name The name of the disk system.
-   */
-  void deleteDiskSystem(const std::string &name) override;
-
-  void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &fileRegexp) override;
-  void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const uint64_t targetedFreeSpace) override;
-  void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &comment) override;
-  void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
-    const std::string& name, const uint64_t sleepTime) override;
-  void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstanceName) override;
-  void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstanceSpaceName) override;
-
-  /**
-   * Creates the specified Disk Instance
-   * @param admin The administrator.
-   * @param name the name of the new disk instance
-   * @param comment the comment of the new disk instance
-   */
-  void createDiskInstance(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name,
-    const std::string &comment) override;
-
-  /**
-   * Returns all the disk instances within the CTA catalogue.
-   *
-   * @return The disk instances in the CTA catalogue.
-   */
-  std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const override;
-
-  /**
-   * Deletes a disk instance.
-   *
-   * @param name The name of the disk instance.
-   */
-  void deleteDiskInstance(const std::string &name) override;
-
-  /**
-   * Changes the comment of the specified disk instance
-   * @param admin The administrator.
-   * @param name the name of the disk instance
-   * @param comment the new comment of the disk instance
-   */
-  void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &comment) override;
-
-  /**
-   * Deletes a disk instance space.
-   *
-   * @param name The name of the disk instance.
-   * @param diskInstance The disk instance of the disk instance space.
-   */
-  void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) override;
-
-  /**
-   * Creates the specified Disk Instance Space
-   * @param admin The administrator.
-   * @param name the name of the new disk instance space
-   * @param diskInstance the disk instance associated to the disk instance space
-   * @param freeSpaceQueryURL the URL to query to obtain the disk instance space free space
-   * @param refreshInterval the period to query for disk instance space free space
-   * @param comment the comment of the new disk instance space
-   */
-  void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name,
-    const std::string &diskInstance,
-    const std::string &freeSpaceQueryURL,
-    const uint64_t refreshInterval,
-    const std::string &comment) override;
-
-  /**
-   * Returns all the disk instance spaces within the CTA catalogue.
-   *
-   * @return The disk instance spaces in the CTA catalogue.
-   */
-  std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const override;
-
-  void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const std::string &comment) override;
-  void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) override;
-  void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin,
-    const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) override;
-  void modifyDiskInstanceSpaceFreeSpace(const std::string &name, const std::string &diskInstance, const uint64_t freeSpace) override;
-
-  /**
-   * Throws a UserError exception if the specified searchCriteria is not valid
-   * due to a user error.
-   *
-   * @param searchCriteria The search criteria.
-   */
-  void checkTapeFileSearchCriteria(const TapeFileSearchCriteria &searchCriteria) const;
-
-    /**
-   * Throws a UserError exception if the specified searchCriteria is not valid
-   * due to a user error.
-   *
-   * @param conn The database connection.
-   * @param searchCriteria The search criteria.
-   */
-  void checkTapeFileSearchCriteria(rdbms::Conn &conn, const TapeFileSearchCriteria &searchCriteria) const;
-
-  /**
-   * Returns the specified archive files.  Please note that the list of files
-   * is ordered by archive file ID.
-   *
-   * @param searchCriteria The search criteria.
-   * @return The archive files.
-   */
-  ArchiveFileItor getArchiveFilesItor(const TapeFileSearchCriteria &searchCriteria) const override;
-
-
-  /**
-   * Returns the specified archive files.  Please note that the list of files
-   * is ordered by archive file ID.
-   *
-   * @param conn The database connection.
-   * @param searchCriteria The search criteria.
-   * @return The archive files.
-   */
-  ArchiveFileItor getArchiveFilesItor(rdbms::Conn &conn, const TapeFileSearchCriteria &searchCriteria) const;
-
-  /**
-   * Throws a UserError exception if the specified searchCriteria is not valid
-   * due to a user error.
-   * @param conn The database connection.
-   * @param searchCriteria The search criteria.
-   */
-  void checkRecycleTapeFileSearchCriteria(cta::rdbms::Conn &conn, const RecycleTapeFileSearchCriteria & searchCriteria) const;
-
-  /**
-   * Returns all the currently deleted files by looking at the FILE_RECYCLE_LOG table
-   *
-   * @param searchCriteria The search criteria
-   * @return The deleted archive files ordered by archive file ID.
-   */
-  FileRecycleLogItor getFileRecycleLogItor(const RecycleTapeFileSearchCriteria & searchCriteria) const override;
-
-  /**
-   * Restores the deleted file in the Recycle log that match the criteria passed
-   *
-   * @param searchCriteria The search criteria
-   * @param newFid the new Fid of the archive file (if the archive file must be restored)
-   */
-  void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria, const std::string &newFid) override;
-
-  /**
-   * Copy the fileRecycleLog to the ARCHIVE_FILE with a new eos fxid
-   * @param conn the database connection
-   * @param fileRecycleLog the fileRecycleLog we want to restore
-   * @param newFid the new eos file id of the archive file
-   * @param lc the log context
-   */
-  void restoreArchiveFileInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLogItor,
-    const std::string &newFid, log::LogContext & lc);
-
-  /**
-   * Returns the specified files in tape file sequence order.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param startFSeq The file sequence number of the first file.  Please note
-   * that there might not be a file with this exact file sequence number.
-   * @param maxNbFiles The maximum number of files to be returned.
-   * @return The specified files in tape file sequence order.
-   */
-  std::list<common::dataStructures::ArchiveFile> getFilesForRepack(
-    const std::string &vid,
-    const uint64_t startFSeq,
-    const uint64_t maxNbFiles) const override;
-
-  /**
-   * Returns all the tape copies (no matter their VIDs) of the archive files
-   * associated with the tape files on the specified tape in FSEQ order
-   * starting at the specified startFSeq.
-   *
-   * @param vid The volume identifier of the tape.
-   * @param startFSeq The file sequence number of the first file.  Please note
-   * that there might not be a file with this exact file sequence number.
-   * @return The specified files in FSEQ order.
-   */
-  ArchiveFileItor getArchiveFilesForRepackItor(
-    const std::string &vid,
-    const uint64_t startFSeq) const override;
-
-  /**
-   * Returns a summary of the tape files that meet the specified search
-   * criteria.
-   *
-   * @param searchCriteria The search criteria.
-   * @return The summary.
-   */
-  common::dataStructures::ArchiveFileSummary getTapeFileSummary(
-    const TapeFileSearchCriteria &searchCriteria) const override;
-
-  /**
-   * Returns the specified archive file. If the search criteria result in more than one tape file being returned
-   * an exception is thrown.
-   * @param searchCriteria The search criteria.
-   * @return The archive file.
-   */
-  common::dataStructures::ArchiveFile getArchiveFileForDeletion(const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
-
-  /**
-  * Deletes a tape file copy
-  *
-  * @param file The tape file to delete
-  * @param reason The reason for deleting the tape file copy
-  */
-  void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) override;
-
-    /**
-   * Returns the archive file with the specified unique identifier.
-   *
-   * This method assumes that the archive file being requested exists and will
-   * therefore throw an exception if it does not.
-   *
-   * Please note that an archive file with no associated tape files is
-   * considered not to exist by this method.
-   *
-   * @param id The unique identifier of the archive file.
-   * @return The archive file.
-   */
-  common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const override;
-
-  /**
-   * Returns true if the specified user has administrator privileges.
-   *
-   * @param admin The administrator.
-   * @return True if the specified user has administrator privileges.
-   */
-  bool isAdmin(const common::dataStructures::SecurityIdentity &admin) const override;
-
-  /**
-   * Checks that the most trivial query goes through. Returns true on success,
-   * false on failure.
-   *
-   * @return True if the query went through.
-   */
-  void ping() override;
-
-  /**
-   * Checks that the online database schema MAJOR version number matches the schema MAJOR version number defined in version.h
-   */
-  void verifySchemaVersion() override;
-
-  /**
-   * Returns the SchemaVersion object corresponding to the catalogue schema version:
-   * - SCHEMA_VERSION_MAJOR
-   * - SCHEMA_VERSION_MINOR
-   * - SCHEMA_VERSION_MAJOR_NEXT (future major version number of the schema in case of upgrade)
-   * - SCHEMA_VERSION_MINOR_NEXT (future minor version number of the schema in case of upgrade)
-   * - STATUS (UPGRADING or PRODUCTION)
-   *
-   * @return The SchemaVersion object corresponding to the catalogue schema version
-   */
-  SchemaVersion getSchemaVersion() const override;
-
-  /**
-   * Returns the names of all the tables in the database schema in alphabetical
-   * order.
-   *
-   * @return The names of all the tables in the database schema in alphabetical
-   * order.
-   */
-  std::list<std::string> getTableNames() const;
-
-protected:
-
-  /**
-   * Object representing the API to the CTA logging system.
-   */
-  log::Logger &m_log;
-
-  /**
-   * Mutex to be used to a take a global lock on the database.
-   */
-  threading::Mutex m_mutex;
-
-  /**
-   * The pool of connections to the underlying relational database to be used
-   * for all operations accept listing archive files which can be relatively
-   * long operations.
-   */
-  mutable rdbms::ConnPool m_connPool;
-
-  /**
-   * The pool of connections to the underlying relational database to be used
-   * for the sole purpose of listing archive files.
-   */
-  mutable rdbms::ConnPool m_archiveFileListingConnPool;
-
-  /**
-   * Returns true if the specified admin user exists.
-   *
-   * @param conn The database connection.
-   * @param adminUsername The name of the admin user.
-   * @return True if the admin user exists.
-   */
-  bool adminUserExists(rdbms::Conn &conn, const std::string adminUsername) const;
-
-  /**
-   * Returns true if the specified vo exists.
-   *
-   * @param conn The database connection.
-   * @param voName The name of the vo
-   * @return True if the vo exists, false otherwise
-   */
-  bool virtualOrganizationExists(rdbms::Conn &conn, const std::string &voName) const;
-
-  /**
-   * Returns true if the specified media type exists.
-   *
-   * @param conn The database connection.
-   * @param name The name of the media type.
-   * @return True if the media type exists.
-   */
-  bool mediaTypeExists(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns true if the specified storage class exists.
-   *
-   * @param conn The database connection.
-   * @param storageClassName The name of the storage class.
-   * @return True if the storage class exists.
-   */
-  bool storageClassExists(rdbms::Conn &conn, const std::string &storageClassName) const;
-
-  /**
-   * Returns true if the specified tape pool exists.
-   *
-   * @param tapePoolName The name of the tape pool.
-   * @return True if the tape pool exists.
-   */
-  bool tapePoolExists(const std::string &tapePoolName) const override;
-
-  /**
-   * Returns true if the specified tape pool exists.
-   *
-   * @param conn The database connection.
-   * @param tapePoolName The name of the tape pool.
-   * @return True if the tape pool exists.
-   */
-  bool tapePoolExists(rdbms::Conn &conn, const std::string &tapePoolName) const;
-
-  /**
-   * Returns true if the specified tape pool is used in an archive route.
-   *
-   * @param conn The database connection.
-   * @param tapePoolName The name of the tape pool.
-   * @return True if the tape pool is used in an archive route.
-   */
-  bool tapePoolUsedInAnArchiveRoute(rdbms::Conn &conn, const std::string &tapePoolName) const;
-
-  /**
-   * Returns true if the specified archive file identifier exists.
-   *
-   * @param conn The database connection.
-   * @param archiveFileId The archive file identifier.
-   * @return True if the archive file identifier exists.
-   */
-  bool archiveFileIdExists(rdbms::Conn &conn, const uint64_t archiveFileId) const;
-
-  /**
-  * @param newStorageClassName The name of the storage class
-  * @param archiveFileId Id for file found in ARCHIVE_FILE
-  */
-  void modifyArchiveFileStorageClassId(const uint64_t archiveFileId, const std::string& newStorageClassName) const override;
-
-    /**
-  * Changes the fxid in for a archive file
-  * @param archiveId The archive file id
-  * @param fxId The eos fxid related to the archive file
-  * @param diskInstance Disk instace
-  */
-  void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId, const std::string &diskInstance) const override;
-/**
-   * Returns true if the specified disk file identifier exists.
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance to which the disk
-   * file identifier belongs.
-   * @param diskFileId The disk file identifier.
-   * @return True if the disk file identifier exists.
-   */
-  bool diskFileIdExists(rdbms::Conn &conn, const std::string &diskInstanceName, const std::string &diskFileId) const;
-
-  /**
-   * Returns true if the specified disk file user exists.
-   *
-   * @param conn              The database connection.
-   * @param diskInstanceName  The name of the disk instance to which the disk file user belongs.
-   * @param diskFileOwnerUid  The user ID of the disk file owner.
-   * @return                  True if the disk file user exists.
-   */
-  bool diskFileUserExists(rdbms::Conn &conn, const std::string &diskInstanceName, uint32_t diskFileOwnerUid) const;
-
-  /**
-   * Returns true if the specified disk file group exists.
-   *
-   * @param conn              The database connection.
-   * @param diskInstanceName  The name of the disk instance to which the disk file group belongs.
-   * @param diskFileGid       The group ID of the disk file.
-   * @return                  True if the disk file group exists.
-   */
-  bool diskFileGroupExists(rdbms::Conn &conn, const std::string &diskInstanceName, uint32_t diskFileGid) const;
-
-  /**
-   * Returns true if the specified archive route exists.
-   *
-   * @param conn The database connection.
-   * @param storageClassName The name of the storage class which is only
-   * guaranteed to be unique within its disk instance.
-   * @param copyNb The copy number of the tape file.
-   * @return True if the archive route exists.
-   */
-  bool archiveRouteExists(rdbms::Conn &conn, const std::string &storageClassName,
-    const uint32_t copyNb) const;
-
-  /**
-   * @return the archive routes of the given storage class and destination tape
-   * pool.
-   *
-   * Under normal circumstances this method should return either 0 or 1 route.
-   * For a given storage class there should be no more than one route to any
-   * given tape pool.
-   *
-   * @param conn The database connection.
-   * @param storageClassName The name of the storage class which is only
-   * guaranteed to be unique within its disk instance.
-   * @param tapePoolName The name of the tape pool.
-   */
-  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(rdbms::Conn &conn,
-    const std::string &storageClassName, const std::string &tapePoolName) const;
-
-  /**
-   * Returns true if the specified tape exists.
-   *
-   * @param vid The volume identifier of the tape.
-   * @return True if the tape exists.
-   */
-  bool tapeExists(const std::string &vid) const override;
-
-  /**
-   * Returns true if the specified tape exists.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @return True if the tape exists.
-   */
-  bool tapeExists(rdbms::Conn &conn, const std::string &vid) const;
-
-  /**
-   * Returns true if the specified disk system exists.
-   *
-   * @param name The name identifier of the disk system.
-   * @return True if the tape exists.
-   */
-  bool diskSystemExists(const std::string &name) const override;
-
-  /**
-   * Returns true if the specified disk system exists.
-   *
-   * @param conn The database connection.
-   * @param name The name identifier of the disk system.
-   * @return True if the disk system exists.
-   */
-  bool diskSystemExists(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns true if the specified disk instance exists.
-   *
-   * @param conn The database connection.
-   * @param name The name identifier of the disk instance.
-   * @return True if the disk instance exists.
-   */
-  bool diskInstanceExists(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns true if the specified disk instance space exists.
-   *
-   * @param conn The database connection.
-   * @param name The name identifier of the disk instance space.
-   * @param diskInstance the disk instance associated to the disk instance space
-   * @return True if the disk instance exists.
-   */
-  bool diskInstanceSpaceExists(rdbms::Conn &conn, const std::string &name, const std::string &diskInstance) const;
-
-
-  /**
-   * Returns the list of tapes that meet the specified search criteria.
-   *
-   * @param conn The database connection.
-   * @param searchCriteria The search criteria.
-   * @return The list of tapes.
-   */
-  std::list<common::dataStructures::Tape> getTapes(rdbms::Conn &conn, const TapeSearchCriteria &searchCriteria) const;
-
-  /**
-   * Returns true if the specified logical library exists.
-   *
-   * @param conn The database connection.
-   * @param logicalLibraryName The name of the logical library.
-   * @return True if the logical library exists.
-   */
-  bool logicalLibraryExists(rdbms::Conn &conn, const std::string &logicalLibraryName) const;
-
-  /**
-   * Returns true if the specified mount policy exists.
-   *
-   * @param conn The database connection.
-   * @param mountPolicyName The name of the mount policy
-   * @return True if the mount policy exists.
-   */
-  bool mountPolicyExists(rdbms::Conn &conn, const std::string &mountPolicyName) const;
-
-  /**
-   * Returns true if the specified requester mount-rule exists.
-   *
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester belongs.
-   * @param requesterName The username of the requester which is only guaranteed
-   * to be unique within its disk instance.
-   * @return True if the requester mount-rule exists.
-   */
-  bool requesterMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-    const std::string &requesterName) const;
-
-  /**
-    * A fully qualified user, in other words the name of the disk instance and
-    * the name of the group.
-    */
-  struct User {
-    /**
-     * The name of the disk instance to which the user name belongs.
-     */
-    std::string diskInstanceName;
-
-    /**
-     * The name of the user which is only guaranteed to be unique within its
-     * disk instance.
-     */
-    std::string username;
-
-    /**
-     * Constructor.
-     *
-     * @param d The name of the disk instance to which the group name belongs.
-     * @param u The name of the group which is only guaranteed to be unique
-     * within its disk instance.
-     */
-    User(const std::string &d, const std::string &u): diskInstanceName(d), username(u) {
-    }
-
-    /**
-     * Less than operator.
-     *
-     * @param rhs The argument on the right hand side of the operator.
-     * @return True if this object is less than the argument on the right hand
-     * side of the operator.
-     */
-    bool operator<(const User &rhs) const {
-      return diskInstanceName < rhs.diskInstanceName || username < rhs.username;
-    }
-  }; // struct User
-
-  /**
-   * Returns a cached version of the specified requester mount-policy or std::nullopt
-   * if one does not exist.
-   *
-   * @param user The fully qualified user, in other words the name of the disk
-   * instance and the name of the group.
-   * @return The mount policy or std::nullopt if one does not exists.
-   * @throw UserErrorWithTimeBasedCacheInfo if there was a user error.
-   */
-  ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy> > getCachedRequesterMountPolicy(const User &user) const;
-
-  /**
-   * Returns true if the specified requester+activity mount-policy exists
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester and requester group belong.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   * @param activityRegex The regex to match request activities
-   * @return True if the requester-activity mount-rule exists
-   */
-  bool requesterActivityMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName, const std::string &requesterName, const std::string &activityRegex) const;
-
-  /**
-   * Returns the specified requester mount-policy or std::nullopt if one does not
-   * exist.
-   *
-   * @param conn The database connection.
-   * @param user The fully qualified user, in other words the name of the disk
-   * instance and the name of the group.
-   * @return The mount policy or std::nullopt if one does not exists.
-   */
-  std::optional<common::dataStructures::MountPolicy> getRequesterMountPolicy(rdbms::Conn &conn, const User &user) const;
-
-  /**
-   * Returns true if the specified requester-group mount-rule exists.
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester group belongs.
-   * @param requesterGroupName The name of the requester group which is only
-   * guaranteed to be unique within its disk instance.
-   * @return True if the requester-group mount-rule exists.
-   */
-  bool requesterGroupMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
-    const std::string &requesterGroupName) const;
-
-  /**
-    * A fully qualified user group, in other words the name of the disk instance
-    * and the name of the group.
-    */
-  struct Group {
-    /**
-     * The name of the disk instance to which the group name belongs.
-     */
-    std::string diskInstanceName;
-
-    /**
-     * The name of the group which is only guaranteed to be unique within its
-     * disk instance.
-     */
-    std::string groupName;
-
-    /**
-     * Constructor.
-     *
-     * @param d The name of the disk instance to which the group name belongs.
-     * @param g The name of the group which is only guaranteed to be unique
-     * within its disk instance.
-     */
-    Group(const std::string &d, const std::string &g): diskInstanceName(d), groupName(g) {
-    }
-
-    /**
-     * Less than operator.
-     *
-     * @param rhs The argument on the right hand side of the operator.
-     * @return True if this object is less than the argument on the right hand
-     * side of the operator.
-     */
-    bool operator<(const Group &rhs) const {
-      return diskInstanceName < rhs.diskInstanceName || groupName < rhs.groupName;
-    }
-  }; // struct Group
-
-  /**
-   * Returns a cached version of the specified requester-group mount-policy or
-   * nullptr if one does not exist.
-   *
-   * This method updates the cache when necessary.
-   *
-   * @param group The fully qualified group, in other words the name of the disk
-   * instance and the name of the group.
-   * @return The cached mount policy or std::nullopt if one does not exists.
-   */
-  ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy> > getCachedRequesterGroupMountPolicy(const Group &group) const;
-
-  /**
-   * Returns the specified requester-group mount-policy or nullptr if one does
-   * not exist.
-   *
-   * @param conn The database connection.
-   * @param group The fully qualified group, in other words the name of the disk
-   * instance and the name of the group.
-   * @return The mount policy or std::nullopt if one does not exists.
-   */
-  std::optional<common::dataStructures::MountPolicy> getRequesterGroupMountPolicy(rdbms::Conn &conn, const Group &group)
-    const;
-
-  /**
-   * Returns the specified tape log information from the specified database
-   * result set.
-   *
-   * @param rset The result set.
-   * @param driveColName The name of the database column that contains the name
-   * of the tape drive.
-   * @param timeColNAme The name of the database column that contains the time
-   * stamp.
-   */
-  std::optional<common::dataStructures::TapeLog> getTapeLogFromRset(const rdbms::Rset &rset,
-    const std::string &driveColName, const std::string &timeColName) const;
-
-  /**
-   * An RdbmsCatalogue specific method that inserts the specified row into the
-   * ArchiveFile table.
-   *
-   * @param conn The database connection.
-   * @param row The row to be inserted.
-   */
-  void insertArchiveFile(rdbms::Conn &conn, const ArchiveFileRowWithoutTimestamps &row);
-
-  /**
-   * Creates the database schema.
-   */
-  void createDbSchema();
-
-  /**
-   * A fully qualified storage class, in other words the name of the disk
-   * instance and the name of the storage class.
-   */
-  struct StorageClass {
-
-    /**
-     * The name of the storage class which is only guaranteed to be unique
-     */
-    std::string storageClassName;
-
-    /**
-     * Constructor.
-     *
-     * @param sN The name of the storage class which is only guaranteed to be
-     * unique within its disk instance.
-     */
-    StorageClass(const std::string &s): storageClassName(s) {
-    }
-
-    /**
-     * Less than operator.
-     *
-     * @param rhs The argument on the right hand side of the operator.
-     * @return True if this object is less than the argument on the right hand
-     * side of the operator.
-     */
-    bool operator<(const StorageClass &rhs) const {
-      return storageClassName < rhs.storageClassName;
-    }
-  }; // struct StorageClass
-
-  /**
-   * Returns a cached version of the expected number of archive routes for the
-   * specified storage class as specified by the call to the
-   * createStorageClass() method as opposed to the actual number entered so far
-   * using the createArchiveRoute() method.
-   *
-   * This method updates the cache when necessary.
-   *
-   * @param storageClass The fully qualified storage class, in other words the
-   * name of the disk instance and the name of the storage class.
-   * @return The expected number of archive routes.
-   */
-  uint64_t getCachedExpectedNbArchiveRoutes(const StorageClass &storageClass) const;
-
-  /**
-   * Returns the expected number of archive routes for the specified storage
-   * class as specified by the call to the createStorageClass() method as
-   * opposed to the actual number entered so far using the createArchiveRoute()
-   * method.
-   *
-   * @param conn The database connection.
-   * @param storageClass The fully qualified storage class, in other words the
-   * name of the disk instance and the name of the storage class.
-   * @return The expected number of archive routes.
-   */
-  uint64_t getExpectedNbArchiveRoutes(rdbms::Conn &conn, const StorageClass &storageClass) const;
-
-  /**
-   * Inserts the specified tape file into the Tape table.
-   *
-   * @param conn The database connection.
-   * @param tapeFile The tape file.
-   * @param archiveFileId The identifier of the archive file of which the tape
-   * file is a copy.
-   */
-  void insertTapeFile(
-    rdbms::Conn &conn,
-    const common::dataStructures::TapeFile &tapeFile,
-    const uint64_t archiveFileId);
-
-  /**
-   * Sets the last FSeq of the specified tape to the specified value.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @param lastFseq The new value of the last FSeq.
-   */
-  void setTapeLastFSeq(rdbms::Conn &conn, const std::string &vid, const uint64_t lastFSeq);
-
-  /**
-   * Returns the last FSeq of the specified tape.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @return The last FSeq.
-   */
-  uint64_t getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const;
-
-  /**
-   * Updates the specified tape with the specified information.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @param lastFSeq The sequence number of the last tape file written to the
-   * tape.
-   * @param compressedBytesWritten The number of compressed bytes written to
-   * the tape.
-   * @param filesWritten The number of files written to tape
-   * @param tapeDrive The name of the tape drive that last wrote to the tape.
-   */
-  void updateTape(
-    rdbms::Conn &conn,
-    const std::string &vid,
-    const uint64_t lastFSeq,
-    const uint64_t compressedBytesWritten,
-    const uint64_t filesWritten,
-    const std::string &tapeDrive);
-
-  /**
-   * Returns the archive file with the specified unique identifier or nullptr if
-   * it does not exist.
-   *
-   * Please note that an archive file with no associated tape files is
-   * considered not to exist by this method.
-   *
-   * @param conn The database connection.
-   * @param id The unique identifier of the archive file.
-   * @return The archive file.
-   */
-  std::unique_ptr<common::dataStructures::ArchiveFile> getArchiveFileById(rdbms::Conn &conn, const uint64_t archiveFileId) const;
-
-  /**
-   * Returns the specified archive file row.   A nullptr pointer is returned if
-   * there is no corresponding row in the ARCHIVE_FILE table.
-   *
-   * @param conn The database connection.
-   * @param id The identifier of the archive file.
-   * @return The archive file row or nullptr.
-   */
-  std::unique_ptr<ArchiveFileRow> getArchiveFileRowById(rdbms::Conn &conn, const uint64_t id) const;
-
-  /**
-   * Returns the specified archive file.   A nullptr pointer is returned if
-   * there are no corresponding rows in the TAPE_FILE table. Only looks at TAPE_FILE entries
-   * on Tapes with state 'ACTIVE'
-   *
-   * @param conn The database connection.
-   * @param archiveFileId The identifier of the archive file.
-   * @return The archive file or nullptr.
-   */
-  std::unique_ptr<common::dataStructures::ArchiveFile> getArchiveFileToRetrieveByArchiveFileId(
-    rdbms::Conn &conn,
-    const uint64_t archiveFileId) const;
-
-  /**
-   * Returns the specified archive file.   A nullptr pointer is returned if
-   * there are no corresponding rows in the TAPE_FILE table.
-   *
-   * @param conn The database connection.
-   * @param archiveFileId The identifier of the archive file.
-   * @return A list of tape file vid and corresponding tape state pairs
-   */
-  const std::list<std::pair<std::string, std::string>> getTapeFileStateListForArchiveFileId(
-    rdbms::Conn &conn,
-    const uint64_t archiveFileId) const;
-
-  /**
-   * Returns the specified archive file.   A nullptr pointer is returned if
-   * there is no corresponding row in the ARCHIVE_FILE table.  Please note that
-   * a non-nullptr is returned if there is a row in the ARCHIVE_FILE table and
-   * there are no rows in the TAPE_FILE table.
-   *
-   * Please note that this method performs a LEFT OUTER JOIN from the
-   * ARCHIVE_FILE table to the TAPE_FILE table.
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance.
-   * @param diskFileId The identifier of the source disk file which is unique
-   * within it's host disk system.  Two files from different disk systems may
-   * have the same identifier.  The combination of diskInstanceName and
-   * diskFileId must be globally unique, in other words unique within the CTA
-   * catalogue.
-   * @return The archive file or nullptr.
-   * an empty list.
-   */
-  std::unique_ptr<common::dataStructures::ArchiveFile> getArchiveFileByDiskFileId(
-    rdbms::Conn &conn,
-    const std::string &diskInstance,
-    const std::string &diskFileId) const;
-
-  /**
-   * Returns the specified archive file.   A nullptr pointer is returned if
-   * there are no corresponding rows in the TAPE_FILE table.
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance.
-   * @param diskFileId The identifier of the source disk file which is unique
-   * within it's host disk system.  Two files from different disk systems may
-   * have the same identifier.  The combination of diskInstanceName and
-   * diskFileId must be globally unique, in other words unique within the CTA
-   * catalogue.
-   * @return The archive file or nullptr.
-   * an empty list.
-   */
-  std::unique_ptr<common::dataStructures::ArchiveFile> getArchiveFileToRetrieveByDiskFileId(
-    rdbms::Conn &conn,
-    const std::string &diskInstance,
-    const std::string &diskFileId) const;
-
-  /**
-   * Returns the mount policies for the specified requester and requester group.
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester and requester group belong.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   * @param requesterGroupName The name of the requester group which is only
-   * guaranteed to be unique within its disk instance.
-   * @return The mount policies.
-   */
-  RequesterAndGroupMountPolicies getMountPolicies(
-    rdbms::Conn &conn,
-    const std::string &diskInstanceName,
-    const std::string &requesterName,
-    const std::string &requesterGroupName) const;
-
-
-  /**
-   * Returns the mount policies for the specified requester, requester group and requester activity.
-   *
-   * @param conn The database connection.
-   * @param diskInstanceName The name of the disk instance to which the
-   * requester and requester group belong.
-   * @param requesterName The name of the requester which is only guaranteed to
-   * be unique within its disk instance.
-   * @param requesterGroupName The name of the requester group which is only
-   * guaranteed to be unique within its disk instance.
-   * @param activity The name of the activity to match the requester activity
-   * mount rules against
-   * @return The mount policies.
-   */
-  RequesterAndGroupMountPolicies getMountPolicies(
-    rdbms::Conn &conn,
-    const std::string &diskInstanceName,
-    const std::string &requesterName,
-    const std::string &requesterGroupName,
-    const std::string &activity) const;
-
-
-  /**
-   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
-   *
-   * @param conn The database connection.
-   * @param diskFileIds List of disk file IDs (fxid).
-   * @return Name of the temporary table
-   */
-  virtual std::string createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const = 0;
-
-  /**
-   * Returns a unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return A unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   */
-  virtual uint64_t getNextArchiveFileId(rdbms::Conn &conn) = 0;
-
-  /**
-   * Returns a unique logical library ID that can be used by a new logical
-   * library within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique logical library ID that can be used by a new logical
-   * library storage class within the catalogue.
-   */
-  virtual uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) = 0;
-
-  /**
-   * Returns a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection
-   * @return a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   */
-  virtual uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) = 0;
-
-  /**
-   * Returns a unique media type ID that can be used by a new media type within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique media type ID that can be used by a new media type
-   * within the catalogue.
-   */
-  virtual uint64_t getNextMediaTypeId(rdbms::Conn &conn) = 0;
-
-  /**
-   * Returns a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   */
-  virtual uint64_t getNextStorageClassId(rdbms::Conn &conn) = 0;
-
-  /**
-   * Returns a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   */
-  virtual uint64_t getNextTapePoolId(rdbms::Conn &conn) = 0;
-
-  /**
-   * Returns a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   */
-  virtual uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) = 0;
-
-  /**
-   * Returns a cached version of the mapping from tape copy to tape pool for the
-   * specified storage class.
-   *
-   * This method updates the cache when necessary.
-   *
-   * @param storageClass The fully qualified storage class, in other words the
-   * name of the disk instance and the name of the storage class.
-   * @return The mapping from tape copy to tape pool for the specified storage
-   * class.
-   */
-  common::dataStructures::TapeCopyToPoolMap getCachedTapeCopyToPoolMap(const StorageClass &storageClass) const;
-
-  /**
-   * Returns the mapping from tape copy to tape pool for the specified storage
-   * class.
-   *
-   * @param conn The database connection.
-   * @param storageClass The fully qualified storage class, in other words the
-   * name of the disk instance and the name of the storage class.
-   * @return The mapping from tape copy to tape pool for the specified storage
-   * class.
-   */
-  common::dataStructures::TapeCopyToPoolMap getTapeCopyToPoolMap(rdbms::Conn &conn,
-    const StorageClass &storageClass) const;
-
-  /**
-   * Throws an exception if one of the fields of the specified event have not
-   * been set.
-   *
-   * @param callingFunc The name of the calling function.
-   * @param event The evnt to be checked.
-   */
-  void checkTapeItemWrittenFieldsAreSet(const std::string &callingFunc, const TapeItemWritten &event) const;
-
-  /**
-   * Throws an exception if one of the fields of the specified event have not
-   * been set.
-   *
-   * @param callingFunc The name of the calling function.
-   * @param event The evnt to be checked.
-   */
-  void checkTapeFileWrittenFieldsAreSet(const std::string &callingFunc, const TapeFileWritten &event) const;
-
-  /**
-   * Throws an exception if the delete request passed in parameter is not consistent
-   * to allow a deletion of the ArchiveFile from the Catalogue.
-   * @param deleteRequest, the deleteRequest to check the consistency.
-   * @param archiveFile the ArchiveFile to delete to check the deleteRequest consistency against.
-   */
-  void checkDeleteRequestConsistency(const cta::common::dataStructures::DeleteArchiveRequest deleteRequest, const cta::common::dataStructures::ArchiveFile & archiveFile) const;
-
-  /**
-   * Returns a cached version of the result of calling isAdmin().
-   *
-   * @param admin The administrator.
-   * @return True if the specified user has administrator privileges.
-   */
-  bool isCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const;
-
-  /**
-   * Returns true if the specified user has administrator privileges.
-   *
-   * Please note that this method always queries the Catalogue database.
-   *
-   * @param admin The administrator.
-   * @return True if the specified user has administrator privileges.
-   */
-  bool isNonCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const;
-
-  /**
-   * Returns the number of tapes in the specified tape pool.
-   *
-   * If the tape pool does not exist then this method returns 0.
-   *
-   * @param conn The database connection.
-   * @param name The name of the tape pool.
-   * @return The number of tapes in the specified tape pool.
-   */
-  uint64_t getNbTapesInPool(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns true if the specified optional string is both set and empty.
-   *
-   * @param optionalStr The optional string.
-   * @return True if the specified optional string is both set and empty.
-   */
-  bool isSetAndEmpty(const std::optional<std::string> &optionalStr) const;
-
-  /**
-   * Returns true if the specified optional string list is both set and empty.
-   *
-   * @param optionalStr The optional string list.
-   * @return True if the specified optional string list is both set and empty.
-   */
-  bool isSetAndEmpty(const std::optional<std::vector<std::string>> &optionalStrList) const;
-
-  /**
-   * Returns true if the specified media type is currently being used by one or
-   * more archive tapes.
-   *
-   * @param conn The database connection.
-   * @param name The name of the media type
-   */
-  bool mediaTypeIsUsedByTapes(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns true if the specified storage class is currently being used by one
-   * or more archive routes.
-   *
-   * @param conn The database connection.
-   * @param storageClassName The name of the storage class.
-   */
-  bool storageClassIsUsedByArchiveRoutes(rdbms::Conn &conn, const std::string &storageClassName) const;
-
-  /**
-   * Returns true if the specified storage class is currently being used by one
-   * or more archive files.
-   *
-   * @param conn The database connection.
-   * @param storageClassName The name of the storage class.
-   */
-  bool storageClassIsUsedByArchiveFiles(rdbms::Conn &conn, const std::string &storageClassName) const;
-
-  /**
-   * Returns true if the specified storage class is currently being used by one
-   * or more files in the recycle log.
-   *
-   * @param conn The database connection.
-   * @param storageClassName The name of the storage class.
-   */
-  bool storageClassIsUsedByFileRecyleLogs(rdbms::Conn & conn, const std::string & storageClassName) const;
-
-  /**
-   * Returns true if the specified Virtual Organization is currently being used by one
-   * or more StorageClasses
-   *
-   * @param conn The database connection.
-   * @param voName The name of the Virtual Organization.
-   */
-  bool virtualOrganizationIsUsedByStorageClasses(rdbms::Conn &conn, const std::string &voName) const;
-
-  /**
-   * Returns true if the specified Virtual Organization is currently being used by one
-   * or more Tapepools
-   *
-   * @param conn The database connection.
-   * @param voName The name of the Virtual Organization.
-   */
-  bool virtualOrganizationIsUsedByTapepools(rdbms::Conn &conn, const std::string &voName) const;
-
-  /**
-   * Returns the ID of the specified logical library or std::nullopt if the logical
-   * library does not exist.
-   *
-   * @param conn The database connection.
-   * @param name The name of the tape pool.
-   * @return the ID of the specified tape pool.
-   */
-  std::optional<uint64_t> getLogicalLibraryId(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns the ID of the specified tape pool or std::nullopt if it the tape pool
-   * does not exist.
-   *
-   * @param conn The database connection.
-   * @param name The name of the tape pool.
-   * @return the ID of the specified tape pool.
-   */
-  std::optional<uint64_t> getTapePoolId(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Returns the ID of the specified media type or std::nullopt if it the media type
-   * does not exist.
-   *
-   * @param conn The database connection.
-   * @param name The name of the media type.
-   * @return the ID of the specified tape pool.
-   */
-  std::optional<uint64_t> getMediaTypeId(rdbms::Conn &conn, const std::string &name) const;
-
-  /**
-   * Updates the disk file ID of the specified archive file.
-   *
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param diskInstance The instance name of the source disk system.
-   * @param diskFileId The identifier of the source disk file which is unique
-   * within it's host disk system.  Two files from different disk systems may
-   * have the same identifier.  The combination of diskInstance and diskFileId
-   * must be globally unique, in other words unique within the CTA catalogue.
-   */
-  void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
-    const std::string &diskFileId) override;
-
-   /**
-   * Insert the ArchiveFile and all its tape files in the FILE_RECYCLE_LOG table.
-   * There will be one entry on the FILE_RECYCLE_LOG table per deleted tape file
-   *
-   * @param request the DeleteRequest object that holds information about the file to delete.
-   * @param lc the logContext
-   */
-  void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
-  log::LogContext & lc) override;
-
-  /**
-   * Copy the archiveFile and the associated tape files from the ARCHIVE_FILE and TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the ARCHIVE_FILE and TAPE_FILE entries.
-   * @param conn the database connection
-   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the FILE_RECYCLE_LOG table
-   * @param lc the log context
-   */
-  virtual void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) = 0;
-
-  /**
-   * Copy the fileRecycleLog to the TAPE_FILE and ARCHIVE_FILE (if the archive file no longer exists)
-   * table and deletes the corresponding FILE_RECYCLE_LOG table entry
-   * @param conn the database connection
-   * @param fileRecycleLog the fileRecycleLog we want to restore
-   * @param newFid The new eos file id of the archive file to create
-   * @param lc the log context
-   */
-  virtual void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) = 0;
-
-  /**
-   * Copies the ARCHIVE_FILE and TAPE_FILE entries to the recycle-bin tables
-   * @param conn the database connection
-   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the recycle-bin
-   */
-  void copyArchiveFileToFileRecycleLog(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest & request);
-
-  /**
-   * Copies the TAPE_FILE entries to the recycle-bin tables
-   * @param conn the database connection
-   * @param file the archiveFile whose tapefiles we want to copy
-   * @param reason The reason for deleting the tape file copy
-   */
-  void copyTapeFilesToFileRecycleLog(rdbms::Conn & conn, const common::dataStructures::ArchiveFile &file, const std::string &reason);
-
-  /**
-   * Copy the tape files from the TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the TAPE_FILE entry.
-   * @param conn the database connection
-   * @param file the archive file containing the tapefile to be copied
-   * @param reason The reason for deleting the tape file copy
-   * @param lc the log context
-   */
-  virtual void copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, log::LogContext & lc) = 0;
-
-  /**
-   * Insert the file in the FILE_RECYCLE_LOG table
-   * @param conn the database connection
-   * @param fileRecycleLog the file to insert into the FILE_RECYCLE_LOG table
-   */
-  void insertFileToRecycleLog(rdbms::Conn & conn, const InsertFileRecycleLog & fileRecycleLog);
-
-  /**
-   * Deletes the ArchiveFile from the ARCHIVE_FILE table
-   * @param conn the database connection
-   * @param request the DeleteArchiveRequest that contains the archiveFileId to delete
-   */
-  void deleteArchiveFile(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest & request);
-
-  /**
-   * Delete the TapeFile from the TAPE_FILE table
-   * @param conn the database connection
-   * @param request the DeleteArchiveRequest that contains the archiveFileId to delete the corresponding tape files
-   */
-  void deleteTapeFiles(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest & request);
-
-  /**
-   * Delete the TapeFiles associated to an ArchiveFile from the TAPE_FILE table
-   * @param conn the database connection
-   * @param file the file that contains the tape files to delete
-   */
-  void deleteTapeFiles(rdbms::Conn & conn, const common::dataStructures::ArchiveFile &file);
-
-   /**
-   * Set the DIRTY flag to true
-   * @param conn the database connection
-   * @param archiveFileId	the ArchiveFile that is going to be deleted and hence dirty the tape because
-   * the tape files will be removed from this tape
-   */
-  void setTapeDirty(rdbms::Conn & conn, const uint64_t & archiveFileId) const;
-
-  /**
-   * Delete the archiveFile and the associated tape files from the recycle-bin
-   * @param archiveFileId the archiveFileId of the archive file to delete
-   * @param lc the logContext
-   */
-  void deleteFileFromRecycleBin(const uint64_t archiveFileId, log::LogContext &lc);
-
-  /**
-   * Delete the archiveFile and the associated tape files that are on the specified tape from the recycle-bin
-   * @param vid the vid of the tape where the files to be deleted are located
-   * @param lc the logContext
-   */
-  void deleteFilesFromRecycleBin(rdbms::Conn & conn,const std::string & vid, log::LogContext & lc);
-
-  /**
-   * Deletes all the log entries corresponding to the vid passed in parameter.
-   *
-   * Please note that this method is idempotent.  If there are no recycle log
-   * entries associated to the vid passed in parameter, the method will return
-   * without any error.
-   *
-   * @param vid, the vid of the files to be deleted
-   * @param lc, the logContext
-   */
-  void deleteFilesFromRecycleLog(const std::string & vid, log::LogContext & lc);
-
-   /**
-   * Deletes all the log entries corresponding to the vid passed in parameter.
-   *
-   * Please note that this method is idempotent.  If there are no recycle log
-   * entries associated to the vid passed in parameter, the method will return
-   * without any error.
-   *
-    *@param conn, the database connection
-   * @param vid, the vid of the files to be deleted
-   * @param lc, the logContext
-   */
-  void deleteFilesFromRecycleLog(rdbms::Conn & conn, const std::string & vid, log::LogContext & lc);
-
-  /**
-   * Delete the TapeFiles and the ArchiveFile from the recycle-bin in one transaction
-   * @param conn the database connection
-   * @param archiveFileId the archiveFileId of the file to delete from the recycle-bin
-   */
-  virtual void deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn & conn, const uint64_t archiveFileId, log::LogContext & lc) = 0;
-
-  /**
-   * Delete the tape files from the TAPE_FILE recycle-bin
-   * @param conn the database connection
-   * @param archiveFileId the archiveFileId of the tape files to delete
-   */
-  void deleteTapeFilesFromRecycleBin(rdbms::Conn & conn, const uint64_t archiveFileId);
-
-  void deleteTapeFileCopyFromRecycleBin(cta::rdbms::Conn & conn, const common::dataStructures::FileRecycleLog fileRecycleLog);
-
-  /**
-   * Delete the archive file from the ARCHIVE_FILE recycle-bin
-   * @param conn the database connection
-   * @param archiveFileId the archiveFileId of the archive file to delete
-   */
-  void deleteArchiveFileFromRecycleBin(rdbms::Conn & conn, const uint64_t archiveFileId);
-
-  /**
-   * In the case we insert a TAPE_FILE that already has a copy on the catalogue (same copyNb),
-   * this TAPE_FILE will go to the FILE_RECYCLE_LOG table.
-   *
-   * This case happens always during the repacking of a tape: the new TAPE_FILE created
-   * will replace the old one, the old one will then be moved to the FILE_RECYCLE_LOG table
-   *
-   * @param conn The database connection.
-   * @returns the list of inserted fileRecycleLog
-   */
-  std::list<cta::catalogue::InsertFileRecycleLog> insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn & conn,const common::dataStructures::TapeFile &tapeFile, const uint64_t archiveFileId);
-
-  /**
-   * Insert the file passed in parameter in the FILE_RECYCLE_LOG table
-   * @param conn the database connection
-   * @param fileRecycleLog the file to insert on the FILE_RECYCLE_LOG table
-   */
-  void insertFileInFileRecycleLog(rdbms::Conn & conn, const InsertFileRecycleLog & fileRecycleLog);
-
-  /**
-   * Generates the SELECT statement required to search for tapes using 100 tape
-   * VIDs.  Each tape VID is represented in the SQL by a bind parameter with the
-   * naming convention of ":VN" where N is an integer from 1 to 100 with no
-   * padding.
-   */
-  std::string getSelectTapesBy100VidsSql() const;
-
-  /**
-   * Executes the specified "getTapesByVid" statement and collects the results into
-   * the specified vidToTapeMap.
-   *
-   * @param stmt "getTapesByVid" statement
-   * @param vidToTapeMap the map from VID to tape
-   */
-  void executeGetTapesByVidStmtAndCollectResults(rdbms::Stmt &stmt,
-    common::dataStructures::VidToTapeMap &vidToTapeMap) const;
-
-  /**
-   * Generates the SELECT statement required to search for VID to logical
-   * library name mappings using 100 tape VIDs.  Each tape VID is represented in
-   * the SQL by a bind parameter with the  naming convention of ":VN" where N is
-   * an integer from 1 to 100 with no padding.
-   */
-  std::string getSelectVidToLogicalLibraryBy100Sql() const;
-
-  /**
-   * Executes the specified "getVidToLogicalLibraryBy100" statement and collects the
-   * results into the specified vidToLogicalLibrary map.
-   * @param stmt the "getVidToLogicalLibraryBy100" statement.
-   * @param vidLogicalLibrary the map from VID to logical library name.
-   */
-  void executeGetVidToLogicalLibraryBy100StmtAndCollectResults(rdbms::Stmt &stmt,
-    std::map<std::string, std::string> &vidToLogicalLibrary) const;
-
-  /**
-   * Returns an iterator across the files on the specified tape ordered by
-   * FSEQ.
-   *
-   * @param vid The volume identifier of the tape.
-   * @return The iterator.
-   */
-  ArchiveFileItor getTapeContentsItor(const std::string &vid) const;
-
-  void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) override;
-
-  std::list<std::string> getTapeDriveNames() const override;
-
-  std::list<common::dataStructures::TapeDrive> getTapeDrives() const override;
-
-  std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const override;
-
-  void setDesiredTapeDriveState(const std::string& tapeDriveName,
-    const common::dataStructures::DesiredDriveState &desiredState) override;
-
-  void setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
-    const std::string &comment) override;
-
-  void updateTapeDriveStatistics(const std::string& tapeDriveName,
-    const std::string& host, const std::string& logicalLibrary,
-    const common::dataStructures::TapeDriveStatistics& statistics) override;
-
-  void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive);
-
-  void deleteTapeDrive(const std::string &tapeDriveName) override;
-
-  void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
-    const std::string &keyName, const std::string &value, const std::string &source) override;
-
-  std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const override;
-
-  void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
-    const std::string &keyName, const std::string &value, const std::string &source) override;
-
-  std::list<cta::catalogue::Catalogue::DriveConfig> getTapeDriveConfigs() const override;
-
-  std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig( const std::string &tapeDriveName,
-    const std::string &keyName) const override;
-
-  void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) override;
-
-  std::map<std::string, uint64_t> getDiskSpaceReservations() const override;
-
-  void reserveDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
-
-  void releaseDiskSpace(const std::string& driveName, const uint64_t mountId, const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
-
-
-  /**
-   * Cached versions of tape copy to tape tape pool mappings for specific
-   * storage classes.
-   */
-  mutable TimeBasedCache<StorageClass, common::dataStructures::TapeCopyToPoolMap> m_tapeCopyToPoolCache;
-
-  /**
-   * Cached versions of mount policies for specific user groups.
-   */
-  mutable TimeBasedCache<Group, std::optional<common::dataStructures::MountPolicy> > m_groupMountPolicyCache;
-
-  /**
-   * Cached versions of mount policies for specific users.
-   */
-  mutable TimeBasedCache<User, std::optional<common::dataStructures::MountPolicy> > m_userMountPolicyCache;
-
-  /**
-   * Cached versions of all mount policies
-   */
-  mutable TimeBasedCache<std::string, std::list<common::dataStructures::MountPolicy>> m_allMountPoliciesCache;
-
-  /**
-   * Cached versions of virtual organization for specific tapepools
-   */
-  mutable TimeBasedCache<std::string, common::dataStructures::VirtualOrganization> m_tapepoolVirtualOrganizationCache;
-
-  /**
-   * Cached versions of the expected number of archive routes for specific
-   * storage classes as specified by the call to the createStorageClass()
-   * method as opposed to the actual number entered so far using the
-   * createArchiveRoute() method.
-   */
-  mutable TimeBasedCache<StorageClass, uint64_t> m_expectedNbArchiveRoutesCache;
-
-  /**
-   * Cached version of isAdmin() results.
-   */
-  mutable TimeBasedCache<common::dataStructures::SecurityIdentity, bool> m_isAdminCache;
-
-private:
-  void settingSqlTapeDriveValues(cta::rdbms::Stmt *stmt, const common::dataStructures::TapeDrive &tapeDrive) const;
-
-  common::dataStructures::TapeDrive gettingSqlTapeDriveValues(cta::rdbms::Rset* rset) const;
-
-  void checkCommentOrReasonMaxLength(const std::optional<std::string>& comment) const;
-}; // class RdbmsCatalogue
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/SchemaCreatingSqliteCatalogue.cpp b/catalogue/SchemaCreatingSqliteCatalogue.cpp
index b50ecaaa4e..fc486cb12b 100644
--- a/catalogue/SchemaCreatingSqliteCatalogue.cpp
+++ b/catalogue/SchemaCreatingSqliteCatalogue.cpp
@@ -15,9 +15,11 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "catalogue/SqliteCatalogueSchema.hpp"
 #include "catalogue/SchemaCreatingSqliteCatalogue.hpp"
+#include "catalogue/SqliteCatalogueSchema.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
 #include "common/utils/utils.hpp"
+#include "rdbms/ConnPool.hpp"
 
 namespace cta {
 namespace catalogue {
@@ -44,7 +46,7 @@ SchemaCreatingSqliteCatalogue::SchemaCreatingSqliteCatalogue(
 void SchemaCreatingSqliteCatalogue::createCatalogueSchema() {
   try {
     const SqliteCatalogueSchema schema;
-    auto conn = m_connPool.getConn();
+    auto conn = m_connPool->getConn();
     executeNonQueries(conn, schema.sql);
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
diff --git a/catalogue/SchemaCreatingSqliteCatalogue.hpp b/catalogue/SchemaCreatingSqliteCatalogue.hpp
index 649e6863a6..53a8813a0d 100644
--- a/catalogue/SchemaCreatingSqliteCatalogue.hpp
+++ b/catalogue/SchemaCreatingSqliteCatalogue.hpp
@@ -17,7 +17,7 @@
 
 #pragma once
 
-#include "catalogue/SqliteCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteCatalogue.hpp"
 
 namespace cta {
 namespace catalogue {
diff --git a/catalogue/SqliteCatalogue.cpp b/catalogue/SqliteCatalogue.cpp
deleted file mode 100644
index 6a37ddd140..0000000000
--- a/catalogue/SqliteCatalogue.cpp
+++ /dev/null
@@ -1,780 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#include "catalogue/ArchiveFileRow.hpp"
-#include "catalogue/ArchiveFileRowWithoutTimestamps.hpp"
-#include "catalogue/CatalogueItor.hpp"
-#include "catalogue/SqliteCatalogue.hpp"
-#include "catalogue/SqliteCatalogueSchema.hpp"
-#include "catalogue/TapeItemWrittenPointer.hpp"
-#include "common/dataStructures/DeleteArchiveRequest.hpp"
-#include "common/dataStructures/FileRecycleLog.hpp"
-#include "common/exception/Exception.hpp"
-#include "common/exception/FileSizeMismatch.hpp"
-#include "common/exception/TapeFseqMismatch.hpp"
-#include "common/exception/UserError.hpp"
-#include "common/log/TimingList.hpp"
-#include "common/threading/MutexLocker.hpp"
-#include "common/Timer.hpp"
-#include "common/utils/utils.hpp"
-#include "rdbms/AutoRollback.hpp"
-#include "rdbms/ConstraintError.hpp"
-#include "rdbms/PrimaryKeyError.hpp"
-
-namespace cta {
-namespace catalogue {
-
-//------------------------------------------------------------------------------
-// constructor
-//------------------------------------------------------------------------------
-SqliteCatalogue::SqliteCatalogue(
-  log::Logger &log,
-  const std::string &filename,
-  const uint64_t nbConns,
-  const uint64_t nbArchiveFileListingConns):
-  RdbmsCatalogue(
-    log,
-    rdbms::Login(rdbms::Login::DBTYPE_SQLITE, "", "", filename, "", 0),
-    nbConns,
-    nbArchiveFileListingConns) {
-}
-
-//------------------------------------------------------------------------------
-// destructor
-//------------------------------------------------------------------------------
-SqliteCatalogue::~SqliteCatalogue() {
-}
-
-//------------------------------------------------------------------------------
-// deleteArchiveFile
-//------------------------------------------------------------------------------
-void SqliteCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
-  log::LogContext &lc) {
-  try {
-    utils::Timer t;
-    auto conn = m_connPool.getConn();
-    const auto getConnTime = t.secs();
-    rdbms::AutoRollback autoRollback(conn);
-    t.reset();
-    const auto archiveFile = getArchiveFileById(conn, archiveFileId);
-    const auto getArchiveFileTime = t.secs();
-
-    if(nullptr == archiveFile.get()) {
-      log::ScopedParamContainer spc(lc);
-      spc.add("fileId", archiveFileId);
-      lc.log(log::WARNING, "Ignoring request to delete archive file because it does not exist in the catalogue");
-      return;
-    }
-
-    if(diskInstanceName != archiveFile->diskInstance) {
-      log::ScopedParamContainer spc(lc);
-      spc.add("fileId", std::to_string(archiveFile->archiveFileID))
-         .add("diskInstance", archiveFile->diskInstance)
-         .add("requestDiskInstance", diskInstanceName)
-         .add("diskFileId", archiveFile->diskFileId)
-         .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
-         .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
-         .add("fileSize", std::to_string(archiveFile->fileSize))
-         .add("creationTime", std::to_string(archiveFile->creationTime))
-         .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
-         .add("storageClass", archiveFile->storageClass)
-         .add("getConnTime", getConnTime)
-         .add("getArchiveFileTime", getArchiveFileTime);
-      archiveFile->checksumBlob.addFirstChecksumToLog(spc);
-      for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
-        std::stringstream tapeCopyLogStream;
-        tapeCopyLogStream << "copy number: " << it->copyNb
-          << " vid: " << it->vid
-          << " fSeq: " << it->fSeq
-          << " blockId: " << it->blockId
-          << " creationTime: " << it->creationTime
-          << " fileSize: " << it->fileSize
-          << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
-          << " copyNb: " << it->copyNb; //this shouldn't be here: repeated field
-        spc.add("TAPE FILE", tapeCopyLogStream.str());
-      }
-      lc.log(log::WARNING, "Failed to delete archive file because the disk instance of the request does not match that "
-        "of the archived file");
-
-      exception::UserError ue;
-      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because the disk instance of "
-        "the request does not match that of the archived file: archiveFileId=" << archiveFileId << " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" <<
-        archiveFile->diskInstance;
-      throw ue;
-    }
-
-    t.reset();
-    {
-      const char *const sql = "BEGIN DEFERRED;";
-      auto stmt = conn.createStmt(sql);
-      stmt.executeNonQuery();
-    }
-    {
-      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-      stmt.executeNonQuery();
-    }
-
-    const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter);
-
-    std::set<std::string> vidsToSetDirty;
-    //We will insert the vids to set dirty in a set so that
-    //we limit the calls to setTapeDirty to the number of tapes that contained the deleted tape files
-    for(auto &tapeFile: archiveFile->tapeFiles){
-      vidsToSetDirty.insert(tapeFile.vid);
-    }
-
-    for(auto &vidToSetDirty: vidsToSetDirty){
-       setTapeDirty(conn,vidToSetDirty);
-    }
-
-    const auto setTapeDirtyTime = t.secs(utils::Timer::resetCounter);
-
-    {
-      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;";
-      auto stmt = conn.createStmt(sql);
-      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
-      stmt.executeNonQuery();
-    }
-    const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter);
-
-    conn.commit();
-    const auto commitTime = t.secs();
-
-    log::ScopedParamContainer spc(lc);
-    spc.add("fileId", std::to_string(archiveFile->archiveFileID))
-       .add("diskInstance", archiveFile->diskInstance)
-       .add("diskFileId", archiveFile->diskFileId)
-       .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
-       .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
-       .add("fileSize", std::to_string(archiveFile->fileSize))
-       .add("creationTime", std::to_string(archiveFile->creationTime))
-       .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
-       .add("storageClass", archiveFile->storageClass)
-       .add("getConnTime", getConnTime)
-       .add("getArchiveFileTime", getArchiveFileTime)
-       .add("deleteFromTapeFileTime", deleteFromTapeFileTime)
-       .add("deleteFromArchiveFileTime", deleteFromArchiveFileTime)
-       .add("setTapeDirtyTime",setTapeDirtyTime)
-       .add("commitTime", commitTime);
-    archiveFile->checksumBlob.addFirstChecksumToLog(spc);
-    for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
-      std::stringstream tapeCopyLogStream;
-      tapeCopyLogStream << "copy number: " << it->copyNb
-        << " vid: " << it->vid
-        << " fSeq: " << it->fSeq
-        << " blockId: " << it->blockId
-        << " creationTime: " << it->creationTime
-        << " fileSize: " << it->fileSize
-        << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
-        << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
-      spc.add("TAPE FILE", tapeCopyLogStream.str());
-    }
-    lc.log(log::INFO, "Archive file deleted from CTA catalogue");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// createAndPopulateTempTableFxid
-//------------------------------------------------------------------------------
-std::string SqliteCatalogue::createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const {
-  try {
-    const std::string tempTableName = "TEMP.DISK_FXIDS";
-
-    // Drop any prexisting temporary table and create a new one
-    conn.executeNonQuery("DROP TABLE IF EXISTS " + tempTableName);
-    conn.executeNonQuery("CREATE TEMPORARY TABLE " + tempTableName + "(DISK_FILE_ID TEXT)");
-
-    if(diskFileIds) {
-      auto stmt = conn.createStmt("INSERT INTO " + tempTableName + " VALUES(:DISK_FILE_ID)");
-      for(auto &diskFileId : diskFileIds.value()) {
-        stmt.bindString(":DISK_FILE_ID", diskFileId);
-        stmt.executeNonQuery();
-      }
-    }
-
-    return tempTableName;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextArchiveFileId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextArchiveFileId(rdbms::Conn &conn) {
-  try {
-    conn.executeNonQuery("INSERT INTO ARCHIVE_FILE_ID VALUES(NULL)");
-    uint64_t archiveFileId = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      archiveFileId = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM ARCHIVE_FILE_ID");
-
-    return archiveFileId;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextLogicalLibraryId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
-  try {
-    conn.executeNonQuery("INSERT INTO LOGICAL_LIBRARY_ID VALUES(NULL)");
-    uint64_t logicalLibraryId = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      logicalLibraryId = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM LOGICAL_LIBRARY_ID");
-
-    return logicalLibraryId;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextVirtualOrganizationId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
-try {
-    conn.executeNonQuery("INSERT INTO VIRTUAL_ORGANIZATION_ID VALUES(NULL)");
-    uint64_t virtualOrganizationId = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      virtualOrganizationId = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM VIRTUAL_ORGANIZATION_ID");
-
-    return virtualOrganizationId;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextMediaTypeId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextMediaTypeId(rdbms::Conn &conn) {
-  try {
-    conn.executeNonQuery("INSERT INTO MEDIA_TYPE_ID VALUES(NULL)");
-    uint64_t id = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      id = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM MEDIA_TYPE_ID");
-
-    return id;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextStorageClassId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextStorageClassId(rdbms::Conn &conn) {
-  try {
-    conn.executeNonQuery("INSERT INTO STORAGE_CLASS_ID VALUES(NULL)");
-    uint64_t storageClassId = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      storageClassId = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM STORAGE_CLASS_ID");
-
-    return storageClassId;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextTapePoolId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextTapePoolId(rdbms::Conn &conn) {
-  try {
-    conn.executeNonQuery("INSERT INTO TAPE_POOL_ID VALUES(NULL)");
-    uint64_t tapePoolId = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      tapePoolId = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM TAPE_POOL_ID");
-
-    return tapePoolId;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getNextFileRecyleLogId
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getNextFileRecyleLogId(rdbms::Conn &conn) {
-  try {
-    conn.executeNonQuery("INSERT INTO FILE_RECYCLE_LOG_ID VALUES(NULL)");
-    uint64_t fileRecycleLogId = 0;
-    {
-      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
-      auto stmt = conn.createStmt(sql);
-      auto rset = stmt.executeQuery();
-      if(!rset.next()) {
-        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
-      }
-      fileRecycleLogId = rset.columnUint64("ID");
-      if(rset.next()) {
-        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
-      }
-    }
-    conn.executeNonQuery("DELETE FROM FILE_RECYCLE_LOG_ID");
-
-    return fileRecycleLogId;
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// getTapeLastFSeq
-//------------------------------------------------------------------------------
-uint64_t SqliteCatalogue::getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) {
-  try {
-    const char *const sql =
-      "SELECT "
-        "LAST_FSEQ AS LAST_FSEQ "
-      "FROM "
-        "TAPE "
-      "WHERE "
-        "VID = :VID;";
-
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":VID", vid);
-    auto rset = stmt.executeQuery();
-    if (!rset.next()) {
-      throw exception::Exception(std::string("The tape with VID " + vid + " does not exist"));
-    }
-
-    return rset.columnUint64("LAST_FSEQ");
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// filesWrittenToTape
-//------------------------------------------------------------------------------
-void SqliteCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) {
-  try {
-    if(events.empty()) {
-      return;
-    }
-
-    auto firstEventItor = events.cbegin();
-    const auto &firstEvent = **firstEventItor;;
-    checkTapeItemWrittenFieldsAreSet(__FUNCTION__, firstEvent);
-
-    // The SQLite implementation of this method relies on the fact that a tape
-    // cannot be physically mounted in two or more drives at the same time
-    //
-    // Given the above assumption regarding the laws of physics, a simple lock
-    // on the mutex of the SqliteCatalogue object is enough to emulate an
-    // Oracle SELECT FOR UPDATE
-    threading::MutexLocker locker(m_mutex);
-    auto conn = m_connPool.getConn();
-
-    const uint64_t lastFSeq = getTapeLastFSeq(conn, firstEvent.vid);
-    uint64_t expectedFSeq = lastFSeq + 1;
-    uint64_t totalLogicalBytesWritten = 0;
-    uint64_t filesCount = 0;
-
-    for(const auto &eventP: events) {
-      const auto & event = *eventP;
-      checkTapeItemWrittenFieldsAreSet(__FUNCTION__, event);
-
-      if(event.vid != firstEvent.vid) {
-        throw exception::Exception(std::string("VID mismatch: expected=") + firstEvent.vid + " actual=" + event.vid);
-      }
-
-      if(expectedFSeq != event.fSeq) {
-        exception::TapeFseqMismatch ex;
-        ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
-          firstEvent.fSeq;
-        throw ex;
-      }
-      expectedFSeq++;
-
-
-      try {
-        // If this is a file (as opposed to a placeholder), do the full processing.
-        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(event);
-        totalLogicalBytesWritten += fileEvent.size;
-        filesCount++;
-      } catch (std::bad_cast&) {}
-    }
-
-    auto lastEventItor = events.cend();
-    lastEventItor--;
-    const TapeItemWritten &lastEvent = **lastEventItor;
-    updateTape(conn, lastEvent.vid, lastEvent.fSeq, totalLogicalBytesWritten, filesCount, lastEvent.tapeDrive);
-
-    for(const auto &event : events) {
-      try {
-        // If this is a file (as opposed to a placeholder), do the full processing.
-        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(*event);
-        fileWrittenToTape(conn, fileEvent);
-      } catch (std::bad_cast&) {}
-    }
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// fileWrittenToTape
-//------------------------------------------------------------------------------
-void SqliteCatalogue::fileWrittenToTape(rdbms::Conn &conn, const TapeFileWritten &event) {
-  try {
-    checkTapeFileWrittenFieldsAreSet(__FUNCTION__, event);
-
-    // Try to insert a row into the ARCHIVE_FILE table - it is normal this will
-    // fail if another tape copy has already been written to tape
-    try {
-      ArchiveFileRowWithoutTimestamps row;
-      row.archiveFileId = event.archiveFileId;
-      row.diskFileId = event.diskFileId;
-      row.diskInstance = event.diskInstance;
-      row.size = event.size;
-      row.checksumBlob = event.checksumBlob;
-      row.storageClassName = event.storageClassName;
-      row.diskFileOwnerUid = event.diskFileOwnerUid;
-      row.diskFileGid = event.diskFileGid;
-      insertArchiveFile(conn, row);
-    } catch(rdbms::PrimaryKeyError &) {
-      // Ignore this error
-    } catch(...) {
-      throw;
-    }
-
-    const time_t now = time(nullptr);
-    const auto archiveFileRow = getArchiveFileRowById(conn, event.archiveFileId);
-
-    if(nullptr == archiveFileRow) {
-      // This should never happen
-      exception::Exception ex;
-      ex.getMessage() << "Failed to find archive file row: archiveFileId=" << event.archiveFileId;
-      throw ex;
-    }
-
-    std::ostringstream fileContext;
-    fileContext << "archiveFileId=" << event.archiveFileId << ", diskInstanceName=" << event.diskInstance <<
-      ", diskFileId=" << event.diskFileId;
-
-    if(archiveFileRow->size != event.size) {
-      catalogue::FileSizeMismatch ex;
-      ex.getMessage() << "File size mismatch: expected=" << archiveFileRow->size << ", actual=" << event.size << ": "
-        << fileContext.str();
-      throw ex;
-    }
-
-    archiveFileRow->checksumBlob.validate(event.checksumBlob);
-
-    // Insert the tape file
-    common::dataStructures::TapeFile tapeFile;
-    tapeFile.vid            = event.vid;
-    tapeFile.fSeq           = event.fSeq;
-    tapeFile.blockId        = event.blockId;
-    tapeFile.fileSize       = event.size;
-    tapeFile.copyNb         = event.copyNb;
-    tapeFile.creationTime   = now;
-    insertTapeFile(conn, tapeFile, event.archiveFileId);
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// copyArchiveFileToRecycleBinAndDelete
-//------------------------------------------------------------------------------
-void SqliteCatalogue::copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do an INSERT INTO and a DELETE FROM
-    //in a single transaction
-    conn.executeNonQuery("BEGIN TRANSACTION");
-    copyArchiveFileToFileRecycleLog(conn,request);
-    tl.insertAndReset("insertToRecycleBinTime",t);
-    setTapeDirty(conn,request.archiveFileID);
-    tl.insertAndReset("setTapeDirtyTime",t);
-    deleteTapeFiles(conn,request);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    RdbmsCatalogue::deleteArchiveFile(conn,request);
-    tl.insertAndReset("deleteArchiveFileTime",t);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId",request.archiveFileID);
-    spc.add("diskFileId",request.diskFileId);
-    spc.add("diskFilePath",request.diskFilePath);
-    spc.add("diskInstance",request.diskInstance);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In SqliteCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// deleteTapeFilesAndArchiveFileFromRecycleBin
-//------------------------------------------------------------------------------
-void SqliteCatalogue::deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn& conn, const uint64_t archiveFileId, log::LogContext& lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do two delete in one transaction
-    conn.executeNonQuery("BEGIN TRANSACTION");
-    deleteTapeFilesFromRecycleBin(conn,archiveFileId);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    deleteArchiveFileFromRecycleBin(conn,archiveFileId);
-    tl.insertAndReset("deleteArchiveFileTime",t);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId",archiveFileId);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In SqliteCatalogue::deleteTapeFilesAndArchiveFileFromRecycleBin: tape files and archiveFiles deleted from the recycle-bin.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// copyTapeFileToFileRecyleLogAndDelete
-//------------------------------------------------------------------------------
-void SqliteCatalogue::copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file,
-                                                          const std::string &reason, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    //We currently do an INSERT INTO and a DELETE FROM
-    //in a single transaction
-    conn.executeNonQuery("BEGIN TRANSACTION");
-    copyTapeFilesToFileRecycleLog(conn, file, reason);
-    tl.insertAndReset("insertToRecycleBinTime",t);
-    setTapeDirty(conn, file.archiveFileID);
-    tl.insertAndReset("setTapeDirtyTime",t);
-    deleteTapeFiles(conn,file);
-    tl.insertAndReset("deleteTapeFilesTime",t);
-    conn.commit();
-    tl.insertAndReset("commitTime",t);
-    log::ScopedParamContainer spc(lc);
-    spc.add("archiveFileId", file.archiveFileID);
-    spc.add("diskFileId", file.diskFileId);
-    spc.add("diskFilePath", file.diskFileInfo.path);
-    spc.add("diskInstance", file.diskInstance);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In SqliteCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
-
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreEntryInRecycleLog
-//------------------------------------------------------------------------------
-void SqliteCatalogue::restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-
-    if (!fileRecycleLogItor.hasMore()) {
-      throw cta::exception::UserError("No file in the recycle bin matches the parameters passed");
-    }
-    auto fileRecycleLog = fileRecycleLogItor.next();
-    if (fileRecycleLogItor.hasMore()) {
-      //stop restoring more than one file at once
-      throw cta::exception::UserError("More than one recycle bin file matches the parameters passed");
-    }
-
-    conn.executeNonQuery("BEGIN TRANSACTION");
-
-    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFilePtr = getArchiveFileById(conn, fileRecycleLog.archiveFileId);
-    if (!archiveFilePtr) {
-      restoreArchiveFileInRecycleLog(conn, fileRecycleLog, newFid, lc);
-    } else {
-      if (archiveFilePtr->tapeFiles.find(fileRecycleLog.copyNb) != archiveFilePtr->tapeFiles.end()) {
-        //copy with same copy_nb exists, cannot restore
-        UserSpecifiedExistingDeletedFileCopy ex;
-        ex.getMessage() << "Cannot restore file copy with archiveFileId " << std::to_string(fileRecycleLog.archiveFileId)
-        << " and copy_nb " << std::to_string(fileRecycleLog.copyNb) << " because a tapefile with same archiveFileId and copy_nb already exists";
-        throw ex;
-      }
-    }
-
-    restoreFileCopyInRecycleLog(conn, fileRecycleLog, lc);
-    conn.commit();
-
-    log::ScopedParamContainer spc(lc);
-    tl.insertAndReset("commitTime",t);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In SqliteCatalogue::restoreEntryInRecycleLog: all file copies successfully restored.");
-  } catch(exception::UserError &) {
-      throw;
-    } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-//------------------------------------------------------------------------------
-// restoreFileCopyInRecycleLog
-//------------------------------------------------------------------------------
-void SqliteCatalogue::restoreFileCopyInRecycleLog(rdbms::Conn &conn, const common::dataStructures::FileRecycleLog &fileRecycleLog, log::LogContext & lc) {
-  try {
-    utils::Timer t;
-    log::TimingList tl;
-    cta::common::dataStructures::TapeFile tapeFile;
-    tapeFile.vid = fileRecycleLog.vid;
-    tapeFile.fSeq = fileRecycleLog.fSeq;
-    tapeFile.copyNb = fileRecycleLog.copyNb;
-    tapeFile.blockId = fileRecycleLog.blockId;
-    tapeFile.fileSize = fileRecycleLog.sizeInBytes;
-    tapeFile.creationTime = fileRecycleLog.tapeFileCreationTime;
-
-    insertTapeFile(conn, tapeFile, fileRecycleLog.archiveFileId);
-    tl.insertAndReset("insertTapeFileTime",t);
-
-    deleteTapeFileCopyFromRecycleBin(conn, fileRecycleLog);
-    tl.insertAndReset("deleteTapeFileCopyFromRecycleBinTime",t);
-
-    log::ScopedParamContainer spc(lc);
-    spc.add("vid", tapeFile.vid);
-    spc.add("archiveFileId", fileRecycleLog.archiveFileId);
-    spc.add("fSeq", tapeFile.fSeq);
-    spc.add("copyNb", tapeFile.copyNb);
-    spc.add("fileSize", tapeFile.fileSize);
-    tl.addToLog(spc);
-    lc.log(log::INFO,"In SqliteCatalogue::restoreFileCopyInRecycleLog: File restored from the recycle log.");
-  } catch(exception::UserError &) {
-    throw;
-  } catch(exception::Exception &ex) {
-    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
-    throw;
-  }
-}
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/SqliteCatalogue.hpp b/catalogue/SqliteCatalogue.hpp
deleted file mode 100644
index af0d0072cd..0000000000
--- a/catalogue/SqliteCatalogue.hpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
- *               option) any later version.
- *
- *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
- *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-#include "catalogue/RdbmsCatalogue.hpp"
-
-namespace cta {
-namespace catalogue {
-
-class CatalogueFactory;
-
-/**
- * An SQLite implementation of the CTA catalogue.
- */
-class SqliteCatalogue: public RdbmsCatalogue {
-public:
-
-  /**
-   * Constructor.
-   *
-   * @param log Object representing the API to the CTA logging system.
-   * @param filename The filename to be passed to the sqlite3_open() function.
-   * @param nbConns The maximum number of concurrent connections to the
-   * underlying relational database for all operations accept listing archive
-   * files which can be relatively long operations.
-   * @param nbArchiveFileListingConns The maximum number of concurrent
-   * connections to the underlying relational database for the sole purpose of
-   * listing archive files.
-   */
-  SqliteCatalogue(
-    log::Logger &log,
-    const std::string &filename,
-    const uint64_t nbConns,
-    const uint64_t nbArchiveFileListingConns);
-
-public:
-
-  /**
-   * Destructor.
-   */
-  ~SqliteCatalogue() override;
-
-  /**
-   * !!!!!!!!!!!!!!!!!!! THIS METHOD SHOULD NOT BE USED !!!!!!!!!!!!!!!!!!!!!!!
-   * SqliteCatalogue implements its own version of deleteArchiveFile() because
-   * SQLite does not supprt 'SELECT FOR UPDATE'.
-   *
-   * Deletes the specified archive file and its associated tape copies from the
-   * catalogue.
-   *
-   * Please note that the name of the disk instance is specified in order to
-   * prevent a disk instance deleting an archive file that belongs to another
-   * disk instance.
-   *
-   * Please note that this method is idempotent.  If the file to be deleted does
-   * not exist in the CTA catalogue then this method returns without error.
-   *
-   * @param instanceName The name of the instance from where the deletion request
-   * originated
-   * @param archiveFileId The unique identifier of the archive file.
-   * @param lc The log context.
-   * @return The metadata of the deleted archive file including the metadata of
-   * the associated and also deleted tape copies.
-   */
-  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId, log::LogContext &lc)
-    override;
-
-protected:
-
-  /**
-   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
-   *
-   * @param conn The database connection.
-   * @param diskFileIds List of disk file IDs (fxid).
-   * @return Name of the temporary table
-   */
-  std::string createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const override;
-
-  /**
-   * Returns a unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * PLEASE NOTE the SQLite implemenation of getNextArchiveFileId() takes a lock
-   * on m_mutex in order to serialize access to the SQLite database.  This has
-   * been done in an attempt to avoid SQLite busy errors.
-   *
-   * @param conn The database connection.
-   * @return A unique archive ID that can be used by a new archive file within
-   * the catalogue.
-   */
-  uint64_t getNextArchiveFileId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique logical library ID that can be used by a new logical
-   * library within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique logical library ID that can be used by a new logical
-   * library storage class within the catalogue.
-   */
-  uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
-  
-  /**
-   * Returns a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   * 
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   * 
-   * @param conn The database connection
-   * @return a unique virtual organization ID that can be used by a new Virtual Organization
-   * within the catalogue.
-   */
-  uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique media type ID that can be used by a new media type within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique media type ID that can be used by a new media type
-   * within the catalogue.
-   */
-  uint64_t getNextMediaTypeId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique storage class ID that can be used by a new storage class
-   * within the catalogue.
-   */
-  uint64_t getNextStorageClassId(rdbms::Conn &conn) override;
-
-  /**
-   * Returns a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique tape pool ID that can be used by a new tape pool within
-   * the catalogue.
-   */
-  uint64_t getNextTapePoolId(rdbms::Conn &conn) override;
-  
-  /**
-   * Returns a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   *
-   * This method must be implemented by the sub-classes of RdbmsCatalogue
-   * because different database technologies propose different solution to the
-   * problem of generating ever increasing numeric identifiers.
-   *
-   * @param conn The database connection.
-   * @return a unique file recycle log ID that can be used by a new entry of file recycle log within
-   * the catalogue.
-   */
-  uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) override;
-
-  /**
-   * Notifies the catalogue that the specified files have been written to tape.
-   *
-   * @param events The tape file written events.
-   */
-  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) override;
-  
-  /**
-   * Copy the archiveFile and the associated tape files from the ARCHIVE_FILE and TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the ARCHIVE_FILE and TAPE_FILE entries.
-   * @param conn the database connection
-   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the FILE_RECYCLE_LOG table
-   * @param lc the log context
-   */
-  void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
-  
-  /**
-   * Delete the TapeFiles and the ArchiveFile from the recycle-bin in one transaction
-   * @param conn the database connection
-   * @param archiveFileId the archiveFileId of the file to delete from the recycle-bin
-   */
-  void deleteTapeFilesAndArchiveFileFromRecycleBin(rdbms::Conn& conn, const uint64_t archiveFileId, log::LogContext& lc) override;
-  
-  /**
-   * Copy the tape files from the TAPE_FILE tables to the FILE_RECYCLE_LOG table
-   * and deletes the TAPE_FILE entry.
-   * @param conn the database connection
-   * @param file the file to be deleted
-   * @param reason The reason for deleting the tape file copy
-   * @param lc the log context
-   */
-  void copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn, const cta::common::dataStructures::ArchiveFile &file, 
-                                            const std::string &reason, log::LogContext & lc) override;
-
-  /**
-   * Copy the files in fileRecycleLogItor to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entries
-   * @param conn the database connection
-   * @param fileRecycleLogItor the collection of fileRecycleLogs we want to restore
-   * @param lc the log context
-   */
-  void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) override;
-
-  /**
-   * Copy the fileRecycleLog to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entry
-   * @param conn the database connection
-   * @param fileRecycleLog the fileRecycleLog we want to restore
-   * @param lc the log context
-   */
-  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLogItor, log::LogContext & lc);
-
-
-private:
-
-  /**
-   * Notifies the catalogue that a file has been written to tape.
-   *
-   * @param conn The database connection.
-   * @param event The tape file written event.
-   */
-  void fileWrittenToTape(rdbms::Conn &conn, const TapeFileWritten &event);
-
-  /**
-   * Gets the last FSeq of the specified tape.
-   *
-   * @param conn The database connection.
-   * @param vid The volume identifier of the tape.
-   * @return The last FSeq of the tape.
-   */
-  uint64_t getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid);
-
-}; // class SqliteCatalogue
-
-} // namespace catalogue
-} // namespace cta
diff --git a/catalogue/TapeDrivesCatalogueState.cpp b/catalogue/TapeDrivesCatalogueState.cpp
index 59068a2924..2e842a2333 100644
--- a/catalogue/TapeDrivesCatalogueState.cpp
+++ b/catalogue/TapeDrivesCatalogueState.cpp
@@ -39,22 +39,22 @@ void TapeDrivesCatalogueState::createTapeDriveStatus(const common::dataStructure
   const common::dataStructures::DriveStatus& status, const tape::daemon::TpconfigLine& tpConfigLine,
   const common::dataStructures::SecurityIdentity& identity, log::LogContext & lc) {
   auto tapeDriveStatus = setTapeDriveStatus(driveInfo, desiredState, type, status, tpConfigLine, identity);
-  auto driveNames = m_catalogue.getTapeDriveNames();
+  auto driveNames = m_catalogue.DriveState()->getTapeDriveNames();
   auto it = std::find(driveNames.begin(), driveNames.end(), tapeDriveStatus.driveName);
   if (it != driveNames.end()) {
-    m_catalogue.deleteTapeDrive(tapeDriveStatus.driveName);
+    m_catalogue.DriveState()->deleteTapeDrive(tapeDriveStatus.driveName);
   }
   tapeDriveStatus.ctaVersion = CTA_VERSION;
-  m_catalogue.createTapeDrive(tapeDriveStatus);
+  m_catalogue.DriveState()->createTapeDrive(tapeDriveStatus);
   log::ScopedParamContainer spc(lc);
   spc.add("drive", driveInfo.driveName);
   lc.log(log::DEBUG, "In TapeDrivesCatalogueState::createTapeDriveStatus(): success.");
 }
 
 void TapeDrivesCatalogueState::checkDriveCanBeCreated(const cta::common::dataStructures::DriveInfo & driveInfo) {
-  const auto driveNames = m_catalogue.getTapeDriveNames();
+  const auto driveNames = m_catalogue.DriveState()->getTapeDriveNames();
   try {
-    const auto tapeDrive = m_catalogue.getTapeDrive(driveInfo.driveName);
+    const auto tapeDrive = m_catalogue.DriveState()->getTapeDrive(driveInfo.driveName);
     if (!tapeDrive) return;  // tape drive does not exist
     if (tapeDrive.value().logicalLibrary != driveInfo.logicalLibrary || tapeDrive.value().host != driveInfo.host) {
       throw DriveAlreadyExistsException(std::string("The drive name=") + driveInfo.driveName +
@@ -72,7 +72,7 @@ void TapeDrivesCatalogueState::checkDriveCanBeCreated(const cta::common::dataStr
 
 void TapeDrivesCatalogueState::removeDrive(const std::string& drive, log::LogContext &lc) {
   try {
-    m_catalogue.deleteTapeDrive(drive);
+    m_catalogue.DriveState()->deleteTapeDrive(drive);
     log::ScopedParamContainer params(lc);
     params.add("driveName", drive);
     lc.log(log::INFO, "In TapeDrivesCatalogueState::removeDrive(): removed tape drive from database.");
@@ -83,10 +83,10 @@ void TapeDrivesCatalogueState::removeDrive(const std::string& drive, log::LogCon
 
 void TapeDrivesCatalogueState::setDesiredDriveState(const std::string& drive,
   const common::dataStructures::DesiredDriveState & desiredState, log::LogContext &lc) {
-  if(!desiredState.comment){
-    m_catalogue.setDesiredTapeDriveState(drive, desiredState);
+  if (!desiredState.comment) {
+    m_catalogue.DriveState()->setDesiredTapeDriveState(drive, desiredState);
   } else {
-    m_catalogue.setDesiredTapeDriveStateComment(drive, desiredState.comment.value());
+    m_catalogue.DriveState()->setDesiredTapeDriveStateComment(drive, desiredState.comment.value());
   }
 }
 
@@ -98,7 +98,8 @@ void TapeDrivesCatalogueState::updateDriveStatistics(const common::dataStructure
   statistics.bytesTransferedInSession = inputs.bytesTransferred;
   statistics.filesTransferedInSession = inputs.filesTransferred;
   statistics.reportTime = inputs.reportTime;
-  m_catalogue.updateTapeDriveStatistics(driveInfo.driveName, driveInfo.host, driveInfo.logicalLibrary, statistics);
+  m_catalogue.DriveState()->updateTapeDriveStatistics(driveInfo.driveName, driveInfo.host, driveInfo.logicalLibrary,
+                                                      statistics);
 }
 
 void TapeDrivesCatalogueState::reportDriveStatus(const common::dataStructures::DriveInfo& driveInfo,
@@ -168,7 +169,7 @@ void TapeDrivesCatalogueState::updateDriveStatus(const common::dataStructures::D
       throw exception::Exception("Unexpected status in DriveRegister::reportDriveStatus");
   }
 
-  m_catalogue.updateTapeDriveStatus(driveState);
+  m_catalogue.DriveState()->updateTapeDriveStatus(driveState);
 }
 
 void TapeDrivesCatalogueState::setDriveDown(common::dataStructures::TapeDrive & driveState,
diff --git a/catalogue/User.hpp b/catalogue/User.hpp
new file mode 100644
index 0000000000..6f962b7836
--- /dev/null
+++ b/catalogue/User.hpp
@@ -0,0 +1,64 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace cta {
+namespace catalogue {
+
+/**
+  * A fully qualified user, in other words the name of the disk instance and
+  * the name of the group.
+  */
+struct User {
+  /**
+   * The name of the disk instance to which the user name belongs.
+   */
+  std::string diskInstanceName;
+
+  /**
+   * The name of the user which is only guaranteed to be unique within its
+   * disk instance.
+   */
+  std::string username;
+
+  /**
+   * Constructor.
+   *
+   * @param d The name of the disk instance to which the group name belongs.
+   * @param u The name of the group which is only guaranteed to be unique
+   * within its disk instance.
+   */
+  User(const std::string &d, const std::string &u): diskInstanceName(d), username(u) {
+  }
+
+  /**
+   * Less than operator.
+   *
+   * @param rhs The argument on the right hand side of the operator.
+   * @return True if this object is less than the argument on the right hand
+   * side of the operator.
+   */
+  bool operator<(const User &rhs) const {
+    return diskInstanceName < rhs.diskInstanceName || username < rhs.username;
+  }
+}; // struct User
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/common_catalogue_schema.sql b/catalogue/common_catalogue_schema.sql
index f110330744..e72e96a2a3 100644
--- a/catalogue/common_catalogue_schema.sql
+++ b/catalogue/common_catalogue_schema.sql
@@ -53,8 +53,8 @@ CREATE UNIQUE INDEX DISK_INSTANCE_SPACE_DISN_UN_IDX ON DISK_INSTANCE_SPACE(DISK_
 
 CREATE TABLE DISK_SYSTEM(
   DISK_SYSTEM_NAME        VARCHAR(100)    CONSTRAINT DISK_SYSTEM_DSNM_NN NOT NULL,
-  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,   
-  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,   
+  DISK_INSTANCE_NAME       VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DIN_NN NOT NULL,
+  DISK_INSTANCE_SPACE_NAME VARCHAR(100)   CONSTRAINT DISK_SYSTEM_DISN_NN NOT NULL,
   FILE_REGEXP             VARCHAR(100)    CONSTRAINT DISK_SYSTEM_FR_NN   NOT NULL,
   TARGETED_FREE_SPACE     UINT64TYPE      CONSTRAINT DISK_SYSTEM_TFS_NN  NOT NULL,
   SLEEP_TIME              UINT64TYPE      CONSTRAINT DISK_SYSTEM_ST_NN   NOT NULL,
diff --git a/catalogue/dummy/DummyAdminUserCatalogue.cpp b/catalogue/dummy/DummyAdminUserCatalogue.cpp
new file mode 100644
index 0000000000..6f8a7c1343
--- /dev/null
+++ b/catalogue/dummy/DummyAdminUserCatalogue.cpp
@@ -0,0 +1,47 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/dummy/DummyAdminUserCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyAdminUserCatalogue::createAdminUser(const common::dataStructures::SecurityIdentity& admin,
+  const std::string& username, const std::string& comment) {
+    throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyAdminUserCatalogue::deleteAdminUser(const std::string& username) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::AdminUser> DummyAdminUserCatalogue::getAdminUsers() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyAdminUserCatalogue::modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin,
+  const std::string& username, const std::string& comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+bool DummyAdminUserCatalogue::isAdmin(const common::dataStructures::SecurityIdentity& admin) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyAdminUserCatalogue.hpp b/catalogue/dummy/DummyAdminUserCatalogue.hpp
new file mode 100644
index 0000000000..e41347f7e8
--- /dev/null
+++ b/catalogue/dummy/DummyAdminUserCatalogue.hpp
@@ -0,0 +1,43 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/AdminUserCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyAdminUserCatalogue : public AdminUserCatalogue {
+public:
+  DummyAdminUserCatalogue() = default;
+  ~DummyAdminUserCatalogue() override = default;
+
+  void createAdminUser(const common::dataStructures::SecurityIdentity &admin, const std::string &username,
+    const std::string &comment) override;
+  void deleteAdminUser(const std::string &username) override;
+
+  std::list<common::dataStructures::AdminUser> getAdminUsers() const override;
+
+  void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &username, const std::string &comment) override;
+
+  bool isAdmin(const common::dataStructures::SecurityIdentity &admin) const override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyArchiveFileCatalogue.cpp b/catalogue/dummy/DummyArchiveFileCatalogue.cpp
new file mode 100644
index 0000000000..53dec33624
--- /dev/null
+++ b/catalogue/dummy/DummyArchiveFileCatalogue.cpp
@@ -0,0 +1,93 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/dummy/DummyArchiveFileCatalogue.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
+#include "common/dataStructures/ArchiveFileSummary.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+uint64_t DummyArchiveFileCatalogue::checkAndGetNextArchiveFileId(const std::string &diskInstanceName,
+  const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::ArchiveFileQueueCriteria DummyArchiveFileCatalogue::getArchiveFileQueueCriteria(
+  const std::string &diskInstanceName, const std::string &storageClassName,
+  const common::dataStructures::RequesterIdentity &user) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+ArchiveFileItor DummyArchiveFileCatalogue::getArchiveFilesItor(const TapeFileSearchCriteria &searchCriteria) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::ArchiveFile DummyArchiveFileCatalogue::getArchiveFileForDeletion(
+  const TapeFileSearchCriteria &searchCriteria) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::ArchiveFile> DummyArchiveFileCatalogue::getFilesForRepack(const std::string &vid,
+  const uint64_t startFSeq, const uint64_t maxNbFiles) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+ArchiveFileItor DummyArchiveFileCatalogue::getArchiveFilesForRepackItor(const std::string &vid,
+  const uint64_t startFSeq) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::ArchiveFileSummary DummyArchiveFileCatalogue::getTapeFileSummary(
+  const TapeFileSearchCriteria &searchCriteria) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::ArchiveFile DummyArchiveFileCatalogue::getArchiveFileById(const uint64_t id) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveFileCatalogue::modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+  const std::string &newStorageClassName) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveFileCatalogue::modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId,
+  const std::string &diskInstance) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveFileCatalogue::moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
+  log::LogContext & lc) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveFileCatalogue::updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
+  const std::string &diskFileId) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveFileCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName,
+  const uint64_t archiveFileId, log::LogContext &lc) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyArchiveFileCatalogue.hpp b/catalogue/dummy/DummyArchiveFileCatalogue.hpp
new file mode 100644
index 0000000000..23bddfd34a
--- /dev/null
+++ b/catalogue/dummy/DummyArchiveFileCatalogue.hpp
@@ -0,0 +1,69 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/ArchiveFileCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyArchiveFileCatalogue : public ArchiveFileCatalogue {
+public:
+  DummyArchiveFileCatalogue() = default;
+  ~DummyArchiveFileCatalogue() override = default;
+
+  uint64_t checkAndGetNextArchiveFileId(const std::string &diskInstanceName, const std::string &storageClassName,
+    const common::dataStructures::RequesterIdentity &user) override;
+
+  common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(const std::string &diskInstanceName,
+    const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) override;
+
+  ArchiveFileItor getArchiveFilesItor(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  common::dataStructures::ArchiveFile getArchiveFileForDeletion(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  std::list<common::dataStructures::ArchiveFile> getFilesForRepack(const std::string &vid, const uint64_t startFSeq,
+    const uint64_t maxNbFiles) const override;
+
+  ArchiveFileItor getArchiveFilesForRepackItor(const std::string &vid, const uint64_t startFSeq) const override;
+
+  common::dataStructures::ArchiveFileSummary getTapeFileSummary(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const override;
+
+  void modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+    const std::string &newStorageClassName) const override;
+
+  void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId,
+    const std::string &diskInstance) const;
+
+  void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
+    log::LogContext & lc) override;\
+
+  void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
+    const std::string &diskFileId) override;
+
+  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
+    log::LogContext &lc) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyArchiveRouteCatalogue.cpp b/catalogue/dummy/DummyArchiveRouteCatalogue.cpp
new file mode 100644
index 0000000000..bc75c2a545
--- /dev/null
+++ b/catalogue/dummy/DummyArchiveRouteCatalogue.cpp
@@ -0,0 +1,53 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/dummy/DummyArchiveRouteCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyArchiveRouteCatalogue::createArchiveRoute(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName,
+  const uint32_t copyNb, const std::string &tapePoolName, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); 
+}
+
+void DummyArchiveRouteCatalogue::deleteArchiveRoute(const std::string &storageClassName, const uint32_t copyNb) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::ArchiveRoute> DummyArchiveRouteCatalogue::getArchiveRoutes() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::ArchiveRoute> DummyArchiveRouteCatalogue::getArchiveRoutes(
+  const std::string &storageClassName, const std::string &tapePoolName) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveRouteCatalogue::modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyArchiveRouteCatalogue::modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyArchiveRouteCatalogue.hpp b/catalogue/dummy/DummyArchiveRouteCatalogue.hpp
new file mode 100644
index 0000000000..797d030edf
--- /dev/null
+++ b/catalogue/dummy/DummyArchiveRouteCatalogue.hpp
@@ -0,0 +1,48 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/ArchiveRouteCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyArchiveRouteCatalogue : public ArchiveRouteCatalogue {
+public:
+  DummyArchiveRouteCatalogue() = default;
+  ~DummyArchiveRouteCatalogue() override = default;
+
+  void createArchiveRoute(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName,
+    const uint32_t copyNb, const std::string &tapePoolName, const std::string &comment) override;
+
+  void deleteArchiveRoute(const std::string &storageClassName, const uint32_t copyNb) override;
+
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const override;
+
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(const std::string &storageClassName,
+    const std::string &tapePoolName) const override;
+
+  void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) override;
+
+  void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyCatalogue.cpp b/catalogue/dummy/DummyCatalogue.cpp
new file mode 100644
index 0000000000..a24cd4a4dd
--- /dev/null
+++ b/catalogue/dummy/DummyCatalogue.cpp
@@ -0,0 +1,152 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/dummy/DummyAdminUserCatalogue.hpp"
+#include "catalogue/dummy/DummyArchiveFileCatalogue.hpp"
+#include "catalogue/dummy/DummyArchiveRouteCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyDiskInstanceCatalogue.hpp"
+#include "catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp"
+#include "catalogue/dummy/DummyDiskSystemCatalogue.hpp"
+#include "catalogue/dummy/DummyDriveConfigCatalogue.hpp"
+#include "catalogue/dummy/DummyDriveStateCatalogue.hpp"
+#include "catalogue/dummy/DummyFileRecycleLogCatalogue.hpp"
+#include "catalogue/dummy/DummyMountPolicyCatalogue.hpp"
+#include "catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.hpp"
+#include "catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.hpp"
+#include "catalogue/dummy/DummyRequesterMountRuleCatalogue.hpp"
+#include "catalogue/dummy/DummySchemaCatalogue.hpp"
+#include "catalogue/dummy/DummyStorageClassCatalogue.hpp"
+#include "catalogue/dummy/DummyTapeCatalogue.hpp"
+#include "catalogue/dummy/DummyTapeFileCatalogue.hpp"
+#include "catalogue/dummy/DummyTapePoolCatalogue.hpp"
+#include "catalogue/dummy/DummyVirtualOrganizationCatalogue.hpp"
+
+
+namespace cta {
+namespace catalogue {
+
+DummyCatalogue::DummyCatalogue()
+  : m_schema(std::make_unique<DummySchemaCatalogue>()),
+    m_adminUser(std::make_unique<DummyAdminUserCatalogue>()),
+    m_diskSystem(std::make_unique<DummyDiskSystemCatalogue>()),
+    m_diskInstance(std::make_unique<DummyDiskInstanceCatalogue>()),
+    m_diskInstanceSpace(std::make_unique<DummyDiskInstanceSpaceCatalogue>()),
+    m_vo(std::make_unique<DummyVirtualOrganizationCatalogue>()),
+    m_archiveRoute(std::make_unique<DummyArchiveRouteCatalogue>()),
+    m_storageClass(std::make_unique<DummyStorageClassCatalogue>()),
+    m_tapePool(std::make_unique<DummyTapePoolCatalogue>()),
+    m_tape(std::make_unique<DummyTapeCatalogue>()),
+    m_mountPolicy(std::make_unique<DummyMountPolicyCatalogue>()),
+    m_requesterActivityMountRule(std::make_unique<DummyRequesterActivityMountRuleCatalogue>()),
+    m_requesterMountRule(std::make_unique<DummyRequesterMountRuleCatalogue>()),
+    m_requesterGroupMountRule(std::make_unique<DummyRequesterGroupMountRuleCatalogue>()),
+    m_tapeFile(std::make_unique<DummyTapeFileCatalogue>()),
+    m_fileRecycleLog(std::make_unique<DummyFileRecycleLogCatalogue>()),
+    m_driveConfig(std::make_unique<DummyDriveConfigCatalogue>()),
+    m_archiveFile(std::make_unique<DummyArchiveFileCatalogue>()),
+    m_driveState(std::make_unique<DummyDriveStateCatalogue>()) {
+}
+
+const std::unique_ptr<SchemaCatalogue>& DummyCatalogue::Schema() {
+  return m_schema;
+}
+
+const std::unique_ptr<AdminUserCatalogue>& DummyCatalogue::AdminUser() {
+  return m_adminUser;
+}
+
+const std::unique_ptr<DiskSystemCatalogue>& DummyCatalogue::DiskSystem() {
+  return m_diskSystem;
+}
+
+const std::unique_ptr<DiskInstanceCatalogue>& DummyCatalogue::DiskInstance() {
+  return m_diskInstance;
+}
+
+const std::unique_ptr<DiskInstanceSpaceCatalogue>& DummyCatalogue::DiskInstanceSpace() {
+  return m_diskInstanceSpace;
+}
+
+const std::unique_ptr<VirtualOrganizationCatalogue>& DummyCatalogue::VO() {
+  return m_vo;
+}
+
+const std::unique_ptr<ArchiveRouteCatalogue>& DummyCatalogue::ArchiveRoute() {
+  return m_archiveRoute;
+}
+
+const std::unique_ptr<MediaTypeCatalogue>& DummyCatalogue::MediaType() {
+  return m_mediaType;
+}
+
+const std::unique_ptr<StorageClassCatalogue>& DummyCatalogue::StorageClass() {
+  return m_storageClass;
+}
+
+const std::unique_ptr<TapePoolCatalogue>& DummyCatalogue::TapePool() {
+  return m_tapePool;
+}
+
+const std::unique_ptr<TapeCatalogue>& DummyCatalogue::Tape() {
+  return m_tape;
+}
+
+const std::unique_ptr<MountPolicyCatalogue>& DummyCatalogue::MountPolicy() {
+  return m_mountPolicy;
+}
+
+const std::unique_ptr<RequesterActivityMountRuleCatalogue>& DummyCatalogue::RequesterActivityMountRule() {
+  return m_requesterActivityMountRule;
+}
+
+const std::unique_ptr<RequesterMountRuleCatalogue>& DummyCatalogue::RequesterMountRule() {
+  return m_requesterMountRule;
+}
+
+const std::unique_ptr<RequesterGroupMountRuleCatalogue>& DummyCatalogue::RequesterGroupMountRule() {
+  return m_requesterGroupMountRule;
+}
+
+const std::unique_ptr<LogicalLibraryCatalogue>& DummyCatalogue::LogicalLibrary() {
+  return m_logicalLibrary;
+}
+
+const std::unique_ptr<TapeFileCatalogue>& DummyCatalogue::TapeFile() {
+  return m_tapeFile;
+}
+
+const std::unique_ptr<FileRecycleLogCatalogue>& DummyCatalogue::FileRecycleLog() {
+  return m_fileRecycleLog;
+}
+
+const std::unique_ptr<DriveConfigCatalogue>& DummyCatalogue::DriveConfig() {
+  return m_driveConfig;
+}
+
+const std::unique_ptr<ArchiveFileCatalogue>& DummyCatalogue::ArchiveFile() {
+  return m_archiveFile;
+}
+
+const std::unique_ptr<DriveStateCatalogue>& DummyCatalogue::DriveState() {
+  return m_driveState;
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyCatalogue.hpp b/catalogue/dummy/DummyCatalogue.hpp
new file mode 100644
index 0000000000..fc06f636a4
--- /dev/null
+++ b/catalogue/dummy/DummyCatalogue.hpp
@@ -0,0 +1,86 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * An empty implementation of the Catalogue used to populate unit tests of the scheduler database
+ * as they need a reference to a Catalogue, used in very few situations (requeueing of retrieve
+ * requests).
+ */
+
+class DummyCatalogue: public Catalogue {
+public:
+  DummyCatalogue();
+  ~DummyCatalogue() override = default;
+
+  const std::unique_ptr<SchemaCatalogue>& Schema() override;
+  const std::unique_ptr<AdminUserCatalogue>& AdminUser() override;
+  const std::unique_ptr<DiskSystemCatalogue>& DiskSystem() override;
+  const std::unique_ptr<DiskInstanceCatalogue>& DiskInstance() override;
+  const std::unique_ptr<DiskInstanceSpaceCatalogue>& DiskInstanceSpace() override;
+  const std::unique_ptr<VirtualOrganizationCatalogue>& VO() override;
+  const std::unique_ptr<ArchiveRouteCatalogue>& ArchiveRoute() override;
+  const std::unique_ptr<MediaTypeCatalogue>& MediaType() override;
+  const std::unique_ptr<StorageClassCatalogue>& StorageClass() override;
+  const std::unique_ptr<TapePoolCatalogue>& TapePool() override;
+  const std::unique_ptr<TapeCatalogue>& Tape() override;
+  const std::unique_ptr<MountPolicyCatalogue>& MountPolicy() override;
+  const std::unique_ptr<RequesterActivityMountRuleCatalogue>& RequesterActivityMountRule() override;
+  const std::unique_ptr<RequesterMountRuleCatalogue>& RequesterMountRule() override;
+  const std::unique_ptr<RequesterGroupMountRuleCatalogue>& RequesterGroupMountRule() override;
+  const std::unique_ptr<LogicalLibraryCatalogue>& LogicalLibrary() override;
+  const std::unique_ptr<TapeFileCatalogue>& TapeFile() override;
+  const std::unique_ptr<FileRecycleLogCatalogue>& FileRecycleLog() override;
+  const std::unique_ptr<ArchiveFileCatalogue>& ArchiveFile() override;
+  const std::unique_ptr<DriveConfigCatalogue>& DriveConfig() override;
+  const std::unique_ptr<DriveStateCatalogue>& DriveState() override;
+
+protected:
+  std::unique_ptr<SchemaCatalogue> m_schema;
+  std::unique_ptr<AdminUserCatalogue> m_adminUser;
+  std::unique_ptr<DiskSystemCatalogue> m_diskSystem;
+  std::unique_ptr<DiskInstanceCatalogue> m_diskInstance;
+  std::unique_ptr<DiskInstanceSpaceCatalogue> m_diskInstanceSpace;
+  std::unique_ptr<VirtualOrganizationCatalogue> m_vo;
+  std::unique_ptr<ArchiveRouteCatalogue> m_archiveRoute;
+  std::unique_ptr<MediaTypeCatalogue> m_mediaType;
+  std::unique_ptr<StorageClassCatalogue> m_storageClass;
+  std::unique_ptr<TapePoolCatalogue> m_tapePool;
+  std::unique_ptr<TapeCatalogue> m_tape;
+  std::unique_ptr<MountPolicyCatalogue> m_mountPolicy;
+  std::unique_ptr<RequesterActivityMountRuleCatalogue> m_requesterActivityMountRule;
+  std::unique_ptr<RequesterMountRuleCatalogue> m_requesterMountRule;
+  std::unique_ptr<RequesterGroupMountRuleCatalogue> m_requesterGroupMountRule;
+  std::unique_ptr<LogicalLibraryCatalogue> m_logicalLibrary;
+  std::unique_ptr<TapeFileCatalogue> m_tapeFile;
+  std::unique_ptr<FileRecycleLogCatalogue> m_fileRecycleLog;
+  std::unique_ptr<DriveConfigCatalogue> m_driveConfig;
+  std::unique_ptr<ArchiveFileCatalogue> m_archiveFile;
+  std::unique_ptr<DriveStateCatalogue> m_driveState;
+};
+
+}  // namespace catalogue
+}  // namespace cta.
+
diff --git a/catalogue/dummy/DummyDiskInstanceCatalogue.cpp b/catalogue/dummy/DummyDiskInstanceCatalogue.cpp
new file mode 100644
index 0000000000..e0e301f73a
--- /dev/null
+++ b/catalogue/dummy/DummyDiskInstanceCatalogue.cpp
@@ -0,0 +1,48 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "catalogue/dummy/DummyDiskInstanceCatalogue.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyDiskInstanceCatalogue::createDiskInstance(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  m_diskInstances[name] = {name, comment, common::dataStructures::EntryLog(), common::dataStructures::EntryLog()};
+}
+
+void DummyDiskInstanceCatalogue::deleteDiskInstance(const std::string &name) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskInstanceCatalogue::modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::DiskInstance> DummyDiskInstanceCatalogue::getAllDiskInstances() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyDiskInstanceCatalogue.hpp b/catalogue/dummy/DummyDiskInstanceCatalogue.hpp
new file mode 100644
index 0000000000..cd65da710d
--- /dev/null
+++ b/catalogue/dummy/DummyDiskInstanceCatalogue.hpp
@@ -0,0 +1,51 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "catalogue/interfaces/DiskInstanceCatalogue.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class DummyDiskInstanceCatalogue: public DiskInstanceCatalogue {
+public:
+  DummyDiskInstanceCatalogue() = default;
+  ~DummyDiskInstanceCatalogue() override = default;
+
+  void createDiskInstance(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+  void deleteDiskInstance(const std::string &name) override;
+
+  void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const override;
+
+private:
+  std::map<std::string, common::dataStructures::DiskInstance> m_diskInstances;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyDiskInstanceSpaceCatalogue.cpp b/catalogue/dummy/DummyDiskInstanceSpaceCatalogue.cpp
new file mode 100644
index 0000000000..4777b3fc36
--- /dev/null
+++ b/catalogue/dummy/DummyDiskInstanceSpaceCatalogue.cpp
@@ -0,0 +1,72 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp"
+#include "common/dataStructures/DiskInstanceSpace.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+std::map<std::string, common::dataStructures::DiskInstanceSpace> DummyDiskInstanceSpaceCatalogue::m_diskInstanceSpaces;
+
+void DummyDiskInstanceSpaceCatalogue::deleteDiskInstanceSpace(const std::string &name,
+  const std::string &diskInstance) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskInstanceSpaceCatalogue::createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL,
+  const uint64_t refreshInterval, const std::string &comment) {
+    m_diskInstanceSpaces[name] = {name, diskInstance, freeSpaceQueryURL, refreshInterval, 0, 0, comment,
+      common::dataStructures::EntryLog(), common::dataStructures::EntryLog()};
+}
+
+std::list<common::dataStructures::DiskInstanceSpace> DummyDiskInstanceSpaceCatalogue::getAllDiskInstanceSpaces() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceRefreshInterval(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const uint64_t refreshInterval) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+  const std::string &diskInstance, const uint64_t freeSpace) {
+  m_diskInstanceSpaces[name].freeSpace = freeSpace;
+  m_diskInstanceSpaces[name].lastRefreshTime = time(nullptr);
+}
+
+void DummyDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceQueryURL(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const std::string &freeSpaceQueryURL) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp b/catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp
new file mode 100644
index 0000000000..1ee8449ab2
--- /dev/null
+++ b/catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp
@@ -0,0 +1,70 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct DiskInstance;
+}
+}
+
+namespace catalogue {
+
+class DummyDiskInstanceSpaceCatalogue: public DiskInstanceSpaceCatalogue {
+public:
+  DummyDiskInstanceSpaceCatalogue() = default;
+  ~DummyDiskInstanceSpaceCatalogue() override = default;
+
+  void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) override;
+
+  void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name,
+    const std::string &diskInstance,
+    const std::string &freeSpaceQueryURL,
+    const uint64_t refreshInterval,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const override;
+
+  void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &comment) override;
+
+  void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) override;
+
+  void modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+    const std::string &diskInstance, const uint64_t freeSpace) override;
+
+  void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) override;
+
+private:
+  friend class DummyDiskSystemCatalogue;
+  static std::map<std::string, common::dataStructures::DiskInstanceSpace> m_diskInstanceSpaces;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyDiskSystemCatalogue.cpp b/catalogue/dummy/DummyDiskSystemCatalogue.cpp
new file mode 100644
index 0000000000..f7b47e13e8
--- /dev/null
+++ b/catalogue/dummy/DummyDiskSystemCatalogue.cpp
@@ -0,0 +1,82 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <map>
+#include <string>
+
+#include "catalogue/dummy/DummyDiskSystemCatalogue.hpp"
+#include "catalogue/dummy/DummyDiskInstanceSpaceCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "disk/DiskSystem.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyDiskSystemCatalogue::createDiskSystem(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &diskInstanceName, const std::string &diskInstanceSpaceName,
+  const std::string &fileRegexp, const uint64_t targetedFreeSpace, const time_t sleepTime,const std::string &comment) {
+  m_diskSystemList.push_back({name, DummyDiskInstanceSpaceCatalogue::m_diskInstanceSpaces.at(diskInstanceSpaceName),
+    fileRegexp, targetedFreeSpace, sleepTime, common::dataStructures::EntryLog(), common::dataStructures::EntryLog{},
+    comment});
+}
+
+void DummyDiskSystemCatalogue::deleteDiskSystem(const std::string &name) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+disk::DiskSystemList DummyDiskSystemCatalogue::getAllDiskSystems() const {
+  return m_diskSystemList;
+}
+
+void DummyDiskSystemCatalogue::modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &fileRegexp) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskSystemCatalogue::modifyDiskSystemTargetedFreeSpace(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+  const uint64_t targetedFreeSpace) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskSystemCatalogue::modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskSystemCatalogue::modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
+  const std::string& name, const uint64_t sleepTime) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskSystemCatalogue::modifyDiskSystemDiskInstanceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyDiskSystemCatalogue::modifyDiskSystemDiskInstanceSpaceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+  const std::string &diskInstanceSpaceName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+bool DummyDiskSystemCatalogue::diskSystemExists(const std::string &name) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyDiskSystemCatalogue.hpp b/catalogue/dummy/DummyDiskSystemCatalogue.hpp
new file mode 100644
index 0000000000..0d74f82ed2
--- /dev/null
+++ b/catalogue/dummy/DummyDiskSystemCatalogue.hpp
@@ -0,0 +1,67 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/interfaces/DiskSystemCatalogue.hpp"
+#include "disk/DiskSystem.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class DummyDiskSystemCatalogue: public DiskSystemCatalogue {
+public:
+  DummyDiskSystemCatalogue() = default;
+  ~DummyDiskSystemCatalogue() override = default;
+
+  void createDiskSystem(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &diskInstanceName, const std::string &diskInstanceSpaceName, const std::string &fileRegexp,
+    const uint64_t targetedFreeSpace, const time_t sleepTime, const std::string &comment) override;
+
+  void deleteDiskSystem(const std::string &name) override;
+
+  disk::DiskSystemList getAllDiskSystems() const override;
+
+  void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &fileRegexp) override;
+
+  void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t targetedFreeSpace) override;
+
+  void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
+    const std::string& name, const uint64_t sleepTime) override;
+
+  void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceName) override;
+
+  void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceSpaceName) override;
+
+  bool diskSystemExists(const std::string &name) const override;
+
+private:
+  disk::DiskSystemList m_diskSystemList;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyDriveConfig.hpp b/catalogue/dummy/DummyDriveConfig.hpp
new file mode 100644
index 0000000000..8eaaaa7cbb
--- /dev/null
+++ b/catalogue/dummy/DummyDriveConfig.hpp
@@ -0,0 +1,53 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/DriveConfigCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyDriveConfigCatalogue : public DriveConfigCatalogue {
+public:
+  DummyDriveConfigCatalogue() = default;
+  ~DummyDriveConfigCatalogue() override = default;
+
+  void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::list<DriveConfig> getTapeDriveConfigs() const override;
+
+  std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const override;
+
+  void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig(const std::string &tapeDriveName,
+    const std::string &keyName) const override;
+
+  void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyDriveConfigCatalogue.cpp b/catalogue/dummy/DummyDriveConfigCatalogue.cpp
new file mode 100644
index 0000000000..e33d24aeb1
--- /dev/null
+++ b/catalogue/dummy/DummyDriveConfigCatalogue.cpp
@@ -0,0 +1,51 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/dummy/DummyDriveConfigCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyDriveConfigCatalogue::createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+  const std::string &keyName, const std::string &value, const std::string &source) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> DummyDriveConfigCatalogue::getTapeDriveConfigs() const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+std::list<std::pair<std::string, std::string>> DummyDriveConfigCatalogue::getTapeDriveConfigNamesAndKeys() const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyDriveConfigCatalogue::modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+  const std::string &keyName, const std::string &value, const std::string &source) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+std::optional<std::tuple<std::string, std::string, std::string>> DummyDriveConfigCatalogue::getTapeDriveConfig(
+  const std::string &tapeDriveName, const std::string &keyName) const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyDriveConfigCatalogue::deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyDriveConfigCatalogue.hpp b/catalogue/dummy/DummyDriveConfigCatalogue.hpp
new file mode 100644
index 0000000000..c717ab9daa
--- /dev/null
+++ b/catalogue/dummy/DummyDriveConfigCatalogue.hpp
@@ -0,0 +1,54 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/interfaces/DriveConfigCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyDriveConfigCatalogue: public DriveConfigCatalogue {
+public:
+  DummyDriveConfigCatalogue() = default;
+  ~DummyDriveConfigCatalogue() override = default;
+
+  void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::list<DriveConfig> getTapeDriveConfigs() const override;
+
+  std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const override;
+
+  void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig(const std::string &tapeDriveName,
+    const std::string &keyName) const override;
+
+  void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyDriveStateCatalogue.cpp b/catalogue/dummy/DummyDriveStateCatalogue.cpp
new file mode 100644
index 0000000000..587c88dc9b
--- /dev/null
+++ b/catalogue/dummy/DummyDriveStateCatalogue.cpp
@@ -0,0 +1,171 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/CatalogueExceptions.hpp"
+#include "catalogue/dummy/DummyDriveStateCatalogue.hpp"
+#include "common/dataStructures/DesiredDriveState.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/dataStructures/TapeDriveStatistics.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyDriveStateCatalogue::createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyDriveStateCatalogue::deleteTapeDrive(const std::string &tapeDriveName) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+std::list<std::string> DummyDriveStateCatalogue::getTapeDriveNames() const {
+  return {m_tapeDriveStatus.driveName};
+}
+
+std::optional<common::dataStructures::TapeDrive> DummyDriveStateCatalogue::getTapeDrive(
+  const std::string &tapeDriveName) const {
+  if (m_tapeDriveStatus.driveName != "") return m_tapeDriveStatus;
+  common::dataStructures::TapeDrive tapeDriveStatus;
+  const time_t reportTime = time(nullptr);
+
+  tapeDriveStatus.driveName = tapeDriveName;
+  tapeDriveStatus.host = "Dummy_Host";
+  tapeDriveStatus.logicalLibrary = "Dummy_Library";
+
+  tapeDriveStatus.downOrUpStartTime = reportTime;
+
+  tapeDriveStatus.mountType = common::dataStructures::MountType::NoMount;
+  tapeDriveStatus.driveStatus = common::dataStructures::DriveStatus::Down;
+  tapeDriveStatus.desiredUp = false;
+  tapeDriveStatus.desiredForceDown = false;
+
+  tapeDriveStatus.diskSystemName = "Dummy_System";
+  tapeDriveStatus.reservedBytes = 0;
+  tapeDriveStatus.reservationSessionId = 0;
+
+
+  return tapeDriveStatus;
+}
+
+std::list<common::dataStructures::TapeDrive> DummyDriveStateCatalogue::getTapeDrives() const {
+  std::list<common::dataStructures::TapeDrive> tapeDrives;
+  const auto tapeDrive = getTapeDrive(m_tapeDriveStatus.driveName);
+  if (tapeDrive.has_value()) tapeDrives.push_back(tapeDrive.value());
+  return tapeDrives;
+}
+
+void DummyDriveStateCatalogue::setDesiredTapeDriveState(const std::string&,
+    const common::dataStructures::DesiredDriveState &desiredState) {
+  m_tapeDriveStatus.desiredUp = desiredState.up;
+  m_tapeDriveStatus.desiredForceDown = desiredState.forceDown;
+  m_tapeDriveStatus.reasonUpDown = desiredState.reason;
+}
+
+void DummyDriveStateCatalogue::setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+  const std::string &comment) {
+  m_tapeDriveStatus.userComment = comment;
+}
+
+void DummyDriveStateCatalogue::updateTapeDriveStatistics(const std::string& tapeDriveName,
+  const std::string& host, const std::string& logicalLibrary,
+  const common::dataStructures::TapeDriveStatistics& statistics) {
+  m_tapeDriveStatus.driveName = tapeDriveName;
+  m_tapeDriveStatus.host = host;
+  m_tapeDriveStatus.logicalLibrary = logicalLibrary;
+  m_tapeDriveStatus.bytesTransferedInSession = statistics.bytesTransferedInSession;
+  m_tapeDriveStatus.filesTransferedInSession = statistics.filesTransferedInSession;
+  m_tapeDriveStatus.lastModificationLog = statistics.lastModificationLog;
+}
+
+void DummyDriveStateCatalogue::updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) {
+  m_tapeDriveStatus = tapeDrive;
+}
+
+std::map<std::string, uint64_t> DummyDriveStateCatalogue::getDiskSpaceReservations() const {
+  std::map<std::string, uint64_t> ret;
+  const auto tdNames = getTapeDriveNames();
+  for (const auto& driveName : tdNames) {
+    const auto tdStatus = getTapeDrive(driveName);
+    if (tdStatus.value().diskSystemName) {
+      // no need to check key, operator[] initializes missing values at zero for scalar types
+      ret[tdStatus.value().diskSystemName.value()] += tdStatus.value().reservedBytes.value();
+    }
+  }
+  return ret;
+}
+
+void DummyDriveStateCatalogue::reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
+  if (diskSpaceReservation.empty()) return;
+
+  log::ScopedParamContainer params(lc);
+  params.add("driveName", driveName)
+        .add("diskSystem", diskSpaceReservation.begin()->first)
+        .add("reservationBytes", diskSpaceReservation.begin()->second)
+        .add("mountId", mountId);
+  lc.log(log::DEBUG, "In RetrieveMount::reserveDiskSpace(): reservation request.");
+
+  auto tdStatus = getTapeDrive(driveName);
+  if (!tdStatus) return;
+
+  if (!tdStatus.value().reservationSessionId) {
+    tdStatus.value().reservationSessionId = mountId;
+    tdStatus.value().reservedBytes = 0;
+  }
+
+  if (tdStatus.value().reservationSessionId != mountId) {
+    tdStatus.value().reservationSessionId = mountId;
+    tdStatus.value().reservedBytes = 0;
+  }
+
+  tdStatus.value().diskSystemName = diskSpaceReservation.begin()->first;
+  tdStatus.value().reservedBytes.value() += diskSpaceReservation.begin()->second;
+  updateTapeDriveStatus(tdStatus.value());
+}
+
+void DummyDriveStateCatalogue::releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
+  if (diskSpaceReservation.empty()) return;
+
+  log::ScopedParamContainer params(lc);
+  params.add("driveName", driveName)
+        .add("diskSystem", diskSpaceReservation.begin()->first)
+        .add("reservationBytes", diskSpaceReservation.begin()->second)
+        .add("mountId", mountId);
+  lc.log(log::DEBUG, "In RetrieveMount::releaseDiskSpace(): reservation release request.");
+
+  auto tdStatus = getTapeDrive(driveName);
+
+  if (!tdStatus) return;
+  if (!tdStatus.value().reservationSessionId) {
+    return;
+  }
+  if (tdStatus.value().reservationSessionId != mountId) {
+    return;
+  }
+  auto& bytes = diskSpaceReservation.begin()->second;
+  if (bytes > tdStatus.value().reservedBytes) throw exception::NegativeDiskSpaceReservationReached(
+    "In DriveState::subtractDiskSpaceReservation(): we would reach a negative reservation size.");
+  tdStatus.value().diskSystemName = diskSpaceReservation.begin()->first;
+  tdStatus.value().reservedBytes.value() -= bytes;
+  updateTapeDriveStatus(tdStatus.value());
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyDriveStateCatalogue.hpp b/catalogue/dummy/DummyDriveStateCatalogue.hpp
new file mode 100644
index 0000000000..875c562740
--- /dev/null
+++ b/catalogue/dummy/DummyDriveStateCatalogue.hpp
@@ -0,0 +1,74 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/interfaces/DriveStateCatalogue.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyDriveStateCatalogue : public DriveStateCatalogue {
+public:
+  DummyDriveStateCatalogue() = default;
+  ~DummyDriveStateCatalogue() override = default;
+
+  void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) override;
+
+  std::list<std::string> getTapeDriveNames() const override;
+
+  std::list<common::dataStructures::TapeDrive> getTapeDrives() const override;
+
+  std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const override;
+
+  void setDesiredTapeDriveState(const std::string& tapeDriveName,
+      const common::dataStructures::DesiredDriveState &desiredState) override;
+
+  void setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+    const std::string &comment) override;
+
+  void updateTapeDriveStatistics(const std::string& tapeDriveName,
+    const std::string& host, const std::string& logicalLibrary,
+    const common::dataStructures::TapeDriveStatistics& statistics) override;
+
+  void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) override;
+
+  void deleteTapeDrive(const std::string &tapeDriveName) override;
+
+  std::map<std::string, uint64_t> getDiskSpaceReservations() const override;
+
+  void reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
+
+  void releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
+
+private:
+  common::dataStructures::TapeDrive m_tapeDriveStatus;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyFileRecycleLogCatalogue.cpp b/catalogue/dummy/DummyFileRecycleLogCatalogue.cpp
new file mode 100644
index 0000000000..11e959ee21
--- /dev/null
+++ b/catalogue/dummy/DummyFileRecycleLogCatalogue.cpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/dummy/DummyFileRecycleLogCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+namespace catalogue {
+
+FileRecycleLogItor DummyFileRecycleLogCatalogue::getFileRecycleLogItor(
+  const RecycleTapeFileSearchCriteria & searchCriteria) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyFileRecycleLogCatalogue::restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+  const std::string &newFid) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyFileRecycleLogCatalogue::deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/dummy/DummyFileRecycleLogCatalogue.hpp b/catalogue/dummy/DummyFileRecycleLogCatalogue.hpp
new file mode 100644
index 0000000000..01e8f25871
--- /dev/null
+++ b/catalogue/dummy/DummyFileRecycleLogCatalogue.hpp
@@ -0,0 +1,40 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyFileRecycleLogCatalogue: public FileRecycleLogCatalogue {
+public:
+  DummyFileRecycleLogCatalogue() = default;
+  ~DummyFileRecycleLogCatalogue() override = default;
+
+  FileRecycleLogItor getFileRecycleLogItor(
+    const RecycleTapeFileSearchCriteria & searchCriteria = RecycleTapeFileSearchCriteria()) const override;
+
+  void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+    const std::string &newFid) override;
+
+  void deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyLogicalLibraryCatalogue.cpp b/catalogue/dummy/DummyLogicalLibraryCatalogue.cpp
new file mode 100644
index 0000000000..f21b32f405
--- /dev/null
+++ b/catalogue/dummy/DummyLogicalLibraryCatalogue.cpp
@@ -0,0 +1,60 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/dummy/DummyLogicalLibraryCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+
+void DummyLogicalLibraryCatalogue::createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool isDisabled, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyLogicalLibraryCatalogue::deleteLogicalLibrary(const std::string &name) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::LogicalLibrary> DummyLogicalLibraryCatalogue::getLogicalLibraries() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyLogicalLibraryCatalogue::modifyLogicalLibraryName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyLogicalLibraryCatalogue::modifyLogicalLibraryComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyLogicalLibraryCatalogue::modifyLogicalLibraryDisabledReason(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &disabledReason) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyLogicalLibraryCatalogue::setLogicalLibraryDisabled(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool disabledValue) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyLogicalLibraryCatalogue.hpp b/catalogue/dummy/DummyLogicalLibraryCatalogue.hpp
new file mode 100644
index 0000000000..cc207083d8
--- /dev/null
+++ b/catalogue/dummy/DummyLogicalLibraryCatalogue.hpp
@@ -0,0 +1,51 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/LogicalLibraryCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyLogicalLibraryCatalogue : public LogicalLibraryCatalogue {
+public:
+  DummyLogicalLibraryCatalogue() = default;
+  ~DummyLogicalLibraryCatalogue() override = default;
+
+  void createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool isDisabled, const std::string &comment) override;
+
+  void deleteLogicalLibrary(const std::string &name) override;
+
+  std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const override;
+
+  void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) override;
+
+  void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &disabledReason) override;
+
+  void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool disabledValue) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyMediaTypeCatalogue.cpp b/catalogue/dummy/DummyMediaTypeCatalogue.cpp
new file mode 100644
index 0000000000..cc01827fc3
--- /dev/null
+++ b/catalogue/dummy/DummyMediaTypeCatalogue.cpp
@@ -0,0 +1,93 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "catalogue/dummy/DummyMediaTypeCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyMediaTypeCatalogue::createMediaType(const common::dataStructures::SecurityIdentity &admin,
+  const MediaType &mediaType) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::deleteMediaType(const std::string &name) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<MediaTypeWithLogs> DummyMediaTypeCatalogue::getMediaTypes() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+MediaType DummyMediaTypeCatalogue::getMediaTypeByVid(const std::string & vid) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &cartridge) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeCapacityInBytes(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t capacityInBytes) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypePrimaryDensityCode(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t primaryDensityCode) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeSecondaryDensityCode(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t secondaryDensityCode) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint32_t> &nbWraps) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint64_t> &minLPos) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint64_t> &maxLPos) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMediaTypeCatalogue::modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyMediaTypeCatalogue.hpp b/catalogue/dummy/DummyMediaTypeCatalogue.hpp
new file mode 100644
index 0000000000..b458c929fb
--- /dev/null
+++ b/catalogue/dummy/DummyMediaTypeCatalogue.hpp
@@ -0,0 +1,67 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/MediaTypeCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyMediaTypeCatalogue : public MediaTypeCatalogue {
+public:
+  DummyMediaTypeCatalogue() = default;
+  ~DummyMediaTypeCatalogue() override = default;
+
+  void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) override;
+
+  void deleteMediaType(const std::string &name) override;
+
+  std::list<MediaTypeWithLogs> getMediaTypes() const override;
+
+  MediaType getMediaTypeByVid(const std::string & vid) const override;
+
+  void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) override;
+
+  void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &cartridge) override;
+
+  void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const uint64_t capacityInBytes) override;
+
+  void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+   const uint8_t primaryDensityCode) override;
+
+  void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint8_t secondaryDensityCode) override;
+
+  void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint32_t> &nbWraps) override;
+
+  void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &minLPos) override;
+
+  void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &maxLPos) override;
+
+  void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyMountPolicyCatalogue.cpp b/catalogue/dummy/DummyMountPolicyCatalogue.cpp
new file mode 100644
index 0000000000..9125264cc9
--- /dev/null
+++ b/catalogue/dummy/DummyMountPolicyCatalogue.cpp
@@ -0,0 +1,110 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "catalogue/dummy/DummyMountPolicyCatalogue.hpp"
+#include "common/dataStructures/MountPolicy.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyMountPolicyCatalogue::createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+  const CreateMountPolicyAttributes & mountPolicy) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::MountPolicy> DummyMountPolicyCatalogue::getMountPolicies() const {
+  std::list<common::dataStructures::MountPolicy> mountPolicies;
+  common::dataStructures::MountPolicy mp1;
+  mp1.name = "mountPolicy";
+  mp1.archivePriority = 1;
+  mp1.archiveMinRequestAge = 0;
+  mp1.retrievePriority = 1;
+  mp1.retrieveMinRequestAge = 0;
+  mountPolicies.push_back(mp1);
+
+  common::dataStructures::MountPolicy mp2;
+  mp2.name = "moreAdvantageous";
+  mp2.archivePriority = 2;
+  mp2.archiveMinRequestAge = 0;
+  mp2.retrievePriority = 2;
+  mp2.retrieveMinRequestAge = 0;
+  mountPolicies.push_back(mp1);
+  return mountPolicies;
+}
+
+std::optional<common::dataStructures::MountPolicy> DummyMountPolicyCatalogue::getMountPolicy(
+  const std::string &mountPolicyName) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::MountPolicy> DummyMountPolicyCatalogue::getCachedMountPolicies() const {
+  std::list<common::dataStructures::MountPolicy> mountPolicies;
+  common::dataStructures::MountPolicy mp1;
+  mp1.name = "mountPolicy";
+  mp1.archivePriority = 1;
+  mp1.archiveMinRequestAge = 0;
+  mp1.retrievePriority = 1;
+  mp1.retrieveMinRequestAge = 0;
+  mountPolicies.push_back(mp1);
+
+  common::dataStructures::MountPolicy mp2;
+  mp2.name = "moreAdvantageous";
+  mp2.archivePriority = 2;
+  mp2.archiveMinRequestAge = 0;
+  mp2.retrievePriority = 2;
+  mp2.retrieveMinRequestAge = 0;
+  mountPolicies.push_back(mp1);
+  return mountPolicies;
+}
+
+void DummyMountPolicyCatalogue::deleteMountPolicy(const std::string &name) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMountPolicyCatalogue::modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t archivePriority) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMountPolicyCatalogue::modifyMountPolicyArchiveMinRequestAge(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t minArchiveRequestAge) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMountPolicyCatalogue::modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t retrievePriority) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMountPolicyCatalogue::modifyMountPolicyRetrieveMinRequestAge(
+  const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t minRetrieveRequestAge) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyMountPolicyCatalogue::modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyMountPolicyCatalogue.hpp b/catalogue/dummy/DummyMountPolicyCatalogue.hpp
new file mode 100644
index 0000000000..4f30f5caf9
--- /dev/null
+++ b/catalogue/dummy/DummyMountPolicyCatalogue.hpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/MountPolicyCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyMountPolicyCatalogue : public MountPolicyCatalogue {
+public:
+  DummyMountPolicyCatalogue() = default;
+  ~DummyMountPolicyCatalogue() override = default;
+
+  void createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+    const CreateMountPolicyAttributes & mountPolicy) override;
+
+  std::list<common::dataStructures::MountPolicy> getMountPolicies() const override;
+
+  std::optional<common::dataStructures::MountPolicy> getMountPolicy(
+    const std::string &mountPolicyName) const override;
+
+  std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const override;
+
+  void deleteMountPolicy(const std::string &name) override;
+
+  void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t archivePriority) override;
+
+  void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minArchiveRequestAge) override;
+
+  void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t retrievePriority) override;
+
+  void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minRetrieveRequestAge) override;
+
+  void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.cpp b/catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.cpp
new file mode 100644
index 0000000000..2f9ca1538d
--- /dev/null
+++ b/catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.cpp
@@ -0,0 +1,58 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyRequesterActivityMountRuleCatalogue::modifyRequesterActivityMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &activityRegex, const std::string &mountPolicy) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterActivityMountRuleCatalogue::modifyRequesterActivityMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &activityRegex, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterActivityMountRuleCatalogue::createRequesterActivityMountRule(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &mountPolicyName,
+  const std::string &diskInstance, const std::string &requesterName, const std::string &activityRegex,
+  const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::RequesterActivityMountRule>
+  DummyRequesterActivityMountRuleCatalogue::getRequesterActivityMountRules() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterActivityMountRuleCatalogue::deleteRequesterActivityMountRule(const std::string &diskInstanceName,
+  const std::string &requesterName, const std::string &activityRegex) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.hpp b/catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..9ac7b2b8bd
--- /dev/null
+++ b/catalogue/dummy/DummyRequesterActivityMountRuleCatalogue.hpp
@@ -0,0 +1,49 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyRequesterActivityMountRuleCatalogue : public RequesterActivityMountRuleCatalogue {
+public:
+  DummyRequesterActivityMountRuleCatalogue() = default;
+  ~DummyRequesterActivityMountRuleCatalogue() override = default;
+
+  void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &mountPolicy) override;
+
+  void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &comment) override;
+
+  void createRequesterActivityMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+    const std::string &activityRegex, const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const override;
+
+  void deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName,
+    const std::string &activityRegex) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.cpp b/catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.cpp
new file mode 100644
index 0000000000..44fffb2cd6
--- /dev/null
+++ b/catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.cpp
@@ -0,0 +1,58 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyRequesterGroupMountRuleCatalogue::modifyRequesterGroupMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterGroupName, const std::string &mountPolicy) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterGroupMountRuleCatalogue::modifyRequesterGroupMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterGroupName, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterGroupMountRuleCatalogue::createRequesterGroupMountRule(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &mountPolicyName,
+  const std::string &diskInstanceName, const std::string &requesterGroupName, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::RequesterGroupMountRule>
+  DummyRequesterGroupMountRuleCatalogue::getRequesterGroupMountRules() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+
+void DummyRequesterGroupMountRuleCatalogue::deleteRequesterGroupMountRule(const std::string &diskInstanceName,
+  const std::string &requesterGroupName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.hpp b/catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..1a340ed761
--- /dev/null
+++ b/catalogue/dummy/DummyRequesterGroupMountRuleCatalogue.hpp
@@ -0,0 +1,48 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyRequesterGroupMountRuleCatalogue : public RequesterGroupMountRuleCatalogue {
+public:
+  DummyRequesterGroupMountRuleCatalogue() = default;
+  ~DummyRequesterGroupMountRuleCatalogue() override = default;
+
+  void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) override;
+
+  void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) override;
+
+  void createRequesterGroupMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstanceName, const std::string &requesterGroupName,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const override;
+
+
+  void deleteRequesterGroupMountRule(const std::string &diskInstanceName,
+    const std::string &requesterGroupName) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyRequesterMountRuleCatalogue.cpp b/catalogue/dummy/DummyRequesterMountRuleCatalogue.cpp
new file mode 100644
index 0000000000..91cc174807
--- /dev/null
+++ b/catalogue/dummy/DummyRequesterMountRuleCatalogue.cpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "catalogue/dummy/DummyRequesterMountRuleCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyRequesterMountRuleCatalogue::modifyRequesterMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &mountPolicy) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterMountRuleCatalogue::modifyRequesteMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterMountRuleCatalogue::createRequesterMountRule(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+  const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::RequesterMountRule> DummyRequesterMountRuleCatalogue::getRequesterMountRules() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyRequesterMountRuleCatalogue::deleteRequesterMountRule(const std::string &diskInstanceName,
+  const std::string &requesterName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummyRequesterMountRuleCatalogue.hpp b/catalogue/dummy/DummyRequesterMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..4822252950
--- /dev/null
+++ b/catalogue/dummy/DummyRequesterMountRuleCatalogue.hpp
@@ -0,0 +1,46 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/RequesterMountRuleCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyRequesterMountRuleCatalogue : public RequesterMountRuleCatalogue {
+public:
+  DummyRequesterMountRuleCatalogue() = default;
+  ~DummyRequesterMountRuleCatalogue() override = default;
+
+  void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) override;
+
+  void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &comment) override;
+
+  void createRequesterMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const override;
+
+  void deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/dummy/DummySchemaCatalogue.cpp b/catalogue/dummy/DummySchemaCatalogue.cpp
new file mode 100644
index 0000000000..fd4128e58c
--- /dev/null
+++ b/catalogue/dummy/DummySchemaCatalogue.cpp
@@ -0,0 +1,38 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/dummy/DummySchemaCatalogue.hpp"
+#include "catalogue/SchemaVersion.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SchemaVersion DummySchemaCatalogue::getSchemaVersion() const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummySchemaCatalogue::verifySchemaVersion() {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummySchemaCatalogue::ping() {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummySchemaCatalogue.hpp b/catalogue/dummy/DummySchemaCatalogue.hpp
new file mode 100644
index 0000000000..c465bebc75
--- /dev/null
+++ b/catalogue/dummy/DummySchemaCatalogue.hpp
@@ -0,0 +1,38 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/SchemaCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummySchemaCatalogue: public SchemaCatalogue {
+public:
+  DummySchemaCatalogue() = default;
+  ~DummySchemaCatalogue() override = default;
+
+  SchemaVersion getSchemaVersion() const override;
+
+  void verifySchemaVersion() override;
+
+  void ping() override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyStorageClassCatalogue.cpp b/catalogue/dummy/DummyStorageClassCatalogue.cpp
new file mode 100644
index 0000000000..bb8b46ee2a
--- /dev/null
+++ b/catalogue/dummy/DummyStorageClassCatalogue.cpp
@@ -0,0 +1,64 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/dummy/DummyStorageClassCatalogue.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyStorageClassCatalogue::createStorageClass(
+  const common::dataStructures::SecurityIdentity &admin,
+  const common::dataStructures::StorageClass &storageClass) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyStorageClassCatalogue::deleteStorageClass(const std::string &storageClassName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::StorageClass> DummyStorageClassCatalogue::getStorageClasses() const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::StorageClass DummyStorageClassCatalogue::getStorageClass(const std::string &name) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyStorageClassCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t nbCopies) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyStorageClassCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyStorageClassCatalogue::modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyStorageClassCatalogue::modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/dummy/DummyStorageClassCatalogue.hpp b/catalogue/dummy/DummyStorageClassCatalogue.hpp
new file mode 100644
index 0000000000..dc07ba7176
--- /dev/null
+++ b/catalogue/dummy/DummyStorageClassCatalogue.hpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "catalogue/interfaces/StorageClassCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyStorageClassCatalogue: public StorageClassCatalogue {
+public:
+  DummyStorageClassCatalogue() = default;
+  ~DummyStorageClassCatalogue() override = default;
+
+  void createStorageClass(const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::StorageClass &storageClass) override;
+
+  void deleteStorageClass(const std::string &storageClassName) override;
+
+  std::list<common::dataStructures::StorageClass> getStorageClasses() const override;
+
+  common::dataStructures::StorageClass getStorageClass(const std::string &name) const override;
+
+  void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t nbCopies) override;
+
+  void modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) override;
+
+  void modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyTapeCatalogue.cpp b/catalogue/dummy/DummyTapeCatalogue.cpp
new file mode 100644
index 0000000000..588cae45af
--- /dev/null
+++ b/catalogue/dummy/DummyTapeCatalogue.cpp
@@ -0,0 +1,206 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "catalogue/dummy/DummyTapeCatalogue.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyTapeCatalogue::createTape(const common::dataStructures::SecurityIdentity &admin,
+  const CreateTapeAttributes & tape) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::deleteTape(const std::string &vid) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<common::dataStructures::Tape> DummyTapeCatalogue::getTapes(
+  const TapeSearchCriteria &searchCriteria) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::VidToTapeMap DummyTapeCatalogue::getTapesByVid(const std::string& vid) const {
+  std::set<std::string> vids = {vid};
+  return getTapesByVid(vids);
+}
+
+common::dataStructures::VidToTapeMap DummyTapeCatalogue::getTapesByVid(const std::set<std::string> &vids) const {
+  // Minimal implementation of VidToMap for retrieve request unit tests. We just support
+  // disabled status for the tapes.
+  // If the tape is not listed, it is listed as enabled in the return value.
+  threading::MutexLocker lm(m_tapeEnablingMutex);
+  common::dataStructures::VidToTapeMap ret;
+  for (const auto & v: vids) {
+    try {
+      ret[v].state = m_tapeEnabling.at(v);
+    } catch (std::out_of_range &) {
+      ret[v].state = common::dataStructures::Tape::ACTIVE;
+    }
+  }
+  return ret;
+}
+
+std::map<std::string, std::string> DummyTapeCatalogue::getVidToLogicalLibrary(
+  const std::set<std::string> &vids) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::reclaimTape(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, cta::log::LogContext & lc) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::checkTapeForLabel(const std::string &vid) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::tapeLabelled(const std::string &vid, const std::string &drive) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+uint64_t DummyTapeCatalogue::getNbFilesOnTape(const std::string &vid) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &mediaType) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &vendor) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &logicalLibraryName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &tapePoolName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &encryptionKeyName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &verificationStatus) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity& admin,
+  const std::string& vid, const std::string & reason) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::setTapeFull(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const bool fullValue) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  const bool dirtyValue) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::setTapeIsFromCastorInUnitTests(const std::string &vid) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::setTapeDisabled(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string & reason) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::setTapeDirty(const std::string & vid) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::modifyTapeComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::optional<std::string> &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::tapeMountedForArchive(const std::string &vid, const std::string &drive) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::tapeMountedForRetrieve(const std::string &vid, const std::string &drive) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::noSpaceLeftOnTape(const std::string &vid) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<TapeForWriting> DummyTapeCatalogue::getTapesForWriting(const std::string &logicalLibraryName) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::Label::Format DummyTapeCatalogue::getTapeLabelFormat(const std::string& vid) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeCatalogue::addRepackingTape(const std::string & vid) {
+  threading::MutexLocker lm(m_tapeEnablingMutex);
+  m_tapeEnabling[vid]=common::dataStructures::Tape::REPACKING;
+}
+
+void DummyTapeCatalogue::modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid,
+const common::dataStructures::Tape::State & state,
+  const std::optional<common::dataStructures::Tape::State> & prev_state,
+  const std::optional<std::string> & stateReason) {
+  threading::MutexLocker lm(m_tapeEnablingMutex);
+  if (prev_state.has_value() && prev_state.value() != m_tapeEnabling[vid]) {
+    throw exception::Exception("Previous state mismatch");
+  }
+  m_tapeEnabling[vid]=state;
+}
+
+bool DummyTapeCatalogue::tapeExists(const std::string& vid) const {
+  return m_tapeEnabling.find(vid) != m_tapeEnabling.end();
+}
+
+common::dataStructures::Tape::State DummyTapeCatalogue::getTapeState(const std::string & vid) const {
+  return m_tapeEnabling.at(vid);
+}
+
+// Special functions for unit tests.
+void DummyTapeCatalogue::addEnabledTape(const std::string & vid) {
+  threading::MutexLocker lm(m_tapeEnablingMutex);
+  m_tapeEnabling[vid]=common::dataStructures::Tape::ACTIVE;
+}
+void DummyTapeCatalogue::addDisabledTape(const std::string & vid) {
+  threading::MutexLocker lm(m_tapeEnablingMutex);
+  m_tapeEnabling[vid]=common::dataStructures::Tape::DISABLED;
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyTapeCatalogue.hpp b/catalogue/dummy/DummyTapeCatalogue.hpp
new file mode 100644
index 0000000000..411243e4f9
--- /dev/null
+++ b/catalogue/dummy/DummyTapeCatalogue.hpp
@@ -0,0 +1,121 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/TapeCatalogue.hpp"
+#include "common/dataStructures/Tape.hpp"
+#include "common/threading/Mutex.hpp"
+#include "common/threading/MutexLocker.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyTapeCatalogue : public TapeCatalogue {
+public:
+  DummyTapeCatalogue() = default;
+  ~DummyTapeCatalogue() override = default;
+
+  void createTape(const common::dataStructures::SecurityIdentity &admin, const CreateTapeAttributes & tape) override;
+
+  void deleteTape(const std::string &vid) override;
+
+  std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria &searchCriteria) const override;
+
+  common::dataStructures::Tape::State getTapeState(const std::string & vid) const;
+
+  common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const override;
+
+  common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string> &vids) const override;
+
+  std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override;
+
+  void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    cta::log::LogContext & lc) override;
+
+  void checkTapeForLabel(const std::string &vid) override;
+
+  void tapeLabelled(const std::string &vid, const std::string &drive) override;
+
+  uint64_t getNbFilesOnTape(const std::string &vid) const override;
+
+  void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &mediaType) override;
+
+  void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &vendor) override;
+
+  void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &logicalLibraryName) override;
+
+  void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &tapePoolName) override;
+
+  void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &encryptionKeyName) override;
+
+  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &verificationStatus) override;
+
+  void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid,
+    const common::dataStructures::Tape::State & state,
+    const std::optional<common::dataStructures::Tape::State> & prev_state,
+    const std::optional<std::string> & stateReason) override;
+
+  bool tapeExists(const std::string &vid) const override;
+
+  void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity& admin, const std::string& vid,
+    const std::string & reason) override;
+
+  void setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool fullValue) override;
+
+  void setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool dirtyValue) override;
+
+  void setTapeIsFromCastorInUnitTests(const std::string &vid) override;
+
+  void setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) override;
+
+  void setTapeDirty(const std::string & vid) override;
+
+  void modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::optional<std::string> &comment) override;
+
+  void tapeMountedForArchive(const std::string &vid, const std::string &drive) override;
+
+  void tapeMountedForRetrieve(const std::string &vid, const std::string &drive) override;
+
+  void noSpaceLeftOnTape(const std::string &vid) override;
+
+  std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const override;
+
+  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override;
+
+  // This two methods are for unit tests only in GarbageCollectorTests.cpp, they are not defined in the interface
+  void addEnabledTape(const std::string & vid);
+  void addDisabledTape(const std::string & vid);
+  void addRepackingTape(const std::string & vid);
+
+private:
+  mutable threading::Mutex m_tapeEnablingMutex;
+  std::map<std::string, common::dataStructures::Tape::State> m_tapeEnabling;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyTapeFileCatalogue.cpp b/catalogue/dummy/DummyTapeFileCatalogue.cpp
new file mode 100644
index 0000000000..660b772c0b
--- /dev/null
+++ b/catalogue/dummy/DummyTapeFileCatalogue.cpp
@@ -0,0 +1,48 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "catalogue/dummy/DummyTapeFileCatalogue.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyTapeFileCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapeFileCatalogue::deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+common::dataStructures::RetrieveFileQueueCriteria DummyTapeFileCatalogue::prepareToRetrieveFile(
+  const std::string &diskInstanceName, const uint64_t archiveFileId,
+  const common::dataStructures::RequesterIdentity &user, const std::optional<std::string> & activity,
+  log::LogContext &lc, const std::optional<std::string> &mountPolicyName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyTapeFileCatalogue.hpp b/catalogue/dummy/DummyTapeFileCatalogue.hpp
new file mode 100644
index 0000000000..c37ccc5bf8
--- /dev/null
+++ b/catalogue/dummy/DummyTapeFileCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/TapeFileCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyTapeFileCatalogue : public TapeFileCatalogue {
+public:
+  DummyTapeFileCatalogue() = default;
+  ~DummyTapeFileCatalogue() override = default;
+
+  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override;
+
+  void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) override;
+
+  common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(const std::string &diskInstanceName,
+    const uint64_t archiveFileId, const common::dataStructures::RequesterIdentity &user,
+    const std::optional<std::string> & activity, log::LogContext &lc,
+    const std::optional<std::string> &mountPolicyName = std::nullopt) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyTapePoolCatalogue.cpp b/catalogue/dummy/DummyTapePoolCatalogue.cpp
new file mode 100644
index 0000000000..62da77dd8a
--- /dev/null
+++ b/catalogue/dummy/DummyTapePoolCatalogue.cpp
@@ -0,0 +1,82 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <optional>
+#include <string>
+
+#include "catalogue/dummy/DummyTapePoolCatalogue.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/TapePoolSearchCriteria.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyTapePoolCatalogue::createTapePool(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+  const std::optional<std::string> &supply, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::deleteTapePool(const std::string &name) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::list<TapePool> DummyTapePoolCatalogue::getTapePools(const TapePoolSearchCriteria &searchCriteria) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+std::optional<TapePool> DummyTapePoolCatalogue::getTapePool(const std::string &tapePoolName) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t nbPartialTapes) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool encryptionValue) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &supply) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+void DummyTapePoolCatalogue::modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+bool DummyTapePoolCatalogue::tapePoolExists(const std::string &tapePoolName) const {
+  throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyTapePoolCatalogue.hpp b/catalogue/dummy/DummyTapePoolCatalogue.hpp
new file mode 100644
index 0000000000..14064a7a1b
--- /dev/null
+++ b/catalogue/dummy/DummyTapePoolCatalogue.hpp
@@ -0,0 +1,62 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/interfaces/TapePoolCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyTapePoolCatalogue : public TapePoolCatalogue {
+public:
+  DummyTapePoolCatalogue() = default;
+  ~DummyTapePoolCatalogue() override = default;
+
+  void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+    const std::optional<std::string> &supply, const std::string &comment) override;
+
+  void deleteTapePool(const std::string &name) override;
+
+  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const override;
+
+  std::optional<TapePool> getTapePool(const std::string &tapePoolName) const override;
+
+  void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) override;
+
+  void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const uint64_t nbPartialTapes) override;
+
+  void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+  void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool encryptionValue) override;
+
+  void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &supply) override;
+
+  void modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) override;
+
+  bool tapePoolExists(const std::string& tapePoolName) const override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyVirtualOrganizationCatalogue.cpp b/catalogue/dummy/DummyVirtualOrganizationCatalogue.cpp
new file mode 100644
index 0000000000..68fcd9adaf
--- /dev/null
+++ b/catalogue/dummy/DummyVirtualOrganizationCatalogue.cpp
@@ -0,0 +1,84 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <string>
+
+#include "catalogue/dummy/DummyVirtualOrganizationCatalogue.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+void DummyVirtualOrganizationCatalogue::createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin,
+  const common::dataStructures::VirtualOrganization &vo) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::deleteVirtualOrganization(const std::string &voName) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+std::list<common::dataStructures::VirtualOrganization> DummyVirtualOrganizationCatalogue::getVirtualOrganizations() const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+common::dataStructures::VirtualOrganization DummyVirtualOrganizationCatalogue::getVirtualOrganizationOfTapepool(
+  const std::string &tapepoolName) const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+common::dataStructures::VirtualOrganization DummyVirtualOrganizationCatalogue::getCachedVirtualOrganizationOfTapepool(
+  const std::string & tapepoolName) const {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::modifyVirtualOrganizationName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+  const std::string &newVoName) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::modifyVirtualOrganizationReadMaxDrives(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::modifyVirtualOrganizationWriteMaxDrives(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::modifyVirtualOrganizationMaxFileSize(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::modifyVirtualOrganizationComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+void DummyVirtualOrganizationCatalogue::modifyVirtualOrganizationDiskInstanceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) {
+  throw exception::Exception(std::string("In ") + __PRETTY_FUNCTION__ + ": not implemented");
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/dummy/DummyVirtualOrganizationCatalogue.hpp b/catalogue/dummy/DummyVirtualOrganizationCatalogue.hpp
new file mode 100644
index 0000000000..936eaac300
--- /dev/null
+++ b/catalogue/dummy/DummyVirtualOrganizationCatalogue.hpp
@@ -0,0 +1,67 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "catalogue/interfaces/VirtualOrganizationCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DummyVirtualOrganizationCatalogue: public VirtualOrganizationCatalogue {
+public:
+  DummyVirtualOrganizationCatalogue() = default;
+  ~DummyVirtualOrganizationCatalogue() override = default;
+
+  void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::VirtualOrganization &vo) override;
+
+  void deleteVirtualOrganization(const std::string &voName) override;
+
+  std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const override;
+
+  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const override;
+
+  common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const override;
+
+  void modifyVirtualOrganizationName(
+    const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+    const std::string &newVoName) override;
+
+  void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t readMaxDrives) override;
+
+  void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t writeMaxDrives) override;
+
+  void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t maxFileSize) override;
+
+  void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &comment) override;
+
+  void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &diskInstance) override;
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/AdminUserCatalogue.hpp b/catalogue/interfaces/AdminUserCatalogue.hpp
new file mode 100644
index 0000000000..97513e94e9
--- /dev/null
+++ b/catalogue/interfaces/AdminUserCatalogue.hpp
@@ -0,0 +1,64 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct AdminUser;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringUsername);
+
+class AdminUserCatalogue {
+public:
+  virtual ~AdminUserCatalogue() = default;
+
+  virtual void createAdminUser(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &username, const std::string &comment) = 0;
+
+  virtual void deleteAdminUser(const std::string &username) = 0;
+
+  virtual std::list<common::dataStructures::AdminUser> getAdminUsers() const = 0;
+
+  virtual void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &username, const std::string &comment) = 0;
+
+  /**
+   * Returns true if the specified user running the CTA command-line tool on
+   * the specified host has administrator privileges.
+   *
+   * @param admin The administrator.
+   * @return True if the specified user running the CTA command-line tool on
+   * the specified host has administrator privileges.
+   */
+  virtual bool isAdmin(const common::dataStructures::SecurityIdentity &admin) const = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/ArchiveFileCatalogue.hpp b/catalogue/interfaces/ArchiveFileCatalogue.hpp
new file mode 100644
index 0000000000..faf4fb3184
--- /dev/null
+++ b/catalogue/interfaces/ArchiveFileCatalogue.hpp
@@ -0,0 +1,230 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <list>
+#include <string>
+
+#include "catalogue/TapeFileSearchCriteria.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct ArchiveFile;
+struct ArchiveFileQueueCriteria;
+struct ArchiveFileSummary;
+struct DeleteArchiveRequest;
+struct RequesterIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace log {
+struct LogContext;
+}
+
+namespace catalogue {
+
+template <typename Item>
+class CatalogueItor;
+
+using ArchiveFileItor = CatalogueItor<common::dataStructures::ArchiveFile>;
+
+class ArchiveFileCatalogue {
+public:
+  virtual ~ArchiveFileCatalogue() = default;
+
+  /**
+   * Checks the specified archival could take place and returns a new and
+   * unique archive file identifier that can be used by a new archive file
+   * within the catalogue.
+   *
+   * @param diskInstanceName The name of the disk instance to which the
+   * storage class belongs.
+   * @param storageClassName The name of the storage class of the file to be
+   * archived.  The storage class name is only guaranteed to be unique within
+   * its disk instance.  The storage class name will be used by the Catalogue
+   * to determine the destination tape pool for each tape copy.
+   * @param user The user for whom the file is to be archived.  This will be
+   * used by the Catalogue to determine the mount policy to be used when
+   * archiving the file.
+   * @return The new archive file identifier.
+   * @throw UserErrorWithCacheInfo if there was a user error.
+   */
+  virtual uint64_t checkAndGetNextArchiveFileId(
+    const std::string &diskInstanceName,
+    const std::string &storageClassName,
+    const common::dataStructures::RequesterIdentity &user) = 0;
+
+  /**
+   * Returns the information required to queue an archive request.
+   *
+   * @param diskInstanceName The name of the disk instance to which the
+   * storage class belongs.
+   * @param storageClassName The name of the storage class of the file to be
+   * archived.  The storage class name is only guaranteed to be unique within
+   * its disk instance.  The storage class name will be used by the Catalogue
+   * to determine the destination tape pool for each tape copy.
+   * @param user The user for whom the file is to be archived.  This will be
+   * used by the Catalogue to determine the mount policy to be used when
+   * archiving the file.
+   * @return The information required to queue an archive request.
+   */
+  virtual common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(
+    const std::string &diskInstanceName,
+    const std::string &storageClassName,
+    const common::dataStructures::RequesterIdentity &user) = 0;
+
+  /**
+   * Returns the specified archive files.  Please note that the list of files
+   * is ordered by archive file ID.
+   *
+   * @param searchCriteria The search criteria.
+   * @return The archive files.
+   */
+  virtual ArchiveFileItor getArchiveFilesItor(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const = 0;
+
+  /**
+   * Returns the specified archive file. If the search criteria result in more than one tape file being returned
+   * an exception is thrown.
+   * @param searchCriteria The search criteria.
+   * @return The archive file.
+   */
+  virtual common::dataStructures::ArchiveFile getArchiveFileForDeletion(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const = 0;
+
+  /**
+   * Returns the specified files in tape file sequence order.
+   *
+   * @param vid The volume identifier of the tape.
+   * @param startFSeq The file sequence number of the first file.  Please note
+   * that there might not be a file with this exact file sequence number.
+   * @param maxNbFiles The maximum number of files to be returned.
+   * @return The specified files in tape file sequence order.
+   */
+  virtual std::list<common::dataStructures::ArchiveFile> getFilesForRepack(
+    const std::string &vid,
+    const uint64_t startFSeq,
+    const uint64_t maxNbFiles) const = 0;
+
+  /**
+   * Returns all the tape copies (no matter their VIDs) of the archive files
+   * associated with the tape files on the specified tape in FSEQ order
+   * starting at the specified startFSeq.
+   *
+   * @param vid The volume identifier of the tape.
+   * @param startFSeq The file sequence number of the first file.  Please note
+   * that there might not be a file with this exact file sequence number.
+   * @return The specified files in FSEQ order.
+   */
+  virtual ArchiveFileItor getArchiveFilesForRepackItor(
+    const std::string &vid,
+    const uint64_t startFSeq) const = 0;
+
+  /**
+   * Returns a summary of the tape files that meet the specified search
+   * criteria.
+   *
+   * @param searchCriteria The search criteria.
+   * @return The summary.
+   */
+  virtual common::dataStructures::ArchiveFileSummary getTapeFileSummary(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const = 0;
+
+  /**
+   * Returns the archive file with the specified unique identifier.
+   *
+   * This method assumes that the archive file being requested exists and will
+   * therefore throw an exception if it does not.
+   *
+   * Please note that an archive file with no associated tape files is
+   * considered not to exist by this method.
+   *
+   * @param id The unique identifier of the archive file.
+   * @return The archive file.
+   */
+  virtual common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const = 0;
+
+  /**
+  * Changes the name of hte storage class
+  * @param archiveFileId Id for file found in ARCHIVE_FILE
+  * @param newStorageClassName The name of the storage class
+  */
+  virtual void modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+    const std::string& newStorageClassName) const = 0;
+
+   /**
+  * Changes the fxid in for a archive file
+  * @param archiveId The archive file id
+  * @param fxId The eos fxid related to the archive file
+  * @param diskInstance Disk instace
+  */
+  virtual void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId,
+    const std::string &diskInstance) const = 0;
+
+  /**
+   * Insert the ArchiveFile and all its tape files in the FILE_RECYCLE_LOG table.
+   * There will be one entry on the FILE_RECYCLE_LOG table per deleted tape file
+   *
+   * @param request the DeleteRequest object that holds information about the file to delete.
+   * @param lc the logContext
+   */
+  virtual void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
+    log::LogContext & lc) = 0;
+
+  /**
+   * Updates the disk file ID of the specified archive file.
+   *
+   * @param archiveFileId The unique identifier of the archive file.
+   * @param diskInstance The instance name of the source disk system.
+   * @param diskFileId The identifier of the source disk file which is unique
+   * within it's host disk system.  Two files from different disk systems may
+   * have the same identifier.  The combination of diskInstance and diskFileId
+   * must be globally unique, in other words unique within the CTA catalogue.
+   */
+  virtual void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
+    const std::string &diskFileId) = 0;
+
+  /**
+   * !!!!!!!!!!!!!!!!!!! THIS METHOD SHOULD NOT BE USED !!!!!!!!!!!!!!!!!!!!!!!
+   * Deletes the specified archive file and its associated tape copies from the
+   * catalogue.
+   *
+   * Please note that the name of the disk instance is specified in order to
+   * prevent a disk instance deleting an archive file that belongs to another
+   * disk instance.
+   *
+   * Please note that this method is idempotent.  If the file to be deleted does
+   * not exist in the CTA catalogue then this method returns without error.
+   *
+   * @param instanceName The name of the instance from where the deletion request
+   * originated
+   * @param archiveFileId The unique identifier of the archive file.
+   * @param lc The log context.
+   * @return The metadata of the deleted archive file including the metadata of
+   * the associated and also deleted tape copies.
+   */
+  virtual void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &instanceName, const uint64_t archiveFileId,
+    log::LogContext &lc) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/ArchiveRouteCatalogue.hpp b/catalogue/interfaces/ArchiveRouteCatalogue.hpp
new file mode 100644
index 0000000000..7c5e157f6f
--- /dev/null
+++ b/catalogue/interfaces/ArchiveRouteCatalogue.hpp
@@ -0,0 +1,97 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+#include <list>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct ArchiveRoute;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentArchiveRoute);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroCopyNb);
+
+class ArchiveRouteCatalogue {
+public:
+  virtual ~ArchiveRouteCatalogue() = default;
+
+  virtual void createArchiveRoute(
+    const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName,
+    const uint32_t copyNb,
+    const std::string &tapePoolName,
+    const std::string &comment) = 0;
+
+  /**
+   * Deletes the specified archive route.
+   *
+   * @param storageClassName The name of the storage class.
+   * @param copyNb The copy number of the tape file.
+   */
+  virtual void deleteArchiveRoute(
+    const std::string &storageClassName,
+    const uint32_t copyNb) = 0;
+
+  virtual std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const = 0;
+
+  /**
+   * @return the archive routes of the given storage class and destination tape
+   * pool.
+   *
+   * Under normal circumstances this method should return either 0 or 1 route.
+   * For a given storage class there should be no more than one route to any
+   * given tape pool.
+   *
+   * @param storageClassName The name of the storage class.
+   * @param tapePoolName The name of the tape pool.
+   */
+  virtual std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(
+    const std::string &storageClassName,
+    const std::string &tapePoolName) const = 0;
+
+  /**
+   * Modifies the tape pool of the specified archive route.
+   *
+   * @param admin The administrator.
+   * @param storageClassName The name of the storage class.
+   * @param copyNb The copy number.
+   * @param tapePoolName The name of the tape pool.
+   * @throw UserSpecifiedANonExistentTapePool if the user specified a
+   * non-existent tape pool.
+   */
+  virtual void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) = 0;
+
+  virtual void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/DiskInstanceCatalogue.hpp b/catalogue/interfaces/DiskInstanceCatalogue.hpp
new file mode 100644
index 0000000000..b93af857e6
--- /dev/null
+++ b/catalogue/interfaces/DiskInstanceCatalogue.hpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct DiskInstance;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskInstanceName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyDiskInstanceAfterDelete);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentDiskInstance);
+
+class DiskInstanceCatalogue {
+public:
+  virtual ~DiskInstanceCatalogue() = default;
+
+  virtual void createDiskInstance(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) = 0;
+
+  virtual void deleteDiskInstance(const std::string &name) = 0;
+
+  virtual void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) = 0;
+
+  virtual std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp b/catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp
new file mode 100644
index 0000000000..cb147245e9
--- /dev/null
+++ b/catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp
@@ -0,0 +1,89 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct DiskInstanceSpace;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskInstanceSpaceName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyDiskInstanceSpaceAfterDelete);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentDiskInstanceSpace);
+
+class DiskInstanceSpaceCatalogue {
+public:
+  virtual ~DiskInstanceSpaceCatalogue() = default;
+
+  /**
+   * Deletes a disk instance space.
+   *
+   * @param name The name of the disk instance.
+   * @param diskInstance The disk instance of the disk instance space.
+   */
+  virtual void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) = 0;
+
+  /**
+   * Creates the specified Disk Instance Space
+   * @param admin The administrator.
+   * @param name the name of the new disk instance space
+   * @param diskInstance the disk instance associated to the disk instance space
+   * @param freeSpaceQueryURL the URL to query to obtain the disk instance space free space
+   * @param refreshInterval the period to query for disk instance space free space
+   * @param comment the comment of the new disk instance space
+   */
+  virtual void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name,
+    const std::string &diskInstance,
+    const std::string &freeSpaceQueryURL,
+    const uint64_t refreshInterval,
+    const std::string &comment) = 0;
+
+  /**
+   * Returns all the disk instance spaces within the CTA catalogue.
+   *
+   * @return The disk instance spaces in the CTA catalogue.
+   */
+  virtual std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const = 0;
+
+  virtual void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &comment) = 0;
+
+  virtual void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) = 0;
+
+  virtual void modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+    const std::string &diskInstance, const uint64_t freeSpace) = 0;
+
+  virtual void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/DiskSystemCatalogue.hpp b/catalogue/interfaces/DiskSystemCatalogue.hpp
new file mode 100644
index 0000000000..ac539aba0b
--- /dev/null
+++ b/catalogue/interfaces/DiskSystemCatalogue.hpp
@@ -0,0 +1,106 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+}
+}
+
+namespace disk {
+class DiskSystemList;
+}
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskSystemName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringFileRegexp);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyDiskSystemAfterDelete);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentDiskSystem);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroSleepTime);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroTargetedFreeSpace);
+
+class DiskSystemCatalogue {
+public:
+  virtual ~DiskSystemCatalogue() = default;
+
+  /**
+   * Creates a disk system.
+   *
+   * @param admin The administrator.
+   * @param name The name of the disk system.
+   * @param fileRegexp The regular expression allowing matching destination URLs
+   * for this disk system.
+   * @param freeSpaceQueryURL The query URL that describes a method to query the
+   * free space from the disk system.
+   * @param refreshInterval The refresh interval (seconds) defining how long do
+   * we use a free space value.
+   * @param targetedFreeSpace The targeted free space (margin) based on the free
+   * space update latency (inherent to the file system and induced by the refresh
+   * interval), and the expected external bandwidth from sources external to CTA.
+   * @param comment Comment.
+   */
+  virtual void createDiskSystem(
+    const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name,
+    const std::string &diskInstanceName,
+    const std::string &diskInstanceSpaceName,
+    const std::string &fileRegexp,
+    const uint64_t targetedFreeSpace,
+    const time_t sleepTime,
+    const std::string &comment) = 0;
+
+  /**
+   * Deletes a disk system.
+   *
+   * @param name The name of the disk system.
+   */
+  virtual void deleteDiskSystem(const std::string &name) = 0;
+
+  virtual disk::DiskSystemList getAllDiskSystems() const = 0;
+
+  virtual void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &fileRegexp) = 0;
+
+  virtual void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t targetedFreeSpace) = 0;
+
+  virtual void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t sleepTime) = 0;
+
+  virtual void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) = 0;
+
+  virtual void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceName) = 0;
+
+  virtual void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceSpaceName) = 0;
+
+  virtual bool diskSystemExists(const std::string &name) const = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/DriveConfigCatalogue.hpp b/catalogue/interfaces/DriveConfigCatalogue.hpp
new file mode 100644
index 0000000000..96156bdeba
--- /dev/null
+++ b/catalogue/interfaces/DriveConfigCatalogue.hpp
@@ -0,0 +1,99 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+namespace cta {
+
+namespace catalogue {
+
+/**
+ * Specifies the interface to a factory Catalogue objects.
+ */
+class DriveConfigCatalogue {
+public:
+  virtual ~DriveConfigCatalogue() = default;
+
+  /*
+   * Struct with a drive configuration
+   */
+  struct DriveConfig {
+    std::string tapeDriveName;
+    std::string category;
+    std::string keyName;
+    std::string value;
+    std::string source;
+  };
+
+  /**
+   * Creates a specified parameter of the configuration for a certain Tape Drive
+   * @param tapeDriveName The name of the tape drive.
+   * @param category The category of the parameter.
+   * @param keyName The key of the parameter.
+   * @param value The value of the parameter.
+   * @param source The source from which the parameter was gotten.
+   */
+  virtual void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) = 0;
+
+  /**
+   * Gets all Drive Configurations of all TapeDrives.
+   * @return Drive Configurations of all TapeDrives.
+   */
+  virtual std::list<DriveConfig> getTapeDriveConfigs() const = 0;
+
+  /**
+   * Gets the Key and Names of configurations of all TapeDrives
+   * @return Keys and Names of configurations.
+   */
+  virtual std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const = 0;
+
+  /**
+   * Modifies a specified parameter of the configuration for a certain Tape Drive
+   * @param tapeDriveName The name of the tape drive.
+   * @param category The category of the parameter.
+   * @param keyName The key of the parameter.
+   * @param value The value of the parameter.
+   * @param source The source from which the parameter was gotten.
+   */
+  virtual void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) = 0;
+
+  /**
+   * Gets a specified parameter of the configuration for a certain Tape Drive
+   * @param tapeDriveName The name of the tape drive.
+   * @param keyName The key of the parameter.
+   * @return Returns the category, value and source of a parameter of the configuarion
+   */
+  virtual std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig(const std::string &tapeDriveName,
+    const std::string &keyName) const = 0;
+
+  /**
+   * Deletes the entry of a Drive Configuration
+   * @param tapeDriveName The name of the tape drive.
+   */
+  virtual void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) = 0;
+};  // class TapeDriveCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/interfaces/DriveStateCatalogue.hpp b/catalogue/interfaces/DriveStateCatalogue.hpp
new file mode 100644
index 0000000000..210e5b22f0
--- /dev/null
+++ b/catalogue/interfaces/DriveStateCatalogue.hpp
@@ -0,0 +1,119 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <optional>
+#include <string>
+
+#include "common/dataStructures/DiskSpaceReservationRequest.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+class DesiredDriveState;
+struct TapeDrive;
+struct TapeDriveStatistics;
+}
+}
+
+namespace log {
+class LogContext;
+}
+
+namespace catalogue {
+
+/**
+ * Specifies the interface to a factory Catalogue objects.
+ */
+class DriveStateCatalogue {
+public:
+  virtual ~DriveStateCatalogue() = default;
+
+  /**
+   * Creates the specified Tape Drive
+   * @param tapeDrive Parameters of the Tape Drive.
+   */
+  virtual void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) = 0;
+
+  /**
+   * Gets the names of all stored Tape Drive
+   * @return List of tape drive names
+   */
+  virtual std::list<std::string> getTapeDriveNames() const = 0;
+
+  /**
+   * Gets the information of all Tape Drives
+   * @return Parameters of all Tape Drives.
+   */
+  virtual std::list<common::dataStructures::TapeDrive> getTapeDrives() const = 0;
+
+  /**
+   * Gets the information of the specified Tape Drive
+   * @param tapeDriveName The name of the tape drive.
+   * @return Parameters of the Tape Drive.
+   */
+  virtual std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const = 0;
+
+  /**
+   * Modifies the desired state parameters off the specified Tape Drive
+   * @param tapeDriveName Name of the Tape Drive.
+   * @param desiredState Desired state parameters of the Tape Drive.
+   */
+  virtual void setDesiredTapeDriveState(const std::string& tapeDriveName,
+      const common::dataStructures::DesiredDriveState &desiredState) = 0;
+
+  virtual void setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+    const std::string &comment) = 0;
+
+  virtual void updateTapeDriveStatistics(const std::string& tapeDriveName,
+    const std::string& host, const std::string& logicalLibrary,
+    const common::dataStructures::TapeDriveStatistics& statistics) = 0;
+
+  virtual void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) = 0;
+
+  /**
+   * Deletes the entry of a Tape Drive
+   * @param tapeDriveName The name of the tape drive.
+   */
+  virtual void deleteTapeDrive(const std::string &tapeDriveName) = 0;
+
+  /**
+   * Gets the disk space reservations for all disk systems
+   */
+  virtual std::map<std::string, uint64_t> getDiskSpaceReservations() const = 0;
+
+  /**
+   * Adds to the current disk space reservation
+   */
+  virtual void reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) = 0;
+
+  /**
+   * Subtracts from the current disk space reservation.
+   *
+   * If the amount released exceeds the current reservation, the reservation will be reduced to zero.
+   */
+  virtual void releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) = 0;
+};  // class DriveStateCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/interfaces/FileRecycleLogCatalogue.hpp b/catalogue/interfaces/FileRecycleLogCatalogue.hpp
new file mode 100644
index 0000000000..21a8002298
--- /dev/null
+++ b/catalogue/interfaces/FileRecycleLogCatalogue.hpp
@@ -0,0 +1,80 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/RecyleTapeFileSearchCriteria.hpp"
+
+namespace cta {
+namespace common {
+namespace dataStructures {
+struct FileRecycleLog;
+}  // namespace dataStructures
+}  // namespace common
+
+namespace log {
+struct LogContext;
+}
+
+namespace catalogue {
+
+template <typename Item>
+class CatalogueItor;
+
+using FileRecycleLogItor = CatalogueItor<common::dataStructures::FileRecycleLog>;
+
+/**
+ * Specifies the interface to a factory Catalogue objects.
+ */
+class FileRecycleLogCatalogue {
+public:
+  virtual ~FileRecycleLogCatalogue() = default;
+
+  /**
+   * Returns all the currently deleted files by looking at the FILE_RECYCLE_LOG table
+   *
+   * @return The deleted archive files ordered by archive file ID.
+   */
+  virtual FileRecycleLogItor getFileRecycleLogItor(
+    const RecycleTapeFileSearchCriteria & searchCriteria = RecycleTapeFileSearchCriteria()) const = 0;
+
+  /**
+   * Restores the deleted file in the Recycle log that match the criteria passed
+   *
+   * @param searchCriteria The search criteria
+   * @param newFid the new Fid of the archive file (if the archive file must be restored)
+   */
+  virtual void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+    const std::string &newFid) = 0;
+
+  /**
+   * Deletes all the log entries corresponding to the vid passed in parameter.
+   *
+   * Please note that this method is idempotent.  If there are no recycle log
+   * entries associated to the vid passed in parameter, the method will return
+   * without any error.
+   *
+   * @param vid, the vid of the files to be deleted
+   * @param lc, the logContext
+   */
+  virtual void deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) = 0;
+};  // class FileRecyleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/interfaces/LogicalLibraryCatalogue.hpp b/catalogue/interfaces/LogicalLibraryCatalogue.hpp
new file mode 100644
index 0000000000..ec0aae840d
--- /dev/null
+++ b/catalogue/interfaces/LogicalLibraryCatalogue.hpp
@@ -0,0 +1,73 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct LogicalLibrary;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringLogicalLibraryName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyLogicalLibrary);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentLogicalLibrary);
+
+
+class LogicalLibraryCatalogue {
+public:
+  virtual ~LogicalLibraryCatalogue() = default;
+
+  virtual void createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool isDisabled, const std::string &comment) = 0;
+
+  virtual void deleteLogicalLibrary(const std::string &name) = 0;
+
+  virtual std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const = 0;
+
+  /**
+   * Modifies the name of the specified logical library.
+   *
+   * @param admin The administrator.
+   * @param currentName The current name of the logical library.
+   * @param newName The new name of the logical library.
+   */
+  virtual void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) = 0;
+
+  virtual void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) = 0;
+
+  virtual void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &disabledReason) = 0;
+
+  virtual void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool disabledValue) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/MediaTypeCatalogue.hpp b/catalogue/interfaces/MediaTypeCatalogue.hpp
new file mode 100644
index 0000000000..640bcf2d84
--- /dev/null
+++ b/catalogue/interfaces/MediaTypeCatalogue.hpp
@@ -0,0 +1,171 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+}  // namespace dataStructures
+}  // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringMediaTypeName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringMediaType);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedMediaTypeUsedByTapes);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroCapacity);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringCartridge);
+
+struct MediaType;
+struct MediaTypeWithLogs;
+
+class MediaTypeCatalogue {
+public:
+  virtual ~MediaTypeCatalogue() = default;
+
+  /**
+   * Creates a tape media type.
+   *
+   * @param admin The administrator.
+   * @param mediaType The tape media type.
+   */
+  virtual void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) = 0;
+
+  /**
+   * Deletes the specified tape media type.
+   *
+   * @param name The name of the tape media type.
+   */
+  virtual void deleteMediaType(const std::string &name) = 0;
+
+  /**
+   * Returns all tape media types.
+   *
+   * @return All tape media types.
+   */
+  virtual std::list<MediaTypeWithLogs> getMediaTypes() const = 0;
+
+  /**
+   * Return the media type associated to the tape corresponding to the
+   * vid passed in parameter
+   * @param vid the vid of the tape to return its media type
+   * @return the media type associated to the tape corresponding to the vid passed in parameter
+   */
+  virtual MediaType getMediaTypeByVid(const std::string & vid) const = 0;
+
+  /**
+   * Modifies the name of the specified tape media type.
+   *
+   * @param admin The administrator.
+   * @param currentName The current name of the tape media type.
+   * @param newName The new name of the tape media type.
+   */
+  virtual void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) = 0;
+
+  /**
+   * Modifies the cartidge of the specified tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param cartridge The new cartidge.
+   */
+  virtual void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &cartridge) = 0;
+
+  /**
+   * Modify the capacity in bytes of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param capacityInBytes The new capacity in bytes.
+   */
+  virtual void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t capacityInBytes) = 0;
+
+  /**
+   * Modify the SCSI primary density code of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param primaryDensityCode The new SCSI primary density code.
+   */
+  virtual void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint8_t primaryDensityCode) = 0;
+
+  /**
+   * Modify the SCSI secondary density code of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param secondaryDensityCode The new SCSI secondary density code.
+   */
+  virtual void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint8_t secondaryDensityCode) = 0;
+
+  /**
+   * Modify the number of tape wraps of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param nbWraps The new number of tape wraps.
+   */
+  virtual void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint32_t> &nbWraps) = 0;
+
+  /**
+   * Modify the minimum longitudinal tape position of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param minLPos The new minimum longitudinal tape position.
+   */
+  virtual void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &minLPos) = 0;
+
+  /**
+   * Modify the maximum longitudinal tape position of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param maxLPos The new maximum longitudinal tape position.
+   */
+  virtual void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &maxLPos) = 0;
+
+  /**
+   * Modify the comment of a tape media type.
+   *
+   * @param admin The administrator.
+   * @param name The name of the tape media type.
+   * @param comment The new comment.
+   */
+  virtual void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/MountPolicyCatalogue.hpp b/catalogue/interfaces/MountPolicyCatalogue.hpp
new file mode 100644
index 0000000000..444200c9fc
--- /dev/null
+++ b/catalogue/interfaces/MountPolicyCatalogue.hpp
@@ -0,0 +1,92 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <optional>
+#include <string>
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct MountPolicy;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+class CreateMountPolicyAttributes;
+
+class MountPolicyCatalogue {
+public:
+  virtual ~MountPolicyCatalogue() = default;
+
+  virtual void createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+    const CreateMountPolicyAttributes & mountPolicy) = 0;
+
+  /**
+   * Returns the list of all existing mount policies.
+   *
+   * @return the list of all existing mount policies.
+   */
+  virtual std::list<common::dataStructures::MountPolicy> getMountPolicies() const = 0;
+
+  /**
+   * Returns the mount policy with the specified name.
+   *
+   * @return the specified mount policy
+   */
+  virtual std::optional<common::dataStructures::MountPolicy> getMountPolicy(
+    const std::string &mountPolicyName) const = 0;
+
+
+  /**
+   * Returns the cached list of all existing mount policies.
+   *
+   * @return the list of all existing mount policies.
+   */
+  virtual std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const = 0;
+
+  /**
+   * Deletes the specified mount policy.
+   *
+   * @param name The name of the mount policy.
+   */
+  virtual void deleteMountPolicy(const std::string &name) = 0;
+
+  virtual void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t archivePriority) = 0;
+
+  virtual void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minArchiveRequestAge) = 0;
+
+  virtual void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t retrievePriority) = 0;
+
+  virtual void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minRetrieveRequestAge) = 0;
+
+  virtual void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) = 0;
+
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp b/catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..ed0632b07b
--- /dev/null
+++ b/catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp
@@ -0,0 +1,90 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct RequesterActivityMountRule;
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+class RequesterActivityMountRuleCatalogue {
+public:
+  virtual ~RequesterActivityMountRuleCatalogue() = default;
+
+  virtual void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &mountPolicy) = 0;
+
+  virtual void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &comment) = 0;
+
+  /**
+   * Creates the rule that the specified mount policy will be used for the
+   * specified requester+matching activities.
+   *
+   * Please note that requester-activity mount-rules overrule requester
+   * mount-rules.
+   *
+   * @param admin The administrator.
+   * @param mountPolicyName The name of the mount policy.
+   * @param diskInstance The name of the disk instance to which the requester
+   * belongs.
+   * @param activityRegex The regex to match request activities
+   * @param requesterName The name of the requester which is only guarantted to
+   * be unique within its disk instance.
+   * @param comment Comment.
+   */
+  virtual void createRequesterActivityMountRule(
+    const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName,
+    const std::string &diskInstance,
+    const std::string &requesterName,
+    const std::string &activityRegex,
+    const std::string &comment) = 0;
+
+  /**
+   * Returns the rules that specify which mount policy is be used for which
+   * requester + activity.
+   *
+   * @return the rules that specify which mount policy is be used for which
+   * requester + activity.
+   */
+  virtual std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const = 0;
+
+    /**
+   * Deletes the specified mount rule.
+   *
+   * @param diskInstanceName The name of the disk instance to which the
+   * requester belongs.
+   * @param requesterName The name of the requester which is only guaranteed to
+   * be unique within its disk instance.
+   * @param activityRegex The regex to match request activities
+   */
+  virtual void deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName,
+    const std::string &activityRegex) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp b/catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..ba084a00dc
--- /dev/null
+++ b/catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp
@@ -0,0 +1,89 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+class SecurityIdentity;
+struct RequesterGroupMountRule;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+class RequesterGroupMountRuleCatalogue {
+public:
+  virtual ~RequesterGroupMountRuleCatalogue() = default;
+
+  virtual void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) = 0;
+
+  virtual void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) = 0;
+
+  /**
+   * Creates the rule that the specified mount policy will be used for the
+   * specified requester group.
+   *
+   * Please note that requester mount-rules overrule requester-group
+   * mount-rules.
+   *
+   * @param admin The administrator.
+   * @param mountPolicyName The name of the mount policy.
+   * @param diskInstanceName The name of the disk instance to which the
+   * requester group belongs.
+   * @param requesterGroupName The name of the requester group which is only
+   * guarantted to be unique within its disk instance.
+   * @param comment Comment.
+   */
+  virtual void createRequesterGroupMountRule(
+    const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName,
+    const std::string &diskInstanceName,
+    const std::string &requesterGroupName,
+    const std::string &comment) = 0;
+
+  /**
+   * Returns the rules that specify which mount policy is be used for which
+   * requester group.
+   *
+   * @return the rules that specify which mount policy is be used for which
+   * requester group.
+   */
+  virtual std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const = 0;
+
+  /**
+   * Deletes the specified mount rule.
+   *
+   * @param diskInstanceName The name of the disk instance to which the
+   * requester group belongs.
+   * @param requesterGroupName The name of the requester group which is only
+   * guaranteed to be unique within its disk instance.
+   */
+  virtual void deleteRequesterGroupMountRule(
+    const std::string &diskInstanceName,
+    const std::string &requesterGroupName) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/interfaces/RequesterMountRuleCatalogue.hpp b/catalogue/interfaces/RequesterMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..698425b1ce
--- /dev/null
+++ b/catalogue/interfaces/RequesterMountRuleCatalogue.hpp
@@ -0,0 +1,86 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+class SecurityIdentity;
+struct RequesterMountRule;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+class RequesterMountRuleCatalogue {
+public:
+  virtual ~RequesterMountRuleCatalogue() = default;
+
+  virtual void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) = 0;
+
+  virtual void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &comment) = 0;
+
+  /**
+   * Creates the rule that the specified mount policy will be used for the
+   * specified requester.
+   *
+   * Please note that requester mount-rules overrule requester-group
+   * mount-rules.
+   *
+   * @param admin The administrator.
+   * @param mountPolicyName The name of the mount policy.
+   * @param diskInstance The name of the disk instance to which the requester
+   * belongs.
+   * @param requesterName The name of the requester which is only guarantted to
+   * be unique within its disk instance.
+   * @param comment Comment.
+   */
+  virtual void createRequesterMountRule(
+    const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName,
+    const std::string &diskInstance,
+    const std::string &requesterName,
+    const std::string &comment) = 0;
+
+  /**
+   * Returns the rules that specify which mount policy is be used for which
+   * requester.
+   *
+   * @return the rules that specify which mount policy is be used for which
+   * requester.
+   */
+  virtual std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const = 0;
+
+  /**
+   * Deletes the specified mount rule.
+   *
+   * @param diskInstanceName The name of the disk instance to which the
+   * requester belongs.
+   * @param requesterName The name of the requester which is only guaranteed to
+   * be unique within its disk instance.
+   */
+  virtual void deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/SchemaCatalogue.hpp b/catalogue/interfaces/SchemaCatalogue.hpp
new file mode 100644
index 0000000000..f02b92872d
--- /dev/null
+++ b/catalogue/interfaces/SchemaCatalogue.hpp
@@ -0,0 +1,58 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "common/exception/Exception.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class SchemaVersion;
+
+CTA_GENERATE_EXCEPTION_CLASS(WrongSchemaVersionException);
+
+class SchemaCatalogue {
+public:
+  virtual ~SchemaCatalogue() = default;
+
+  /**
+   * Returns the SchemaVersion object corresponding to the catalogue schema version:
+   * - SCHEMA_VERSION_MAJOR
+   * - SCHEMA_VERSION_MINOR
+   * - SCHEMA_VERSION_MAJOR_NEXT (future major version number of the schema in case of upgrade)
+   * - SCHEMA_VERSION_MINOR_NEXT (future minor version number of the schema in case of upgrade)
+   * - STATUS (UPGRADING or PRODUCTION)
+   *
+   * @return The SchemaVersion object corresponding to the catalogue schema version
+   */
+  virtual SchemaVersion getSchemaVersion() const = 0;
+
+  /**
+   * Checks that the online database schema MAJOR version number matches
+   * the schema MAJOR version number defined in version.h
+   */
+  virtual void verifySchemaVersion() = 0;
+
+  /**
+   * Checks that the most trivial query goes through. Throws an exception if not.
+   */
+  virtual void ping() = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/StorageClassCatalogue.hpp b/catalogue/interfaces/StorageClassCatalogue.hpp
new file mode 100644
index 0000000000..6c209266bc
--- /dev/null
+++ b/catalogue/interfaces/StorageClassCatalogue.hpp
@@ -0,0 +1,88 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+struct StorageClass;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringStorageClassName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringVo);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedStorageClassUsedByArchiveFiles);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedStorageClassUsedByArchiveRoutes);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedStorageClassUsedByFileRecycleLogs);
+
+class StorageClassCatalogue {
+public:
+  virtual ~StorageClassCatalogue() = default;
+
+  /**
+   * Creates the specified storage class.
+   *
+   * @param admin The administrator.
+   * @param storageClass The storage class.
+   */
+  virtual void createStorageClass(
+    const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::StorageClass &storageClass) = 0;
+
+  /**
+   * Deletes the specified storage class.
+   *
+   * @param storageClassName The name of the storage class which is only
+   * guaranteed to be unique within its disk instance.
+   */
+  virtual void deleteStorageClass(const std::string &storageClassName) = 0;
+
+  virtual std::list<common::dataStructures::StorageClass> getStorageClasses() const = 0;
+
+  virtual common::dataStructures::StorageClass getStorageClass(const std::string &name) const = 0;
+
+  virtual void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t nbCopies) = 0;
+
+  virtual void modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) = 0;
+
+  virtual void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) = 0;
+
+  /**
+   * Modifies the name of the specified storage class.
+   *
+   * @param currentName The current name of the storage class.
+   * @param newName The new name of the storage class.
+   */
+  virtual void modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/TapeCatalogue.hpp b/catalogue/interfaces/TapeCatalogue.hpp
new file mode 100644
index 0000000000..219d6ab438
--- /dev/null
+++ b/catalogue/interfaces/TapeCatalogue.hpp
@@ -0,0 +1,286 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <optional>
+#include <set>
+#include <string>
+
+#include "catalogue/TapeSearchCriteria.hpp"
+#include "common/dataStructures/VidToTapeMap.hpp"
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+struct Tape;
+}
+}
+
+namespace log {
+class LogContext;
+}
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringVendor);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringVid);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonEmptyTape);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTape);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTapeState);
+
+class CreateTapeAttributes;
+class TapeForWriting;
+
+class TapeCatalogue {
+public:
+  virtual ~TapeCatalogue() = default;
+
+  /**
+   * Creates a tape which is assumed to have isFromCastor disabled.
+   *
+   * @param admin The administrator.
+   * @param tape The attributes of the tape to be created.
+   */
+  virtual void createTape(const common::dataStructures::SecurityIdentity &admin, const CreateTapeAttributes &tape) = 0;
+
+  virtual void deleteTape(const std::string &vid) = 0;
+
+  /**
+   * Returns the list of tapes that meet the specified search criteria.
+   *
+   * @param searchCriteria The search criteria.
+   * @return The list of tapes.
+   * @throw UserSpecifiedANonExistentTapePool if the user specified a
+   * non-existent tape pool.
+   */
+  virtual std::list<common::dataStructures::Tape> getTapes(
+    const TapeSearchCriteria &searchCriteria = TapeSearchCriteria()) const = 0;
+
+  /**
+   * Returns the tape with the specified volume identifier.
+   *
+   * This method will throw an exception if it cannot find the specified tape.
+   *
+   * @param vids The tape volume identifier (VID).
+   * @return Map from tape volume identifier to tape.
+   */
+  virtual common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const = 0;
+
+  /**
+   * Returns the tapes with the specified volume identifiers.
+   *
+   * This method will throw an exception if it cannot find ALL of the specified
+   * tapes.
+   *
+   * @param vids The tape volume identifiers (VIDs).
+   * @return Map from tape volume identifier to tape.
+   */
+  virtual common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string> &vids) const = 0;
+
+  /**
+   * Returns map from VID to logical library name for specified set of VIDs.
+   *
+   * @param vids The tape volume identifiers (VIDs).
+   * @return map from VID to logical library name.
+   */
+  virtual std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const = 0;
+
+  /**
+   * Reclaims the specified tape.
+   *
+   * This method will throw an exception if the specified tape does not exist.
+   *
+   * This method will throw an exception if the specified tape is not FULL.
+   *
+   * This method will throw an exception if there is still at least one tape
+   * file recorded in the catalogue as being on the specified tape.
+   *
+   * @param admin The administrator.
+   * @param vid The volume identifier of the tape to be reclaimed.
+   * @param lc the logContext
+   */
+  virtual void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    cta::log::LogContext & lc) = 0;
+
+  /**
+   * Checks the specified tape for the tape label command.
+   *
+   * This method checks if the tape is safe to be labeled and will throw an
+   * exception if the specified tape does not ready to be labeled.
+   *
+   * @param vid The volume identifier of the tape to be checked.
+   */
+  virtual void checkTapeForLabel(const std::string &vid) = 0;
+
+  /**
+   * Notifies the catalogue that the specified tape was labelled.
+   *
+   * @param vid The volume identifier of the tape.
+   * @param drive The name of tape drive that was used to label the tape.
+   */
+  virtual void tapeLabelled(const std::string &vid, const std::string &drive) = 0;
+
+  /**
+   * Returns the number of any files contained in the tape identified by its vid
+   * @param vid the vid in which we will the number of files
+   * @return the number of files on the tape
+   */
+  virtual uint64_t getNbFilesOnTape(const std::string &vid) const = 0;
+
+  virtual void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &mediaType) = 0;
+
+  virtual void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &vendor) = 0;
+
+  virtual void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &vid, const std::string &logicalLibraryName) = 0;
+
+  virtual void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &tapePoolName) = 0;
+
+  virtual void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &vid, const std::string &encryptionKeyName) = 0;
+
+  virtual void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &vid, const std::string &verificationStatus) = 0;
+
+  /**
+   * Returns true if the specified tape exists.
+   *
+   * @param vid The volume identifier of the tape.
+   * @return True if the tape exists.
+   */
+  virtual bool tapeExists(const std::string &vid) const = 0;
+
+  /**
+   * Modify the state of the specified tape
+   * @param admin, the person or the system who modified the state of the tape
+   * @param vid the VID of the tape to change the state
+   * @param state the new state
+   * @param stateReason the reason why the state changes, if the state is ACTIVE and the stateReason is std::nullopt, the state will be reset to null
+   */
+  virtual void modifyTapeState(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const common::dataStructures::Tape::State & state,
+    const std::optional<common::dataStructures::Tape::State> & prev_state,
+    const std::optional<std::string> & stateReason) = 0;
+  /**
+   * Sets the full status of the specified tape.
+   *
+   * Please note that this method is to be called by the CTA front-end in
+   * response to a command from the CTA command-line interface (CLI).
+   *
+   * @param admin The administrator.
+   * @param vid The volume identifier of the tape to be marked as full.
+   * @param fullValue Set to true if the tape is full.
+   */
+  virtual void setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool fullValue) = 0;
+
+  /**
+   * Sets the dirty status of the specified tape.
+   *
+   * Please note that this method is to be called by the CTA front-end in
+   * response to a command from the CTA command-line interface (CLI).
+   *
+   * @param admin The administrator.
+   * @param vid The volume identifier of the tape to be marked as full.
+   * @param dirty Set to true if the tape is dirty.
+   */
+  virtual void setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool dirty) = 0;
+
+  /**
+   * This method notifies the CTA catalogue to set the specified tape is from CASTOR.
+   * This method only for unitTests and MUST never be called in CTA!!!
+   *
+   * @param vid The volume identifier of the tape.
+   */
+  virtual void setTapeIsFromCastorInUnitTests(const std::string &vid) = 0;
+
+  virtual void setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) = 0;
+
+  virtual void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) = 0;
+
+  virtual void setTapeDirty(const std::string & vid) = 0;
+
+  /**
+   * Modifies the tape comment
+   * If the comment == std::nullopt, it will delete the comment from the tape table
+   * @param admin the admin who removes the comment
+   * @param vid the vid of the tape to remove the comment
+   * @param comment the new comment. If comment == std::nullopt, the comment will be deleted.
+   */
+  virtual void modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::optional<std::string> &comment) = 0;
+
+  /**
+   * Notifies the CTA catalogue that the specified tape has been mounted in
+   * order to archive files.
+   *
+   * The purpose of this method is to keep track of which drive mounted a given
+   * tape for archiving files last.
+   *
+   * @param vid The volume identifier of the tape.
+   * @param drive The name of the drive where the tape was mounted.
+   */
+  virtual void tapeMountedForArchive(const std::string &vid, const std::string &drive) = 0;  // internal function (noCLI)
+
+  /**
+   * Notifies the CTA catalogue that the specified tape has been mounted in
+   * order to retrieve files.
+   *
+   * The purpose of this method is to keep track of which drive mounted a given
+   * tape for retrieving files last.
+   *
+   * @param vid The volume identifier of the tape.
+   * @param drive The name of the drive where the tape was mounted.
+   */
+  virtual void tapeMountedForRetrieve(const std::string &vid, const std::string &drive) = 0;
+
+  /**
+   * This method notifies the CTA catalogue that there is no more free space on
+   * the specified tape.
+   *
+   * @param vid The volume identifier of the tape.
+   */
+  virtual void noSpaceLeftOnTape(const std::string &vid) = 0;
+
+  /**
+   * Returns the list of tapes that can be written to by a tape drive in the
+   * specified logical library, in other words tapes that are labelled, not
+   * disabled, not full, not read-only and are in the specified logical library.
+   *
+   * @param logicalLibraryName The name of the logical library.
+   * @return The list of tapes for writing.
+   */
+  virtual std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const = 0;
+
+  virtual common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/TapeFileCatalogue.hpp b/catalogue/interfaces/TapeFileCatalogue.hpp
new file mode 100644
index 0000000000..fbad28d441
--- /dev/null
+++ b/catalogue/interfaces/TapeFileCatalogue.hpp
@@ -0,0 +1,96 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct ArchiveFile;
+struct RequesterIdentity;
+struct RetrieveFileQueueCriteria;
+}
+}
+
+namespace log {
+struct LogContext;
+}
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedExistingDeletedFileCopy);
+
+class TapeItemWrittenPointer;
+
+/**
+ * Specifies the interface to a factory Catalogue objects.
+ */
+class TapeFileCatalogue {
+public:
+  virtual ~TapeFileCatalogue() = default;
+
+  /**
+   * Notifies the catalogue that the specified files have been written to tape.
+   *
+   * @param events The tape file written events.
+   * @throw TapeFseqMismatch If an unexpected tape file sequence number is encountered.
+   * @throw FileSizeMismatch If an unexpected tape file size is encountered.
+   */
+  virtual void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) = 0;
+
+  /**
+  * Deletes a tape file copy
+  *
+  * @param file The tape file to delete
+  * @param reason The reason for deleting the tape file copy
+  */
+  virtual void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) = 0;
+
+  /**
+   * Prepares for a file retrieval by returning the information required to
+   * queue the associated retrieve request(s).
+   *
+   * @param diskInstanceName The name of the instance from where the retrieval
+   * request originated
+   * @param archiveFileId The unique identifier of the archived file that is
+   * to be retrieved.
+   * @param user The user for whom the file is to be retrieved.  This will be
+   * used by the Catalogue to determine the mount policy to be used when
+   * retrieving the file.
+   * @param activity The activity under which the user wants to start the retrieve
+   * The call will fail if the activity is set and unknown.
+   * @param lc The log context.
+   *
+   * @return The information required to queue the associated retrieve request(s).
+   */
+  virtual common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(
+    const std::string &diskInstanceName,
+    const uint64_t archiveFileId,
+    const common::dataStructures::RequesterIdentity &user,
+    const std::optional<std::string> & activity,
+    log::LogContext &lc,
+    const std::optional<std::string> &mountPolicyName =  std::nullopt) = 0;
+};  // class FileRecyleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/interfaces/TapePoolCatalogue.hpp b/catalogue/interfaces/TapePoolCatalogue.hpp
new file mode 100644
index 0000000000..eb2e7ab3f1
--- /dev/null
+++ b/catalogue/interfaces/TapePoolCatalogue.hpp
@@ -0,0 +1,87 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "catalogue/TapePoolSearchCriteria.hpp"
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+class TapePool;
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringTapePoolName);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyTapePool);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTapePool);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedTapePoolUsedInAnArchiveRoute);
+
+class TapePoolCatalogue {
+public:
+  virtual ~TapePoolCatalogue() = default;
+
+  virtual void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+    const std::optional<std::string> &supply, const std::string &comment) = 0;
+
+  virtual void deleteTapePool(const std::string &name) = 0;
+
+  virtual std::list<TapePool> getTapePools(
+    const TapePoolSearchCriteria &searchCriteria = TapePoolSearchCriteria()) const = 0;
+
+  virtual std::optional<TapePool> getTapePool(const std::string &tapePoolName) const = 0;
+
+  virtual void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) = 0;
+
+  virtual void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t nbPartialTapes) = 0;
+
+  virtual void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) = 0;
+
+  virtual void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool encryptionValue) = 0;
+
+  virtual void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &supply) = 0;
+
+  virtual void modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) = 0;
+
+  /**
+   * Returns true if the specified tape pool exists.
+   *
+   * @param tapePoolName The name of the tape pool.
+   * @return True if the tape pool exists.
+   */
+  virtual bool tapePoolExists(const std::string &tapePoolName) const = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/interfaces/VirtualOrganizationCatalogue.hpp b/catalogue/interfaces/VirtualOrganizationCatalogue.hpp
new file mode 100644
index 0000000000..f17cc46371
--- /dev/null
+++ b/catalogue/interfaces/VirtualOrganizationCatalogue.hpp
@@ -0,0 +1,135 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <string>
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+struct VirtualOrganization;
+} // namespace dataStructures
+} // namespace common
+
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentVirtualOrganization);
+
+class VirtualOrganizationCatalogue {
+public:
+  virtual ~VirtualOrganizationCatalogue() = default;
+
+  /**
+   * Creates the specified Virtual Organization
+   * @param admin The administrator.
+   * @param vo the Virtual Organization
+   */
+  virtual void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::VirtualOrganization &vo) = 0;
+
+  /**
+   * Deletes the specified Virtual Organization
+   * @param voName the name of the VirtualOrganization to delete
+   */
+  virtual void deleteVirtualOrganization(const std::string &voName) = 0;
+
+  /**
+   * Get all the Virtual Organizations from the Catalogue
+   * @return the list of all the Virtual Organizations
+   */
+  virtual std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const = 0;
+
+  /**
+   * Get the virtual organization corresponding to the tapepool passed in parameter
+   * @param tapepoolName the name of the tapepool which we want the virtual organization
+   * @return the VirtualOrganization associated to the tapepool passed in parameter
+   */
+  virtual common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const = 0;
+
+  /**
+   * Get, from the cache, the virtual organization corresponding to the tapepool passed in parameter
+   * @param tapepoolName the name of the tapepool which we want the virtual organization
+   * @return the VirtualOrganization associated to the tapepool passed in parameter
+   */
+  virtual common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const = 0;
+
+  /**
+   * Modifies the name of the specified Virtual Organization.
+   *
+   * @param currentVoName The current name of the Virtual Organization.
+   * @param newVoName The new name of the Virtual Organization.
+   */
+  virtual void modifyVirtualOrganizationName(
+    const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+    const std::string &newVoName) = 0;
+
+  /**
+   * Modifies the max number of allocated drives for read for the specified Virtual Organization
+   *
+   * @param voName the VO name
+   * @param readMaxDrives the new max number of allocated drives for read for the specified Virtual Organization
+   */
+  virtual void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t readMaxDrives) = 0;
+
+  /**
+   * Modifies the max number of allocated drives for write for the specified Virtual Organization
+   *
+   * @param voName the VO name
+   * @param writeMaxDrives the new max number of allocated drives for write for the specified Virtual Organization
+   */
+  virtual void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t writeMaxDrives) = 0;
+
+  /**
+   * Modifies the max size of files  for the specified Virtual Organization
+   *
+   * @param voName the VO name
+   * @param maxFileSize the new max file size for the specified Virtual Organization
+   */
+  virtual void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t maxFileSize) = 0;
+
+  /**
+   * Modifies the comment of the specified Virtual Organization
+   *
+   * @param voName The name of the Virtual Organization.
+   * @param comment The new comment of the Virtual Organization.
+   */
+  virtual void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &comment) = 0;
+
+  /**
+   * Modifies the comment of the specified Virtual Organization
+   *
+   * @param voName The name of the Virtual Organization.
+   * @param diskInstance The new disk instance of the Virtual Organization.
+   */
+  virtual void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &diskInstance) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/CommonExceptions.hpp b/catalogue/rdbms/CommonExceptions.hpp
new file mode 100644
index 0000000000..02359c9165
--- /dev/null
+++ b/catalogue/rdbms/CommonExceptions.hpp
@@ -0,0 +1,31 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <common/exception/Exception.hpp>
+#include <common/exception/UserError.hpp>
+
+namespace cta {
+namespace catalogue {
+
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringComment);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAZeroRefreshInterval);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringFreeSpaceQueryURL);
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsAdminUserCatalogue.cpp b/catalogue/rdbms/RdbmsAdminUserCatalogue.cpp
new file mode 100644
index 0000000000..ff1b31a375
--- /dev/null
+++ b/catalogue/rdbms/RdbmsAdminUserCatalogue.cpp
@@ -0,0 +1,287 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsAdminUserCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/Logger.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+
+RdbmsAdminUserCatalogue::RdbmsAdminUserCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool):
+  m_log(log), m_connPool(connPool), m_isAdminCache(10) {}
+
+void RdbmsAdminUserCatalogue::createAdminUser(
+  const common::dataStructures::SecurityIdentity &admin,
+  const std::string &username,
+  const std::string &comment) {
+  try {
+    if (username.empty()) {
+      throw UserSpecifiedAnEmptyStringUsername("Cannot create admin user because the username is an empty string");
+    }
+
+    if (comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create admin user because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    auto conn = m_connPool->getConn();
+    if (adminUserExists(conn, username)) {
+      throw exception::UserError(std::string("Cannot create admin user " + username +
+        " because an admin user with the same name already exists"));
+    }
+    const uint64_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO ADMIN_USER("
+        "ADMIN_USER_NAME,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":ADMIN_USER_NAME,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":ADMIN_USER_NAME", username);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsAdminUserCatalogue::adminUserExists(rdbms::Conn &conn, const std::string adminUsername) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ADMIN_USER_NAME AS ADMIN_USER_NAME "
+      "FROM "
+        "ADMIN_USER "
+      "WHERE "
+        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":ADMIN_USER_NAME", adminUsername);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsAdminUserCatalogue::deleteAdminUser(const std::string &username) {
+  try {
+    const char *const sql = "DELETE FROM ADMIN_USER WHERE ADMIN_USER_NAME = :ADMIN_USER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":ADMIN_USER_NAME", username);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete admin-user ") + username + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::AdminUser> RdbmsAdminUserCatalogue::getAdminUsers() const {
+  try {
+    std::list<common::dataStructures::AdminUser> admins;
+    const char *const sql =
+      "SELECT "
+        "ADMIN_USER_NAME AS ADMIN_USER_NAME,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "ADMIN_USER "
+      "ORDER BY "
+        "ADMIN_USER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::AdminUser admin;
+
+      admin.name = rset.columnString("ADMIN_USER_NAME");
+      admin.comment = rset.columnString("USER_COMMENT");
+      admin.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      admin.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      admin.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      admin.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      admin.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      admin.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      admins.push_back(admin);
+    }
+
+    return admins;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsAdminUserCatalogue::modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &username, const std::string &comment) {
+  try {
+    if (username.empty()) {
+      throw UserSpecifiedAnEmptyStringUsername("Cannot modify admin user because the username is an empty string");
+    }
+
+    if (comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot modify admin user because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE ADMIN_USER SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":ADMIN_USER_NAME", username);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify admin user ") + username + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// isNonCachedAdmin
+//------------------------------------------------------------------------------
+bool RdbmsAdminUserCatalogue::isNonCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ADMIN_USER_NAME AS ADMIN_USER_NAME "
+      "FROM "
+        "ADMIN_USER "
+      "WHERE "
+        "ADMIN_USER_NAME = :ADMIN_USER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":ADMIN_USER_NAME", admin.username);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsAdminUserCatalogue::isCachedAdmin(const common::dataStructures::SecurityIdentity &admin)
+  const {
+  try {
+    auto getNonCachedValue = [&] {
+      return isNonCachedAdmin(admin);
+    };
+    return m_isAdminCache.getCachedValue(admin, getNonCachedValue).value;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsAdminUserCatalogue::isAdmin(const common::dataStructures::SecurityIdentity &admin) const {
+  try {
+    return isCachedAdmin(admin);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
+
diff --git a/catalogue/rdbms/RdbmsAdminUserCatalogue.hpp b/catalogue/rdbms/RdbmsAdminUserCatalogue.hpp
new file mode 100644
index 0000000000..7583f0ce13
--- /dev/null
+++ b/catalogue/rdbms/RdbmsAdminUserCatalogue.hpp
@@ -0,0 +1,90 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/AdminUserCatalogue.hpp"
+#include "catalogue/TimeBasedCache.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsAdminUserCatalogue : public AdminUserCatalogue {
+public:
+  RdbmsAdminUserCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsAdminUserCatalogue() override = default;
+
+  void createAdminUser(const common::dataStructures::SecurityIdentity &admin, const std::string &username,
+    const std::string &comment) override;
+
+  void deleteAdminUser(const std::string &username) override;
+
+  std::list<common::dataStructures::AdminUser> getAdminUsers() const override;
+
+  void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &username, const std::string &comment) override;
+
+  bool isAdmin(const common::dataStructures::SecurityIdentity &admin) const override;
+
+private:
+  /**
+   * Returns true if the specified admin user exists.
+   *
+   * @param conn The database connection.
+   * @param adminUsername The name of the admin user.
+   * @return True if the admin user exists.
+   */
+  bool adminUserExists(rdbms::Conn &conn, const std::string adminUsername) const;
+
+  /**
+   * Returns a cached version of the result of calling isAdmin().
+   *
+   * @param admin The administrator.
+   * @return True if the specified user has administrator privileges.
+   */
+  bool isCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const;
+
+  /**
+   * Returns true if the specified user has administrator privileges.
+   *
+   * Please note that this method always queries the Catalogue database.
+   *
+   * @param admin The administrator.
+   * @return True if the specified user has administrator privileges.
+   */
+  bool isNonCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const;
+
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+
+  /**
+   * Cached version of isAdmin() results.
+   */
+  mutable TimeBasedCache<common::dataStructures::SecurityIdentity, bool> m_isAdminCache;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsArchiveFileCatalogue.cpp b/catalogue/rdbms/RdbmsArchiveFileCatalogue.cpp
new file mode 100644
index 0000000000..213a838ebf
--- /dev/null
+++ b/catalogue/rdbms/RdbmsArchiveFileCatalogue.cpp
@@ -0,0 +1,1209 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/ArchiveFileRow.hpp"
+#include "catalogue/ArchiveFileRowWithoutTimestamps.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/Group.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueTapeContentsItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+#include "catalogue/User.hpp"
+#include "catalogue/ValueAndTimeBasedCacheInfo.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
+#include "common/dataStructures/ArchiveFileSummary.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/RequesterIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/LostDatabaseConnection.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/exception/UserErrorWithCacheInfo.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsArchiveFileCatalogue::RdbmsArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log),
+    m_connPool(connPool),
+    m_rdbmsCatalogue(rdbmsCatalogue),
+    m_tapeCopyToPoolCache(10),
+    m_expectedNbArchiveRoutesCache(10) {}
+
+uint64_t RdbmsArchiveFileCatalogue::checkAndGetNextArchiveFileId(const std::string &diskInstanceName,
+  const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) {
+  try {
+    const auto storageClass = catalogue::StorageClass(storageClassName);
+    const auto copyToPoolMap = getCachedTapeCopyToPoolMap(storageClass);
+    const auto expectedNbRoutes = getCachedExpectedNbArchiveRoutes(storageClass);
+
+    // Check that the number of archive routes is correct
+    if(copyToPoolMap.empty()) {
+      exception::UserError ue;
+      ue.getMessage() << "Storage class " << storageClassName << " has no archive routes";
+      throw ue;
+    }
+    if(copyToPoolMap.size() != expectedNbRoutes) {
+      exception::UserError ue;
+      ue.getMessage() << "Storage class " << storageClassName << " does not have the"
+        " expected number of archive routes routes: expected=" << expectedNbRoutes << ", actual=" <<
+        copyToPoolMap.size();
+      throw ue;
+    }
+
+    const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
+    const auto userMountPolicy = userMountPolicyAndCacheInfo.value;
+    // Only consider the requester's group if there is no user mount policy
+    if(!userMountPolicy) {
+      const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
+      const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value;
+
+      if(!groupMountPolicy) {
+
+        const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
+        const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value;
+
+        if(!defaultUserMountPolicy) {
+          exception::UserErrorWithCacheInfo ue(userMountPolicyAndCacheInfo.cacheInfo);
+          ue.getMessage() << "Failed to check and get next archive file ID: No mount rules: storageClass=" <<
+            storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group;
+          throw ue;
+        }
+      }
+    }
+
+    // Now that we have found both the archive routes and the mount policy it's
+    // safe to consume an archive file identifier
+    {
+      auto conn = m_connPool->getConn();
+      return getNextArchiveFileId(conn);
+    }
+  } catch(exception::UserErrorWithCacheInfo &ue) {
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("cacheInfo", ue.cacheInfo)
+       .add("userError", ue.getMessage().str());
+    lc.log(log::INFO, "Catalogue::checkAndGetNextArchiveFileId caught a UserErrorWithCacheInfo");
+    throw;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::ArchiveFileQueueCriteria RdbmsArchiveFileCatalogue::getArchiveFileQueueCriteria(
+  const std::string &diskInstanceName, const std::string &storageClassName,
+  const common::dataStructures::RequesterIdentity &user) {
+  try {
+    const auto storageClass = catalogue::StorageClass(storageClassName);
+    const common::dataStructures::TapeCopyToPoolMap copyToPoolMap = getCachedTapeCopyToPoolMap(storageClass);
+    const uint64_t expectedNbRoutes = getCachedExpectedNbArchiveRoutes(storageClass);
+
+    // Check that the number of archive routes is correct
+    if(copyToPoolMap.empty()) {
+      exception::UserError ue;
+      ue.getMessage() << "Storage class " << diskInstanceName << ": " << storageClassName << " has no archive routes";
+      throw ue;
+    }
+    if(copyToPoolMap.size() != expectedNbRoutes) {
+      exception::UserError ue;
+      ue.getMessage() << "Storage class " << diskInstanceName << ": " << storageClassName << " does not have the"
+        " expected number of archive routes routes: expected=" << expectedNbRoutes << ", actual=" <<
+        copyToPoolMap.size();
+      throw ue;
+    }
+
+    // Get the mount policy - user mount policies overrule group ones
+    const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
+    const auto userMountPolicy = userMountPolicyAndCacheInfo.value;
+
+    if(userMountPolicy) {
+      return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *userMountPolicy);
+    } else {
+      const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
+      const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value;
+
+      if(groupMountPolicy) {
+        return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *groupMountPolicy);
+      } else {
+        const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
+        const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value;
+
+        if(defaultUserMountPolicy) {
+          return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *defaultUserMountPolicy);
+        } else {
+          exception::UserErrorWithCacheInfo ue(defaultUserMountPolicyAndCacheInfo.cacheInfo);
+          ue.getMessage() << "Failed to get archive file queue criteria: No mount rules: storageClass=" <<
+            storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group;
+          throw ue;
+        }
+      }
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+ArchiveFileItor RdbmsArchiveFileCatalogue::getArchiveFilesItor(const TapeFileSearchCriteria &searchCriteria) const {
+  checkTapeFileSearchCriteria(searchCriteria);
+
+  // If this is the listing of the contents of a tape
+  if (!searchCriteria.archiveFileId && !searchCriteria.diskInstance && !searchCriteria.diskFileIds &&
+    !searchCriteria.fSeq && searchCriteria.vid) {
+    return getTapeContentsItor(searchCriteria.vid.value());
+  }
+
+  try {
+    // Create a connection to populate the temporary table (specialised by database type)
+    auto conn = m_rdbmsCatalogue->m_archiveFileListingConnPool->getConn();
+    const auto tempDiskFxidsTableName = m_rdbmsCatalogue->createAndPopulateTempTableFxid(conn,
+      searchCriteria.diskFileIds);
+    // Pass ownership of the connection to the Iterator object
+    auto impl = new RdbmsCatalogueGetArchiveFilesItor(m_log, std::move(conn), searchCriteria, tempDiskFxidsTableName);
+    return ArchiveFileItor(impl);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::ArchiveFile RdbmsArchiveFileCatalogue::getArchiveFileForDeletion(
+  const TapeFileSearchCriteria &criteria) const {
+  if (!criteria.diskFileIds && !criteria.archiveFileId) {
+    throw exception::UserError("To delete a file copy either the diskFileId+diskInstanceName or archiveFileId must be specified");
+  }
+  if (criteria.diskFileIds && !criteria.diskInstance) {
+    throw exception::UserError("DiskFileId makes no sense without disk instance");
+  }
+  if (!criteria.vid) {
+    throw exception::UserError("Vid must be specified");
+  }
+
+  auto vid = criteria.vid.value();
+  TapeFileSearchCriteria searchCriteria = criteria;
+  searchCriteria.vid = std::nullopt; //unset vid, we want to get all copies of the archive file so we can check that it is not a one copy file
+  auto itor = getArchiveFilesItor(searchCriteria);
+
+  // itor should have at most one archive file since we always search on unique attributes
+  if (!itor.hasMore()) {
+    if (criteria.archiveFileId) {
+      throw exception::UserError(std::string("Cannot delete a copy of the file with archiveFileId ") +
+                                std::to_string(criteria.archiveFileId.value()) +
+                                 " because the file does not exist");
+    } else {
+      throw exception::UserError(std::string("Cannot delete a copy of the file with eosFxid ") +
+                                criteria.diskFileIds.value().front() + " and diskInstance " +
+                                criteria.diskInstance.value() + " because the file does not exist");
+    }
+  }
+
+  cta::common::dataStructures::ArchiveFile af = itor.next();
+
+  if (af.tapeFiles.size() == 1) {
+    if (criteria.archiveFileId) {
+      throw exception::UserError(std::string("Cannot delete a copy of the file with archiveFileId ") +
+                                std::to_string(criteria.archiveFileId.value()) +
+                                " because it is the only copy");
+    } else {
+      throw exception::UserError(std::string("Cannot delete a copy of the file with eosFxid ") +
+                                criteria.diskFileIds.value().front() + " and diskInstance " +
+                                criteria.diskInstance.value() + " because it is the only copy");
+    }
+  }
+  af.tapeFiles.removeAllVidsExcept(vid); // assume there is only one copy per vid, this should return a list with at most one item
+  if (af.tapeFiles.empty()) {
+    if (criteria.archiveFileId) {
+      throw exception::UserError(std::string("No copy of the file with archiveFileId ") +
+                                std::to_string(criteria.archiveFileId.value()) +
+                                 " on vid " + vid);
+    } else {
+      throw exception::UserError(std::string("No copy of the file with eosFxid ") +
+                                criteria.diskFileIds.value().front() + " and diskInstance " +
+                                criteria.diskInstance.value() + " on vid " + vid);
+    }
+  }
+  if (af.tapeFiles.size() > 1){
+    if (criteria.archiveFileId) {
+      throw exception::UserError(std::string("Error: More than one copy of the file with archiveFileId ") +
+                                std::to_string(criteria.archiveFileId.value()) +
+                                 " on vid " + vid);
+    } else {
+      throw exception::UserError(std::string("Error: More than one copy of the file with eosFxid ") +
+                                criteria.diskFileIds.value().front() + " and diskInstance " +
+                                criteria.diskInstance.value() + " on vid " + vid);
+    }
+  }
+  return af;
+}
+
+std::list<common::dataStructures::ArchiveFile> RdbmsArchiveFileCatalogue::getFilesForRepack(const std::string &vid,
+  const uint64_t startFSeq, const uint64_t maxNbFiles) const {
+  try {
+    std::string sql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
+        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
+        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
+        "TAPE_FILE.VID AS VID,"
+        "TAPE_FILE.FSEQ AS FSEQ,"
+        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "INNER JOIN TAPE ON "
+        "TAPE_FILE.VID = TAPE.VID "
+      "INNER JOIN TAPE_POOL ON "
+        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "WHERE "
+        "TAPE_FILE.VID = :VID AND "
+        "TAPE_FILE.FSEQ >= :START_FSEQ "
+       "ORDER BY FSEQ";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.bindUint64(":START_FSEQ", startFSeq);
+    auto rset = stmt.executeQuery();
+
+    std::list<common::dataStructures::ArchiveFile> archiveFiles;
+    while(rset.next()) {
+      common::dataStructures::ArchiveFile archiveFile;
+
+      archiveFile.archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
+      archiveFile.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      archiveFile.diskFileId = rset.columnString("DISK_FILE_ID");
+      archiveFile.diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
+      archiveFile.diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
+      archiveFile.fileSize = rset.columnUint64("SIZE_IN_BYTES");
+      archiveFile.checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
+      archiveFile.storageClass = rset.columnString("STORAGE_CLASS_NAME");
+      archiveFile.creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
+      archiveFile.reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
+
+      common::dataStructures::TapeFile tapeFile;
+      tapeFile.vid = rset.columnString("VID");
+      tapeFile.fSeq = rset.columnUint64("FSEQ");
+      tapeFile.blockId = rset.columnUint64("BLOCK_ID");
+      tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
+      tapeFile.copyNb = rset.columnUint64("COPY_NB");
+      tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
+      tapeFile.checksumBlob = archiveFile.checksumBlob; // Duplicated for convenience
+
+      archiveFile.tapeFiles.push_back(tapeFile);
+
+      archiveFiles.push_back(archiveFile);
+
+      if(maxNbFiles == archiveFiles.size()) break;
+    }
+    return archiveFiles;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+ArchiveFileItor RdbmsArchiveFileCatalogue::getArchiveFilesForRepackItor(const std::string &vid,
+  const uint64_t startFSeq) const {
+  try {
+    auto impl = new RdbmsCatalogueGetArchiveFilesForRepackItor(m_log, *(m_rdbmsCatalogue->m_archiveFileListingConnPool),
+      vid, startFSeq);
+    return ArchiveFileItor(impl);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::ArchiveFileSummary RdbmsArchiveFileCatalogue::getTapeFileSummary(
+  const TapeFileSearchCriteria &searchCriteria) const {
+  try {
+    auto conn = m_connPool->getConn();
+
+    std::string sql =
+      "SELECT "
+        "COALESCE(SUM(ARCHIVE_FILE.SIZE_IN_BYTES), 0) AS TOTAL_BYTES,"
+        "COUNT(ARCHIVE_FILE.ARCHIVE_FILE_ID) AS TOTAL_FILES "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "INNER JOIN TAPE ON "
+        "TAPE_FILE.VID = TAPE.VID "
+      "INNER JOIN TAPE_POOL ON "
+        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID";
+
+    const bool thereIsAtLeastOneSearchCriteria =
+      searchCriteria.archiveFileId  ||
+      searchCriteria.diskInstance   ||
+      searchCriteria.vid            ||
+      searchCriteria.diskFileIds;
+
+    if(thereIsAtLeastOneSearchCriteria) {
+      sql += " WHERE ";
+    }
+
+    bool addedAWhereConstraint = false;
+
+    if(searchCriteria.archiveFileId) {
+      sql += " ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.diskInstance) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += "ARCHIVE_FILE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.vid) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += "TAPE_FILE.VID = :VID";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.diskFileIds) {
+      const auto tempDiskFxidsTableName = m_rdbmsCatalogue->createAndPopulateTempTableFxid(conn,
+        searchCriteria.diskFileIds);
+
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += "ARCHIVE_FILE.DISK_FILE_ID IN (SELECT DISK_FILE_ID FROM " + tempDiskFxidsTableName + ")";
+      addedAWhereConstraint = true;
+    }
+
+    auto stmt = conn.createStmt(sql);
+    if(searchCriteria.archiveFileId) {
+      stmt.bindUint64(":ARCHIVE_FILE_ID", searchCriteria.archiveFileId.value());
+    }
+    if(searchCriteria.diskInstance) {
+      stmt.bindString(":DISK_INSTANCE_NAME", searchCriteria.diskInstance.value());
+    }
+    if(searchCriteria.vid) {
+      stmt.bindString(":VID", searchCriteria.vid.value());
+    }
+    auto rset = stmt.executeQuery();
+
+    if(!rset.next()) {
+      throw exception::Exception("SELECT COUNT statement did not return a row");
+    }
+
+    common::dataStructures::ArchiveFileSummary summary;
+    summary.totalBytes = rset.columnUint64("TOTAL_BYTES");
+    summary.totalFiles = rset.columnUint64("TOTAL_FILES");
+    return summary;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::ArchiveFile RdbmsArchiveFileCatalogue::getArchiveFileById(const uint64_t id) const {
+  try {
+    auto conn = m_connPool->getConn();
+    const auto archiveFile = getArchiveFileById(conn, id);
+
+    // Throw an exception if the archive file does not exist
+    if(nullptr == archiveFile.get()) {
+      exception::Exception ex;
+      ex.getMessage() << "No such archive file with ID " << id;
+      throw (ex);
+    }
+
+    return *archiveFile;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsArchiveFileCatalogue::getArchiveFileById(rdbms::Conn &conn,
+  const uint64_t id) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
+        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
+        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
+        "TAPE_FILE.VID AS VID,"
+        "TAPE_FILE.FSEQ AS FSEQ,"
+        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID "
+      "ORDER BY "
+        "TAPE_FILE.CREATION_TIME ASC";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", id);
+    auto rset = stmt.executeQuery();
+    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
+    while (rset.next()) {
+      if(nullptr == archiveFile.get()) {
+        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
+
+        archiveFile->archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
+        archiveFile->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+        archiveFile->diskFileId = rset.columnString("DISK_FILE_ID");
+        archiveFile->diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
+        archiveFile->diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
+        archiveFile->fileSize = rset.columnUint64("SIZE_IN_BYTES");
+        archiveFile->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
+        archiveFile->storageClass = rset.columnString("STORAGE_CLASS_NAME");
+        archiveFile->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
+        archiveFile->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
+      }
+
+      // If there is a tape file
+      if(!rset.columnIsNull("VID")) {
+        // Add the tape file to the archive file's in-memory structure
+        common::dataStructures::TapeFile tapeFile;
+        tapeFile.vid = rset.columnString("VID");
+        tapeFile.fSeq = rset.columnUint64("FSEQ");
+        tapeFile.blockId = rset.columnUint64("BLOCK_ID");
+        tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
+        tapeFile.copyNb = rset.columnUint64("COPY_NB");
+        tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
+        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
+
+        archiveFile->tapeFiles.push_back(tapeFile);
+      }
+    }
+
+    return archiveFile;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::TapeCopyToPoolMap RdbmsArchiveFileCatalogue::getCachedTapeCopyToPoolMap(
+  const catalogue::StorageClass &storageClass) const {
+  try {
+    auto getNonCachedValue = [&] {
+      auto conn = m_connPool->getConn();
+      return getTapeCopyToPoolMap(conn, storageClass);
+    };
+    return m_tapeCopyToPoolCache.getCachedValue(storageClass, getNonCachedValue).value;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t RdbmsArchiveFileCatalogue::getCachedExpectedNbArchiveRoutes(
+  const catalogue::StorageClass &storageClass) const {
+  try {
+    auto getNonCachedValue = [&] {
+      auto conn = m_connPool->getConn();
+      return getExpectedNbArchiveRoutes(conn, storageClass);
+    };
+    return m_expectedNbArchiveRoutesCache.getCachedValue(storageClass, getNonCachedValue).value;
+  } catch (exception::LostDatabaseConnection &le) {
+    throw exception::LostDatabaseConnection(std::string(__FUNCTION__) + " failed: " + le.getMessage().str());
+  } catch(exception::UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy>>
+  RdbmsArchiveFileCatalogue::getCachedRequesterMountPolicy(const User &user) const {
+  try {
+    auto getNonCachedValue = [&] {
+      auto conn = m_connPool->getConn();
+      const auto mountPolicy = static_cast<RdbmsMountPolicyCatalogue*>(m_rdbmsCatalogue->MountPolicy().get());
+      return mountPolicy->getRequesterMountPolicy(conn, user);
+    };
+    return m_rdbmsCatalogue->m_userMountPolicyCache.getCachedValue(user, getNonCachedValue);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getCachedRequesterGroupMountPolicy
+//------------------------------------------------------------------------------
+ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy>>
+  RdbmsArchiveFileCatalogue::getCachedRequesterGroupMountPolicy(const Group &group) const {
+  try {
+    auto getNonCachedValue = [&] {
+      auto conn = m_connPool->getConn();
+      const auto mountPolicyCatalogue = static_cast<RdbmsMountPolicyCatalogue*>(m_rdbmsCatalogue->MountPolicy().get());
+      return mountPolicyCatalogue->getRequesterGroupMountPolicy(conn, group);
+    };
+    return m_rdbmsCatalogue->m_groupMountPolicyCache.getCachedValue(group, getNonCachedValue);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+ArchiveFileItor RdbmsArchiveFileCatalogue::getArchiveFilesItor(rdbms::Conn &conn,
+  const TapeFileSearchCriteria &searchCriteria) const {
+
+  checkTapeFileSearchCriteria(conn, searchCriteria);
+
+  // If this is the listing of the contents of a tape
+  if (!searchCriteria.archiveFileId && !searchCriteria.diskInstance && !searchCriteria.diskFileIds &&
+    searchCriteria.vid) {
+    return getTapeContentsItor(searchCriteria.vid.value());
+  }
+
+  try {
+    auto archiveListingConn = m_rdbmsCatalogue->m_archiveFileListingConnPool->getConn();
+    const auto tempDiskFxidsTableName = m_rdbmsCatalogue->createAndPopulateTempTableFxid(archiveListingConn,
+      searchCriteria.diskFileIds);
+    // Pass ownership of the connection to the Iterator object
+    auto impl = new RdbmsCatalogueGetArchiveFilesItor(m_log, std::move(archiveListingConn), searchCriteria,
+      tempDiskFxidsTableName);
+    return ArchiveFileItor(impl);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveFileCatalogue::checkTapeFileSearchCriteria(const TapeFileSearchCriteria &searchCriteria) const {
+  auto conn = m_connPool->getConn();
+  checkTapeFileSearchCriteria(conn, searchCriteria);
+
+}
+
+void RdbmsArchiveFileCatalogue::checkTapeFileSearchCriteria(rdbms::Conn &conn,
+  const TapeFileSearchCriteria &searchCriteria) const {
+  if(searchCriteria.archiveFileId) {
+    if(!RdbmsCatalogueUtils::archiveFileIdExists(conn, searchCriteria.archiveFileId.value())) {
+      throw exception::UserError(std::string("Archive file with ID ") +
+        std::to_string(searchCriteria.archiveFileId.value()) + " does not exist");
+    }
+  }
+
+  if(searchCriteria.diskFileIds && !searchCriteria.diskInstance) {
+    throw exception::UserError(std::string("Disk file IDs are ambiguous without disk instance name"));
+  }
+
+  if (searchCriteria.fSeq && !searchCriteria.vid) {
+    throw exception::UserError(std::string("fSeq makes no sense without vid"));
+  }
+
+  if(searchCriteria.vid) {
+    if(!RdbmsCatalogueUtils::tapeExists(conn, searchCriteria.vid.value())) {
+      throw exception::UserError(std::string("Tape ") + searchCriteria.vid.value() + " does not exist");
+    }
+  }
+}
+
+ArchiveFileItor RdbmsArchiveFileCatalogue::getTapeContentsItor(const std::string &vid) const {
+  try {
+    // Create a connection to populate the temporary table (specialised by database type)
+    auto impl = new RdbmsCatalogueTapeContentsItor(m_log, *m_connPool, vid);
+    return ArchiveFileItor(impl);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::TapeCopyToPoolMap RdbmsArchiveFileCatalogue::getTapeCopyToPoolMap(rdbms::Conn &conn,
+  const catalogue::StorageClass &storageClass) const {
+  try {
+    common::dataStructures::TapeCopyToPoolMap copyToPoolMap;
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB,"
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME "
+      "FROM "
+        "ARCHIVE_ROUTE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_POOL ON "
+        "ARCHIVE_ROUTE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "WHERE "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.storageClassName);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      const uint32_t copyNb = rset.columnUint64("COPY_NB");
+      const std::string tapePoolName = rset.columnString("TAPE_POOL_NAME");
+      copyToPoolMap[copyNb] = tapePoolName;
+    }
+
+    return copyToPoolMap;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t RdbmsArchiveFileCatalogue::getExpectedNbArchiveRoutes(rdbms::Conn &conn,
+  const catalogue::StorageClass &storageClass) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "COUNT(*) AS NB_ROUTES "
+      "FROM "
+        "ARCHIVE_ROUTE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "WHERE "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.storageClassName);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set of SELECT COUNT(*) is empty");
+    }
+    return rset.columnUint64("NB_ROUTES");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveFileCatalogue::modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+  const std::string& newStorageClassName) const {
+  try {
+    auto conn = m_connPool->getConn();
+    if(!RdbmsCatalogueUtils::storageClassExists(conn, newStorageClassName)) {
+      exception::UserError ue;
+      ue.getMessage() << "Cannot modify archive file " << ": " << archiveFileId << " because storage class "
+      << ":" << newStorageClassName << " does not exist";
+      throw ue;
+    }
+
+    const char *const sql =
+    "UPDATE ARCHIVE_FILE   "
+    "SET STORAGE_CLASS_ID = ("
+      "SELECT STORAGE_CLASS_ID FROM STORAGE_CLASS WHERE STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME "
+    ") "
+    "WHERE "
+      "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", newStorageClassName);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    auto rset = stmt.executeQuery();
+
+  } catch(exception::UserError &ue) {
+      throw ue;
+  } catch(exception::Exception &ex) {
+      ex.getMessage().str(std::string(__FUNCTION__) + ": " +  ex.getMessage().str());
+  }
+}
+
+void RdbmsArchiveFileCatalogue::modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId,
+  const std::string &diskInstance) const {
+  const char *const sql =
+  "UPDATE ARCHIVE_FILE SET "
+    "DISK_FILE_ID = :FXID,"
+    "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+  "WHERE "
+    "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+
+  auto conn = m_connPool->getConn();
+  auto stmt = conn.createStmt(sql);
+  stmt.bindString(":FXID", fxId);
+  stmt.bindUint64(":ARCHIVE_FILE_ID", archiveId);
+  stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+  stmt.executeNonQuery();
+}
+
+//------------------------------------------------------------------------------
+// moveArchiveFileToRecycleBin
+//------------------------------------------------------------------------------
+void RdbmsArchiveFileCatalogue::moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
+  log::LogContext & lc) {
+  if(!request.archiveFile){
+    //The archive file does not exist in the catalogue, nothing to do with it
+    return;
+  }
+  cta::common::dataStructures::ArchiveFile archiveFile = request.archiveFile.value();
+  utils::Timer t, totalTime;
+  log::TimingList tl;
+  try {
+    checkDeleteRequestConsistency(request,archiveFile);
+    tl.insertAndReset("checkDeleteRequestConsistency",t);
+  } catch(const cta::exception::Exception & ex){
+    log::ScopedParamContainer spc(lc);
+    spc.add("fileId", std::to_string(request.archiveFileID))
+     .add("diskInstance", archiveFile.diskInstance)
+     .add("requestDiskInstance", request.diskInstance)
+     .add("diskFileId", archiveFile.diskFileId)
+     .add("diskFileInfo.owner_uid", archiveFile.diskFileInfo.owner_uid)
+     .add("diskFileInfo.gid", archiveFile.diskFileInfo.gid)
+     .add("fileSize", std::to_string(archiveFile.fileSize))
+     .add("creationTime", std::to_string(archiveFile.creationTime))
+     .add("reconciliationTime", std::to_string(archiveFile.reconciliationTime))
+     .add("diskFilePath",request.diskFilePath)
+     .add("errorMessage",ex.getMessageValue())
+     .add("storageClass", archiveFile.storageClass);
+    archiveFile.checksumBlob.addFirstChecksumToLog(spc);
+    for(auto it=archiveFile.tapeFiles.begin(); it!=archiveFile.tapeFiles.end(); it++) {
+      std::stringstream tapeCopyLogStream;
+      tapeCopyLogStream << "copy number: " << static_cast<int>(it->copyNb)
+        << " vid: " << it->vid
+        << " fSeq: " << it->fSeq
+        << " blockId: " << it->blockId
+        << " creationTime: " << it->creationTime
+        << " fileSize: " << it->fileSize;
+      spc.add("TAPE FILE", tapeCopyLogStream.str());
+    }
+    lc.log(log::WARNING, "Failed to move archive file to the file-recycle-log.");
+
+    exception::UserError ue;
+    ue.getMessage() << "Failed to move archive file with ID " << request.archiveFileID
+      << " to the file-recycle-log. errorMessage=" << ex.getMessageValue();
+    throw ue;
+  }
+
+  try {
+    //All checks are good, we can move the file to the recycle-bin
+    auto conn = m_connPool->getConn();
+    rdbms::AutoRollback autoRollback(conn);
+    copyArchiveFileToFileRecyleLogAndDelete(conn,request,lc);
+    tl.insertAndReset("copyArchiveFileToFileRecyleLogAndDeleteTime",t);
+    tl.insertAndReset("totalTime",totalTime);
+    log::ScopedParamContainer spc(lc);
+    spc.add("fileId", std::to_string(request.archiveFileID))
+     .add("diskInstance", archiveFile.diskInstance)
+     .add("requestDiskInstance", request.diskInstance)
+     .add("diskFileId", archiveFile.diskFileId)
+     .add("diskFileInfo.owner_uid", archiveFile.diskFileInfo.owner_uid)
+     .add("diskFileInfo.gid", archiveFile.diskFileInfo.gid)
+     .add("fileSize", std::to_string(archiveFile.fileSize))
+     .add("creationTime", std::to_string(archiveFile.creationTime))
+     .add("reconciliationTime", std::to_string(archiveFile.reconciliationTime))
+     .add("storageClass", archiveFile.storageClass);
+    archiveFile.checksumBlob.addFirstChecksumToLog(spc);
+    for(auto it=archiveFile.tapeFiles.begin(); it!=archiveFile.tapeFiles.end(); it++) {
+      std::stringstream tapeCopyLogStream;
+      tapeCopyLogStream << "copy number: " << static_cast<int>(it->copyNb)
+        << " vid: " << it->vid
+        << " fSeq: " << it->fSeq
+        << " blockId: " << it->blockId
+        << " creationTime: " << it->creationTime
+        << " fileSize: " << it->fileSize;
+      spc.add("TAPE FILE", tapeCopyLogStream.str());
+    }
+    tl.addToLog(spc);
+    lc.log(log::INFO, "In RdbmsCatalogue::moveArchiveFileToRecycleLog(): ArchiveFile moved to the file-recycle-log.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveFileCatalogue::checkDeleteRequestConsistency(
+  const cta::common::dataStructures::DeleteArchiveRequest deleteRequest,
+  const cta::common::dataStructures::ArchiveFile& archiveFile) const {
+  if(deleteRequest.diskInstance != archiveFile.diskInstance){
+    std::ostringstream msg;
+    msg << "Failed to move archive file with ID " << deleteRequest.archiveFileID
+        << " to the recycle-bin because the disk instance of "
+        "the request does not match that of the archived file: archiveFileId=" << archiveFile.archiveFileID
+        << " requestDiskInstance=" << deleteRequest.diskInstance << " archiveFileDiskInstance=" <<
+        archiveFile.diskInstance;
+    throw cta::exception::Exception(msg.str());
+  }
+  if(deleteRequest.diskFilePath.empty()){
+    std::ostringstream msg;
+    msg << "Failed to move archive file with ID " << deleteRequest.archiveFileID
+        << " to the recycle-bin because the disk file path has not been provided.";
+    throw cta::exception::Exception(msg.str());
+  }
+}
+
+void RdbmsArchiveFileCatalogue::deleteArchiveFile(rdbms::Conn& conn,
+  const common::dataStructures::DeleteArchiveRequest& request) {
+  try{
+    const char *const deleteArchiveFileSql =
+    "DELETE FROM "
+      "ARCHIVE_FILE "
+    "WHERE ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+
+    auto deleteArchiveFileStmt = conn.createStmt(deleteArchiveFileSql);
+    deleteArchiveFileStmt.bindUint64(":ARCHIVE_FILE_ID",request.archiveFileID);
+    deleteArchiveFileStmt.executeNonQuery();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveFileCatalogue::insertArchiveFile(rdbms::Conn &conn, const ArchiveFileRowWithoutTimestamps &row) const {
+  try {
+    if(!RdbmsCatalogueUtils::storageClassExists(conn, row.storageClassName)) {
+      throw exception::UserError(std::string("Storage class ") + row.diskInstance + ":" + row.storageClassName +
+        " does not exist");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO ARCHIVE_FILE("
+        "ARCHIVE_FILE_ID,"
+        "DISK_INSTANCE_NAME,"
+        "DISK_FILE_ID,"
+        "DISK_FILE_UID,"
+        "DISK_FILE_GID,"
+        "SIZE_IN_BYTES,"
+        "CHECKSUM_BLOB,"
+        "CHECKSUM_ADLER32,"
+        "STORAGE_CLASS_ID,"
+        "CREATION_TIME,"
+        "RECONCILIATION_TIME)"
+      "SELECT "
+        ":ARCHIVE_FILE_ID,"
+        ":DISK_INSTANCE_NAME,"
+        ":DISK_FILE_ID,"
+        ":DISK_FILE_UID,"
+        ":DISK_FILE_GID,"
+        ":SIZE_IN_BYTES,"
+        ":CHECKSUM_BLOB,"
+        ":CHECKSUM_ADLER32,"
+        "STORAGE_CLASS_ID,"
+        ":CREATION_TIME,"
+        ":RECONCILIATION_TIME "
+      "FROM "
+        "STORAGE_CLASS "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindUint64(":ARCHIVE_FILE_ID", row.archiveFileId);
+    stmt.bindString(":DISK_INSTANCE_NAME", row.diskInstance);
+    stmt.bindString(":DISK_FILE_ID", row.diskFileId);
+    stmt.bindUint64(":DISK_FILE_UID", row.diskFileOwnerUid);
+    stmt.bindUint64(":DISK_FILE_GID", row.diskFileGid);
+    stmt.bindUint64(":SIZE_IN_BYTES", row.size);
+    stmt.bindBlob  (":CHECKSUM_BLOB", row.checksumBlob.serialize());
+    // Keep transition ADLER32 checksum up-to-date if it exists
+    uint32_t adler32;
+    try {
+      std::string adler32hex = checksum::ChecksumBlob::ByteArrayToHex(row.checksumBlob.at(checksum::ADLER32));
+      adler32 = strtoul(adler32hex.c_str(), 0, 16);
+    } catch(exception::ChecksumTypeMismatch &ex) {
+      adler32 = 0;
+    }
+    stmt.bindUint64(":CHECKSUM_ADLER32", adler32);
+    stmt.bindString(":STORAGE_CLASS_NAME", row.storageClassName);
+    stmt.bindUint64(":CREATION_TIME", now);
+    stmt.bindUint64(":RECONCILIATION_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch (exception::UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: archiveFileId=" + std::to_string(row.archiveFileId) +
+       ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getArchiveFileRowByArchiveId
+//------------------------------------------------------------------------------
+std::unique_ptr<ArchiveFileRow> RdbmsArchiveFileCatalogue::getArchiveFileRowById(rdbms::Conn &conn,
+  const uint64_t id) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID, "
+        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
+        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID, "
+        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID, "
+        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID, "
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES, "
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB, "
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32, "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME, "
+        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME, "
+        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", id);
+    auto rset = stmt.executeQuery();
+
+    std::unique_ptr<ArchiveFileRow> row;
+    if (rset.next()) {
+      row = std::make_unique<ArchiveFileRow>();
+
+      row->archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
+      row->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      row->diskFileId = rset.columnString("DISK_FILE_ID");
+      row->diskFileOwnerUid = rset.columnUint64("DISK_FILE_UID");
+      row->diskFileGid = rset.columnUint64("DISK_FILE_GID");
+      row->size = rset.columnUint64("SIZE_IN_BYTES");
+      row->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
+      row->storageClassName = rset.columnString("STORAGE_CLASS_NAME");
+      row->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
+      row->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
+    }
+
+    return row;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+//------------------------------------------------------------------------------
+// updateDiskFileId
+//------------------------------------------------------------------------------
+void RdbmsArchiveFileCatalogue::updateDiskFileId(const uint64_t archiveFileId, const std::string &diskInstance,
+  const std::string &diskFileId) {
+  try {
+    const char *const sql =
+      "UPDATE ARCHIVE_FILE SET "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME, "
+        "DISK_FILE_ID = :DISK_FILE_ID "
+      "WHERE "
+        "ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_FILE_ID", diskFileId);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      std::ostringstream msg;
+      msg << "Cannot update the disk file ID of the archive file with archive file ID " << archiveFileId <<
+        " because the archive file does not exist";
+      throw exception::UserError(msg.str());
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getArchiveFileToRetrieveByArchiveFileId
+//------------------------------------------------------------------------------
+std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsArchiveFileCatalogue::getArchiveFileToRetrieveByArchiveFileId(
+  rdbms::Conn &conn, const uint64_t archiveFileId) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
+        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
+        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
+        "TAPE_FILE.VID AS VID,"
+        "TAPE_FILE.FSEQ AS FSEQ,"
+        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "INNER JOIN TAPE ON "
+        "TAPE_FILE.VID = TAPE.VID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID AND "
+        "TAPE.TAPE_STATE IN ('ACTIVE', 'DISABLED') "
+      "ORDER BY "
+        "TAPE_FILE.CREATION_TIME ASC";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    auto rset = stmt.executeQuery();
+    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
+    while (rset.next()) {
+      if(nullptr == archiveFile.get()) {
+        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
+
+        archiveFile->archiveFileID = rset.columnUint64("ARCHIVE_FILE_ID");
+        archiveFile->diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+        archiveFile->diskFileId = rset.columnString("DISK_FILE_ID");
+        archiveFile->diskFileInfo.owner_uid = rset.columnUint64("DISK_FILE_UID");
+        archiveFile->diskFileInfo.gid = rset.columnUint64("DISK_FILE_GID");
+        archiveFile->fileSize = rset.columnUint64("SIZE_IN_BYTES");
+        archiveFile->checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
+        archiveFile->storageClass = rset.columnString("STORAGE_CLASS_NAME");
+        archiveFile->creationTime = rset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
+        archiveFile->reconciliationTime = rset.columnUint64("RECONCILIATION_TIME");
+      }
+
+      // If there is a tape file we add it to the archiveFile's list of tape files
+      if(!rset.columnIsNull("VID")) {
+        // Add the tape file to the archive file's in-memory structure
+        common::dataStructures::TapeFile tapeFile;
+        tapeFile.vid = rset.columnString("VID");
+        tapeFile.fSeq = rset.columnUint64("FSEQ");
+        tapeFile.blockId = rset.columnUint64("BLOCK_ID");
+        tapeFile.fileSize = rset.columnUint64("LOGICAL_SIZE_IN_BYTES");
+        tapeFile.copyNb = rset.columnUint64("COPY_NB");
+        tapeFile.creationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
+        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
+
+        archiveFile->tapeFiles.push_back(tapeFile);
+      }
+    }
+
+    //If there are no tape files that belong to the archive file, then return a nullptr.
+    if(archiveFile.get() != nullptr && archiveFile->tapeFiles.empty()){
+      archiveFile.reset(nullptr);
+    }
+
+    return archiveFile;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getArchiveFileToRetrieveByArchiveFileId
+//------------------------------------------------------------------------------
+const std::list<std::pair<std::string, std::string>> RdbmsArchiveFileCatalogue::getTapeFileStateListForArchiveFileId(
+  rdbms::Conn &conn, const uint64_t archiveFileId) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE_FILE.VID AS VID,"
+        "TAPE.TAPE_STATE AS STATE "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "INNER JOIN TAPE ON "
+        "TAPE_FILE.VID = TAPE.VID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID ";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    auto rset = stmt.executeQuery();
+    std::list<std::pair<std::string, std::string>> ret;
+    while (rset.next()) {
+      const auto &vid = rset.columnString("VID");
+      const auto &state = rset.columnString("STATE");
+      ret.push_back(std::pair<std::string, std::string>(vid, state));
+    }
+    return ret;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp b/catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp
new file mode 100644
index 0000000000..35bf9470f3
--- /dev/null
+++ b/catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp
@@ -0,0 +1,334 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/ArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+#include "catalogue/TimeBasedCache.hpp"
+#include "common/dataStructures/TapeCopyToPoolMap.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct DeleteArchiveRequest;
+struct MountPolicy;
+struct RequesterIdentity;
+}
+}
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+struct ArchiveFileRowWithoutTimestamps;
+struct ArchiveFileRow;
+
+class RdbmsCatalogue;
+
+template <typename Value>
+class ValueAndTimeBasedCacheInfo;
+
+struct User;
+struct Group;
+
+class RdbmsArchiveFileCatalogue : public ArchiveFileCatalogue {
+public:
+  ~RdbmsArchiveFileCatalogue() override = default;
+
+  uint64_t checkAndGetNextArchiveFileId(const std::string &diskInstanceName, const std::string &storageClassName,
+    const common::dataStructures::RequesterIdentity &user) override;
+
+  common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(const std::string &diskInstanceName,
+    const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) override;
+
+  ArchiveFileItor getArchiveFilesItor(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  common::dataStructures::ArchiveFile getArchiveFileForDeletion(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  std::list<common::dataStructures::ArchiveFile> getFilesForRepack(const std::string &vid, const uint64_t startFSeq,
+    const uint64_t maxNbFiles) const override;
+
+  ArchiveFileItor getArchiveFilesForRepackItor(const std::string &vid, const uint64_t startFSeq) const override;
+
+  common::dataStructures::ArchiveFileSummary getTapeFileSummary(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const override;
+
+  void modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+    const std::string& newStorageClassName) const override;
+
+  void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId,
+    const std::string &diskInstance) const override;
+
+  void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
+    log::LogContext & lc) override;
+
+  void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
+    const std::string &diskFileId) override;
+
+protected:
+  RdbmsArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  /**
+   * Returns a unique archive ID that can be used by a new archive file within
+   * the catalogue.
+   *
+   * This method must be implemented by the sub-classes of RdbmsCatalogue
+   * because different database technologies propose different solution to the
+   * problem of generating ever increasing numeric identifiers.
+   *
+   * @param conn The database connection.
+   * @return A unique archive ID that can be used by a new archive file within
+   * the catalogue.
+   */
+  virtual uint64_t getNextArchiveFileId(rdbms::Conn &conn) = 0;
+
+  friend class OracleFileRecycleLogCatalogue;
+  friend class PostgresFileRecycleLogCatalogue;
+  friend class SqliteFileRecycleLogCatalogue;
+  /**
+   * Returns the archive file with the specified unique identifier or nullptr if
+   * it does not exist.
+   *
+   * Please note that an archive file with no associated tape files is
+   * considered not to exist by this method.
+   *
+   * @param conn The database connection.
+   * @param id The unique identifier of the archive file.
+   * @return The archive file.
+   */
+  std::unique_ptr<common::dataStructures::ArchiveFile> getArchiveFileById(rdbms::Conn &conn,
+    const uint64_t archiveFileId) const;
+
+  /**
+   * Copy the archiveFile and the associated tape files from the ARCHIVE_FILE and TAPE_FILE tables to the FILE_RECYCLE_LOG table
+   * and deletes the ARCHIVE_FILE and TAPE_FILE entries.
+   * @param conn the database connection
+   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the FILE_RECYCLE_LOG table
+   * @param lc the log context
+   */
+  virtual void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+    const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) = 0;
+
+  void deleteArchiveFile(rdbms::Conn& conn, const common::dataStructures::DeleteArchiveRequest& request);
+
+protected:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+private:
+
+  /**
+   * Cached versions of tape copy to tape tape pool mappings for specific
+   * storage classes.
+   */
+  mutable TimeBasedCache<catalogue::StorageClass, common::dataStructures::TapeCopyToPoolMap> m_tapeCopyToPoolCache;
+
+  /**
+   * Cached versions of the expected number of archive routes for specific
+   * storage classes as specified by the call to the createStorageClass()
+   * method as opposed to the actual number entered so far using the
+   * createArchiveRoute() method.
+   */
+  mutable TimeBasedCache<catalogue::StorageClass, uint64_t> m_expectedNbArchiveRoutesCache;
+
+  /**
+   * Returns a cached version of the mapping from tape copy to tape pool for the
+   * specified storage class.
+   *
+   * This method updates the cache when necessary.
+   *
+   * @param storageClass The fully qualified storage class, in other words the
+   * name of the disk instance and the name of the storage class.
+   * @return The mapping from tape copy to tape pool for the specified storage
+   * class.
+   */
+  common::dataStructures::TapeCopyToPoolMap getCachedTapeCopyToPoolMap(
+    const catalogue::StorageClass &storageClass) const;
+
+  /**
+   * Returns a cached version of the expected number of archive routes for the
+   * specified storage class as specified by the call to the
+   * createStorageClass() method as opposed to the actual number entered so far
+   * using the createArchiveRoute() method.
+   *
+   * This method updates the cache when necessary.
+   *
+   * @param storageClass The fully qualified storage class, in other words the
+   * name of the disk instance and the name of the storage class.
+   * @return The expected number of archive routes.
+   */
+  uint64_t getCachedExpectedNbArchiveRoutes(const catalogue::StorageClass &storageClass) const;
+
+  /**
+   * Returns a cached version of the specified requester mount-policy or std::nullopt
+   * if one does not exist.
+   *
+   * @param user The fully qualified user, in other words the name of the disk
+   * instance and the name of the group.
+   * @return The mount policy or std::nullopt if one does not exists.
+   * @throw UserErrorWithTimeBasedCacheInfo if there was a user error.
+   */
+  ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy> > getCachedRequesterMountPolicy(
+    const User &user) const;
+
+  /**
+   * Returns a cached version of the specified requester-group mount-policy or
+   * nullptr if one does not exist.
+   *
+   * This method updates the cache when necessary.
+   *
+   * @param group The fully qualified group, in other words the name of the disk
+   * instance and the name of the group.
+   * @return The cached mount policy or std::nullopt if one does not exists.
+   */
+  ValueAndTimeBasedCacheInfo<std::optional<common::dataStructures::MountPolicy> > getCachedRequesterGroupMountPolicy(
+    const Group &group) const;
+
+  /**
+   * Throws a UserError exception if the specified searchCriteria is not valid
+   * due to a user error.
+   *
+   * @param searchCriteria The search criteria.
+   */
+  void checkTapeFileSearchCriteria(const TapeFileSearchCriteria &searchCriteria) const;
+
+    /**
+   * Throws a UserError exception if the specified searchCriteria is not valid
+   * due to a user error.
+   *
+   * @param conn The database connection.
+   * @param searchCriteria The search criteria.
+   */
+  void checkTapeFileSearchCriteria(rdbms::Conn &conn, const TapeFileSearchCriteria &searchCriteria) const;
+
+  /**
+   * Returns an iterator across the files on the specified tape ordered by
+   * FSEQ.
+   *
+   * @param vid The volume identifier of the tape.
+   * @return The iterator.
+   */
+  ArchiveFileItor getTapeContentsItor(const std::string &vid) const;
+
+  /**
+   * Returns the specified archive files.  Please note that the list of files
+   * is ordered by archive file ID.
+   *
+   * @param conn The database connection.
+   * @param searchCriteria The search criteria.
+   * @return The archive files.
+   */
+  ArchiveFileItor getArchiveFilesItor(rdbms::Conn &conn, const TapeFileSearchCriteria &searchCriteria) const;
+
+  /**
+   * Returns the mapping from tape copy to tape pool for the specified storage
+   * class.
+   *
+   * @param conn The database connection.
+   * @param storageClass The fully qualified storage class, in other words the
+   * name of the disk instance and the name of the storage class.
+   * @return The mapping from tape copy to tape pool for the specified storage
+   * class.
+   */
+  common::dataStructures::TapeCopyToPoolMap getTapeCopyToPoolMap(rdbms::Conn &conn,
+    const catalogue::StorageClass &storageClass) const;
+
+  /**
+   * Returns the expected number of archive routes for the specified storage
+   * class as specified by the call to the createStorageClass() method as
+   * opposed to the actual number entered so far using the createArchiveRoute()
+   * method.
+   *
+   * @param conn The database connection.
+   * @param storageClass The fully qualified storage class, in other words the
+   * name of the disk instance and the name of the storage class.
+   * @return The expected number of archive routes.
+   */
+  uint64_t getExpectedNbArchiveRoutes(rdbms::Conn &conn, const catalogue::StorageClass &storageClass) const;
+
+  /**
+   * Throws an exception if the delete request passed in parameter is not consistent
+   * to allow a deletion of the ArchiveFile from the Catalogue.
+   * @param deleteRequest, the deleteRequest to check the consistency.
+   * @param archiveFile the ArchiveFile to delete to check the deleteRequest consistency against.
+   */
+  void checkDeleteRequestConsistency(const cta::common::dataStructures::DeleteArchiveRequest deleteRequest,
+    const cta::common::dataStructures::ArchiveFile & archiveFile) const;
+
+  friend class RdbmsFileRecycleLogCatalogue;
+  friend class SqliteTapeFileCatalogue;
+
+  /**
+   * An RdbmsCatalogue specific method that inserts the specified row into the
+   * ArchiveFile table.
+   *
+   * @param conn The database connection.
+   * @param row The row to be inserted.
+   */
+  void insertArchiveFile(rdbms::Conn &conn, const ArchiveFileRowWithoutTimestamps &row) const;
+
+  /**
+   * Returns the specified archive file row.   A nullptr pointer is returned if
+   * there is no corresponding row in the ARCHIVE_FILE table.
+   *
+   * @param conn The database connection.
+   * @param id The identifier of the archive file.
+   * @return The archive file row or nullptr.
+   */
+  std::unique_ptr<ArchiveFileRow> getArchiveFileRowById(rdbms::Conn &conn, const uint64_t id) const;
+
+  friend class RdbmsTapeFileCatalogue;
+  /**
+   * Returns the specified archive file.   A nullptr pointer is returned if
+   * there are no corresponding rows in the TAPE_FILE table. Only looks at TAPE_FILE entries
+   * on Tapes with state 'ACTIVE'
+   *
+   * @param conn The database connection.
+   * @param archiveFileId The identifier of the archive file.
+   * @return The archive file or nullptr.
+   */
+  std::unique_ptr<common::dataStructures::ArchiveFile> getArchiveFileToRetrieveByArchiveFileId(rdbms::Conn &conn,
+    const uint64_t archiveFileId) const;
+
+  /**
+   * Returns the specified archive file.   A nullptr pointer is returned if
+   * there are no corresponding rows in the TAPE_FILE table.
+   *
+   * @param conn The database connection.
+   * @param archiveFileId The identifier of the archive file.
+   * @return A list of tape file vid and corresponding tape state pairs
+   */
+  const std::list<std::pair<std::string, std::string>> getTapeFileStateListForArchiveFileId(rdbms::Conn &conn,
+    const uint64_t archiveFileId) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsArchiveRouteCatalogue.cpp b/catalogue/rdbms/RdbmsArchiveRouteCatalogue.cpp
new file mode 100644
index 0000000000..46cc5fae2f
--- /dev/null
+++ b/catalogue/rdbms/RdbmsArchiveRouteCatalogue.cpp
@@ -0,0 +1,411 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsArchiveRouteCatalogue.hpp"
+#include "catalogue/interfaces/StorageClassCatalogue.hpp"
+#include "catalogue/interfaces/TapePoolCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "common/dataStructures/ArchiveRoute.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/Logger.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+
+RdbmsArchiveRouteCatalogue::RdbmsArchiveRouteCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool):
+  m_log(log), m_connPool(connPool) {}
+
+void RdbmsArchiveRouteCatalogue::createArchiveRoute(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName,
+  const std::string &comment) {
+  try {
+    if(storageClassName.empty()) {
+      throw UserSpecifiedAnEmptyStringStorageClassName("Cannot create archive route because storage class name is an"
+        " empty string");
+    }
+    if(0 == copyNb) {
+      throw UserSpecifiedAZeroCopyNb("Cannot create archive route because copy number is zero");
+    }
+    if(tapePoolName.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create archive route because tape pool name is an empty"
+        " string");
+    }
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create archive route because comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    const time_t now = time(nullptr);
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::archiveRouteExists(conn, storageClassName, copyNb)) {
+      exception::UserError ue;
+      ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
+        << "->" << tapePoolName << " because it already exists";
+      throw ue;
+    }
+    {
+      const auto routes = getArchiveRoutes(conn, storageClassName, tapePoolName);
+      if(!routes.empty()) {
+        exception::UserError ue;
+        ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
+          << "->" << tapePoolName << " because a route already exists for this storage class and tape pool";
+        throw ue;
+      }
+    }
+    if(!RdbmsCatalogueUtils::storageClassExists(conn, storageClassName)) {
+      exception::UserError ue;
+      ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
+        << "->" << tapePoolName << " because storage class " << ":" << storageClassName <<
+        " does not exist";
+      throw ue;
+    }
+    if(!RdbmsCatalogueUtils::tapePoolExists(conn, tapePoolName)) {
+      exception::UserError ue;
+      ue.getMessage() << "Cannot create archive route " << ": " << storageClassName << "," << copyNb
+        << "->" << tapePoolName << " because tape pool " << tapePoolName + " does not exist";
+      throw ue;
+    }
+
+    const char *const sql =
+      "INSERT INTO ARCHIVE_ROUTE("
+        "STORAGE_CLASS_ID,"
+        "COPY_NB,"
+        "TAPE_POOL_ID,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "SELECT "
+        "STORAGE_CLASS_ID,"
+        ":COPY_NB,"
+        "(SELECT TAPE_POOL_ID FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME) AS TAPE_POOL_ID,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME "
+      "FROM "
+        "STORAGE_CLASS "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt.bindUint64(":COPY_NB", copyNb);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveRouteCatalogue::deleteArchiveRoute(const std::string &storageClassName, const uint32_t copyNb) {
+  try {
+    const char *const sql =
+      "DELETE FROM "
+        "ARCHIVE_ROUTE "
+      "WHERE "
+        "STORAGE_CLASS_ID = ("
+          "SELECT "
+            "STORAGE_CLASS_ID "
+          "FROM "
+            "STORAGE_CLASS "
+          "WHERE "
+            "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME) AND "
+        "COPY_NB = :COPY_NB";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt.bindUint64(":COPY_NB", copyNb);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      exception::UserError ue;
+      ue.getMessage() << "Cannot delete archive route for storage-class " << ":" + storageClassName +
+        " and copy number " << copyNb << " because it does not exist";
+      throw ue;
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::ArchiveRoute> RdbmsArchiveRouteCatalogue::getArchiveRoutes() const {
+  try {
+    std::list<common::dataStructures::ArchiveRoute> routes;
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB,"
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+
+        "ARCHIVE_ROUTE.USER_COMMENT AS USER_COMMENT,"
+
+        "ARCHIVE_ROUTE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "ARCHIVE_ROUTE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "ARCHIVE_ROUTE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "ARCHIVE_ROUTE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "ARCHIVE_ROUTE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "ARCHIVE_ROUTE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "ARCHIVE_ROUTE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_POOL ON "
+        "ARCHIVE_ROUTE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "ORDER BY "
+        "STORAGE_CLASS_NAME, COPY_NB";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::ArchiveRoute route;
+
+      route.storageClassName = rset.columnString("STORAGE_CLASS_NAME");
+      route.copyNb = rset.columnUint64("COPY_NB");
+      route.tapePoolName = rset.columnString("TAPE_POOL_NAME");
+      route.comment = rset.columnString("USER_COMMENT");
+      route.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      route.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      route.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      route.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      route.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      route.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      routes.push_back(route);
+    }
+
+    return routes;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::ArchiveRoute> RdbmsArchiveRouteCatalogue::getArchiveRoutes(
+  const std::string &storageClassName, const std::string &tapePoolName) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return getArchiveRoutes(conn, storageClassName, tapePoolName);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::ArchiveRoute> RdbmsArchiveRouteCatalogue::getArchiveRoutes(rdbms::Conn &conn,
+  const std::string &storageClassName, const std::string &tapePoolName) const {
+  try {
+    std::list<common::dataStructures::ArchiveRoute> routes;
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME, "
+        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB, "
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME, "
+
+        "ARCHIVE_ROUTE.USER_COMMENT AS USER_COMMENT, "
+
+        "ARCHIVE_ROUTE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME, "
+        "ARCHIVE_ROUTE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME, "
+        "ARCHIVE_ROUTE.CREATION_LOG_TIME AS CREATION_LOG_TIME, "
+
+        "ARCHIVE_ROUTE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME, "
+        "ARCHIVE_ROUTE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME, "
+        "ARCHIVE_ROUTE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "ARCHIVE_ROUTE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_POOL ON "
+        "ARCHIVE_ROUTE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "WHERE "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND "
+        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME "
+      "ORDER BY "
+        "STORAGE_CLASS_NAME, COPY_NB";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::ArchiveRoute route;
+
+      route.storageClassName = rset.columnString("STORAGE_CLASS_NAME");
+      route.copyNb = rset.columnUint64("COPY_NB");
+      route.tapePoolName = rset.columnString("TAPE_POOL_NAME");
+      route.comment = rset.columnString("USER_COMMENT");
+      route.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      route.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      route.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      route.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      route.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      route.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      routes.push_back(route);
+    }
+
+    return routes;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveRouteCatalogue::modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE ARCHIVE_ROUTE SET "
+        "TAPE_POOL_ID = (SELECT TAPE_POOL_ID FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME),"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "STORAGE_CLASS_ID = ("
+          "SELECT "
+            "STORAGE_CLASS_ID "
+          "FROM "
+            "STORAGE_CLASS "
+          "WHERE "
+            "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME) AND "
+        "COPY_NB = :COPY_NB";
+    auto conn = m_connPool->getConn();
+
+    if(!RdbmsCatalogueUtils::archiveRouteExists(conn, storageClassName, copyNb)) {
+      throw UserSpecifiedANonExistentArchiveRoute("Archive route does not exist");
+    }
+
+    if(!RdbmsCatalogueUtils::tapePoolExists(conn, tapePoolName)) {
+      throw UserSpecifiedANonExistentTapePool("Tape pool does not exist");
+    }
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt.bindUint64(":COPY_NB", copyNb);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentArchiveRoute("Archive route does not exist");
+    }
+  } catch(exception::UserError &ue) {
+    std::ostringstream msg;
+    msg << "Cannot modify tape pool of archive route: storageClassName=" << storageClassName << " copyNb=" << copyNb <<
+      " tapePoolName=" << tapePoolName << ": " << ue.getMessage().str();
+    ue.getMessage().str(msg.str());
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsArchiveRouteCatalogue::modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE ARCHIVE_ROUTE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "STORAGE_CLASS_ID = ("
+          "SELECT "
+            "STORAGE_CLASS_ID "
+          "FROM "
+            "STORAGE_CLASS "
+          "WHERE "
+            "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME) AND "
+        "COPY_NB = :COPY_NB";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt.bindUint64(":COPY_NB", copyNb);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      exception::UserError ue;
+      ue.getMessage() << "Cannot modify archive route for storage-class " << ":" + storageClassName +
+        " and copy number " << copyNb << " because it does not exist";
+      throw ue;
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+} // namespace catalogue
+} // namespace cta
+
diff --git a/catalogue/rdbms/RdbmsArchiveRouteCatalogue.hpp b/catalogue/rdbms/RdbmsArchiveRouteCatalogue.hpp
new file mode 100644
index 0000000000..5fd1582253
--- /dev/null
+++ b/catalogue/rdbms/RdbmsArchiveRouteCatalogue.hpp
@@ -0,0 +1,77 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/ArchiveRouteCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsArchiveRouteCatalogue : public ArchiveRouteCatalogue {
+public:
+  RdbmsArchiveRouteCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsArchiveRouteCatalogue() override = default;
+
+  void createArchiveRoute(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName,
+    const uint32_t copyNb, const std::string &tapePoolName, const std::string &comment) override;
+
+  void deleteArchiveRoute(const std::string &storageClassName, const uint32_t copyNb) override;
+
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const override;
+
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(const std::string &storageClassName,
+    const std::string &tapePoolName) const override;
+
+  void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) override;
+
+  void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+
+  /**
+   * @return the archive routes of the given storage class and destination tape
+   * pool.
+   *
+   * Under normal circumstances this method should return either 0 or 1 route.
+   * For a given storage class there should be no more than one route to any
+   * given tape pool.
+   *
+   * @param conn The database connection.
+   * @param storageClassName The name of the storage class which is only
+   * guaranteed to be unique within its disk instance.
+   * @param tapePoolName The name of the tape pool.
+   */
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(rdbms::Conn &conn,
+    const std::string &storageClassName, const std::string &tapePoolName) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsCatalogue.cpp b/catalogue/rdbms/RdbmsCatalogue.cpp
new file mode 100644
index 0000000000..1e2e53c0e3
--- /dev/null
+++ b/catalogue/rdbms/RdbmsCatalogue.cpp
@@ -0,0 +1,164 @@
+ /*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/rdbms/RdbmsAdminUserCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveRouteCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueTapeContentsItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsDriveStateCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsSchemaCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+
+namespace cta {
+namespace catalogue {
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+RdbmsCatalogue::RdbmsCatalogue(
+  log::Logger &log,
+  const rdbms::Login &login,
+  const uint64_t nbConns,
+  const uint64_t nbArchiveFileListingConns):
+  m_log(log),
+  m_connPool(std::make_shared<rdbms::ConnPool>(login, nbConns)),
+  m_archiveFileListingConnPool(std::make_shared<rdbms::ConnPool>(login, nbArchiveFileListingConns)),
+  m_groupMountPolicyCache(10),
+  m_userMountPolicyCache(10),
+  m_allMountPoliciesCache(60),
+  m_tapepoolVirtualOrganizationCache(60),
+  m_schema(std::make_unique<RdbmsSchemaCatalogue>(m_log, m_connPool)),
+  m_adminUser(std::make_unique<RdbmsAdminUserCatalogue>(m_log, m_connPool)),
+  m_diskSystem(std::make_unique<RdbmsDiskSystemCatalogue>(m_log, m_connPool)),
+  m_diskInstance(std::make_unique<RdbmsDiskInstanceCatalogue>(m_log, m_connPool)),
+  m_diskInstanceSpace(std::make_unique<RdbmsDiskInstanceSpaceCatalogue>(m_log, m_connPool)),
+  m_archiveRoute(std::make_unique<RdbmsArchiveRouteCatalogue>(m_log, m_connPool)),
+  m_mountPolicy(std::make_unique<RdbmsMountPolicyCatalogue>(m_log, m_connPool, this)),
+  m_requesterActivityMountRule(std::make_unique<RdbmsRequesterActivityMountRuleCatalogue>(m_log, m_connPool, this)),
+  m_requesterMountRule(std::make_unique<RdbmsRequesterMountRuleCatalogue>(m_log, m_connPool, this)),
+  m_requesterGroupMountRule(std::make_unique<RdbmsRequesterGroupMountRuleCatalogue>(m_log, m_connPool, this)),
+  m_driveConfig(std::make_unique<RdbmsDriveConfigCatalogue>(m_log, m_connPool)),
+  m_driveState(std::make_unique<RdbmsDriveStateCatalogue>(m_log, m_connPool)) {
+}
+
+const std::unique_ptr<SchemaCatalogue>& RdbmsCatalogue::Schema() {
+  return m_schema;
+}
+
+const std::unique_ptr<AdminUserCatalogue>& RdbmsCatalogue::AdminUser() {
+  return m_adminUser;
+}
+
+const std::unique_ptr<DiskSystemCatalogue>& RdbmsCatalogue::DiskSystem() {
+  return m_diskSystem;
+}
+
+const std::unique_ptr<DiskInstanceCatalogue>& RdbmsCatalogue::DiskInstance() {
+  return m_diskInstance;
+}
+
+const std::unique_ptr<DiskInstanceSpaceCatalogue>& RdbmsCatalogue::DiskInstanceSpace() {
+  return m_diskInstanceSpace;
+}
+
+const std::unique_ptr<VirtualOrganizationCatalogue>& RdbmsCatalogue::VO() {
+  return m_vo;
+}
+
+const std::unique_ptr<ArchiveRouteCatalogue>& RdbmsCatalogue::ArchiveRoute() {
+  return m_archiveRoute;
+}
+
+const std::unique_ptr<MediaTypeCatalogue>& RdbmsCatalogue::MediaType() {
+  return m_mediaType;
+}
+
+const std::unique_ptr<StorageClassCatalogue>& RdbmsCatalogue::StorageClass() {
+  return m_storageClass;
+}
+
+const std::unique_ptr<TapePoolCatalogue>& RdbmsCatalogue::TapePool() {
+  return m_tapePool;
+}
+
+const std::unique_ptr<TapeCatalogue>& RdbmsCatalogue::Tape() {
+  return m_tape;
+}
+
+const std::unique_ptr<MountPolicyCatalogue>& RdbmsCatalogue::MountPolicy() {
+  return m_mountPolicy;
+}
+
+const std::unique_ptr<RequesterActivityMountRuleCatalogue>& RdbmsCatalogue::RequesterActivityMountRule() {
+  return m_requesterActivityMountRule;
+}
+
+const std::unique_ptr<RequesterMountRuleCatalogue>& RdbmsCatalogue::RequesterMountRule() {
+  return m_requesterMountRule;
+}
+
+const std::unique_ptr<RequesterGroupMountRuleCatalogue>& RdbmsCatalogue::RequesterGroupMountRule() {
+  return m_requesterGroupMountRule;
+}
+
+const std::unique_ptr<LogicalLibraryCatalogue>& RdbmsCatalogue::LogicalLibrary() {
+  return m_logicalLibrary;
+}
+
+const std::unique_ptr<TapeFileCatalogue>& RdbmsCatalogue::TapeFile() {
+  return m_tapeFile;
+}
+
+const std::unique_ptr<FileRecycleLogCatalogue>& RdbmsCatalogue::FileRecycleLog() {
+  return m_fileRecycleLog;
+}
+
+const std::unique_ptr<DriveConfigCatalogue>& RdbmsCatalogue::DriveConfig() {
+  return m_driveConfig;
+}
+
+const std::unique_ptr<ArchiveFileCatalogue>& RdbmsCatalogue::ArchiveFile() {
+  return m_archiveFile;
+}
+
+const std::unique_ptr<DriveStateCatalogue>& RdbmsCatalogue::DriveState() {
+  return m_driveState;
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsCatalogue.hpp b/catalogue/rdbms/RdbmsCatalogue.hpp
new file mode 100644
index 0000000000..5a98a16007
--- /dev/null
+++ b/catalogue/rdbms/RdbmsCatalogue.hpp
@@ -0,0 +1,182 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/Group.hpp"
+#include "catalogue/TimeBasedCache.hpp"
+#include "catalogue/User.hpp"
+#include "common/threading/Mutex.hpp"
+#include "common/log/Logger.hpp"
+
+#include "common/dataStructures/MountPolicy.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Login;
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+/**
+ * CTA catalogue implemented using a relational database backend.
+ */
+class RdbmsCatalogue: public Catalogue {
+protected:
+  /**
+   * Protected constructor only to be called by sub-classes.
+   *
+   * @param log Object representing the API to the CTA logging system.
+   * @param login The database login details to be used to create new
+   * connections.
+   * @param nbConns The maximum number of concurrent connections to the
+   * underlying relational database for all operations accept listing archive
+   * files which can be relatively long operations.
+   * @param nbArchiveFileListingConns The maximum number of concurrent
+   * connections to the underlying relational database for the sole purpose of
+   * listing archive files.
+   */
+  RdbmsCatalogue(
+    log::Logger &log,
+    const rdbms::Login &login,
+    const uint64_t nbConns,
+    const uint64_t nbArchiveFileListingConns);
+
+public:
+  ~RdbmsCatalogue() override = default;
+
+  const std::unique_ptr<SchemaCatalogue>& Schema() override;
+  const std::unique_ptr<AdminUserCatalogue>& AdminUser() override;
+  const std::unique_ptr<DiskSystemCatalogue>& DiskSystem() override;
+  const std::unique_ptr<DiskInstanceCatalogue>& DiskInstance() override;
+  const std::unique_ptr<DiskInstanceSpaceCatalogue>& DiskInstanceSpace() override;
+  const std::unique_ptr<VirtualOrganizationCatalogue>& VO() override;
+  const std::unique_ptr<ArchiveRouteCatalogue>& ArchiveRoute() override;
+  const std::unique_ptr<MediaTypeCatalogue>& MediaType() override;
+  const std::unique_ptr<StorageClassCatalogue>& StorageClass() override;
+  const std::unique_ptr<TapePoolCatalogue>& TapePool() override;
+  const std::unique_ptr<TapeCatalogue>& Tape() override;
+  const std::unique_ptr<MountPolicyCatalogue>& MountPolicy() override;
+  const std::unique_ptr<RequesterActivityMountRuleCatalogue>& RequesterActivityMountRule() override;
+  const std::unique_ptr<RequesterMountRuleCatalogue>& RequesterMountRule() override;
+  const std::unique_ptr<RequesterGroupMountRuleCatalogue>& RequesterGroupMountRule() override;
+  const std::unique_ptr<LogicalLibraryCatalogue>& LogicalLibrary() override;
+  const std::unique_ptr<TapeFileCatalogue>& TapeFile() override;
+  const std::unique_ptr<FileRecycleLogCatalogue>& FileRecycleLog() override;
+  const std::unique_ptr<DriveConfigCatalogue>& DriveConfig() override;
+  const std::unique_ptr<DriveStateCatalogue>& DriveState() override;
+  const std::unique_ptr<ArchiveFileCatalogue>& ArchiveFile() override;
+
+protected:
+  friend class RdbmsFileRecycleLogCatalogue;
+  friend class RdbmsTapeCatalogue;
+  friend class RdbmsArchiveFileCatalogue;
+  friend class OracleTapeFileCatalogue;
+  friend class SqliteTapeFileCatalogue;
+  /**
+   * Object representing the API to the CTA logging system.
+   */
+  log::Logger &m_log;
+
+  /**
+   * Mutex to be used to a take a global lock on the database.
+   */
+  threading::Mutex m_mutex;
+
+  /**
+   * The pool of connections to the underlying relational database to be used
+   * for all operations accept listing archive files which can be relatively
+   * long operations.
+   */
+  mutable std::shared_ptr<rdbms::ConnPool> m_connPool;
+
+  /**
+   * The pool of connections to the underlying relational database to be used
+   * for the sole purpose of listing archive files.
+   */
+  mutable std::shared_ptr<rdbms::ConnPool> m_archiveFileListingConnPool;
+
+  /**
+   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
+   *
+   * @param conn The database connection.
+   * @param diskFileIds List of disk file IDs (fxid).
+   * @return Name of the temporary table
+   */
+  virtual std::string createAndPopulateTempTableFxid(rdbms::Conn &conn,
+    const std::optional<std::vector<std::string>> &diskFileIds) const = 0;
+
+  friend class RdbmsMountPolicyCatalogue;
+  friend class RdbmsRequesterActivityMountRuleCatalogue;
+  friend class RdbmsRequesterMountRuleCatalogue;
+  friend class RdbmsRequesterGroupMountRuleCatalogue;
+
+  /**
+   * Cached versions of mount policies for specific user groups.
+   */
+  mutable TimeBasedCache<Group, std::optional<common::dataStructures::MountPolicy> > m_groupMountPolicyCache;
+
+  /**
+   * Cached versions of mount policies for specific users.
+   */
+  mutable TimeBasedCache<User, std::optional<common::dataStructures::MountPolicy> > m_userMountPolicyCache;
+
+  /**
+   * Cached versions of all mount policies
+   */
+  mutable TimeBasedCache<std::string, std::list<common::dataStructures::MountPolicy>> m_allMountPoliciesCache;
+
+  friend class RdbmsVirtualOrganizationCatalogue;
+  friend class RdbmsTapePoolCatalogue;
+  /**
+   * Cached versions of virtual organization for specific tapepools
+   */
+  mutable TimeBasedCache<std::string, common::dataStructures::VirtualOrganization> m_tapepoolVirtualOrganizationCache;
+
+protected:
+  std::unique_ptr<SchemaCatalogue> m_schema;
+  std::unique_ptr<AdminUserCatalogue> m_adminUser;
+  std::unique_ptr<DiskSystemCatalogue> m_diskSystem;
+  std::unique_ptr<DiskInstanceCatalogue> m_diskInstance;
+  std::unique_ptr<DiskInstanceSpaceCatalogue> m_diskInstanceSpace;
+  std::unique_ptr<VirtualOrganizationCatalogue> m_vo;
+  std::unique_ptr<ArchiveRouteCatalogue> m_archiveRoute;
+  std::unique_ptr<MediaTypeCatalogue> m_mediaType;
+  std::unique_ptr<StorageClassCatalogue> m_storageClass;
+  std::unique_ptr<TapePoolCatalogue> m_tapePool;
+  std::unique_ptr<TapeCatalogue> m_tape;
+  std::unique_ptr<MountPolicyCatalogue> m_mountPolicy;
+  std::unique_ptr<RequesterActivityMountRuleCatalogue> m_requesterActivityMountRule;
+  std::unique_ptr<RequesterMountRuleCatalogue> m_requesterMountRule;
+  std::unique_ptr<RequesterGroupMountRuleCatalogue> m_requesterGroupMountRule;
+  std::unique_ptr<LogicalLibraryCatalogue> m_logicalLibrary;
+  std::unique_ptr<DriveConfigCatalogue> m_driveConfig;
+  std::unique_ptr<DriveStateCatalogue> m_driveState;
+  std::unique_ptr<TapeFileCatalogue> m_tapeFile;
+  std::unique_ptr<FileRecycleLogCatalogue> m_fileRecycleLog;
+  std::unique_ptr<ArchiveFileCatalogue> m_archiveFile;
+};  // class RdbmsCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp
similarity index 99%
rename from catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp
rename to catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp
index c944b44c4f..89c706cdf6 100644
--- a/catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp
+++ b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.cpp
@@ -16,7 +16,7 @@
  */
 
 #include "catalogue/CatalogueItor.hpp"
-#include "catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp"
 #include "common/exception/Exception.hpp"
 #include "common/exception/LostDatabaseConnection.hpp"
 #include "common/exception/UserError.hpp"
diff --git a/catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp
similarity index 98%
rename from catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp
rename to catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp
index b8d8f1b6c5..8ed84fc6d1 100644
--- a/catalogue/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp
+++ b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesForRepackItor.hpp
@@ -32,7 +32,7 @@ namespace catalogue {
  * RdbmsCatalogue::getArchiveFilesForRepack() implementation of
  * ArchiveFileItorImpl.
  */
-class RdbmsCatalogueGetArchiveFilesForRepackItor: public Catalogue::ArchiveFileItor::Impl {
+class RdbmsCatalogueGetArchiveFilesForRepackItor: public catalogue::ArchiveFileItor::Impl {
 public:
 
   /**
diff --git a/catalogue/RdbmsCatalogueGetArchiveFilesItor.cpp b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.cpp
similarity index 99%
rename from catalogue/RdbmsCatalogueGetArchiveFilesItor.cpp
rename to catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.cpp
index be4c0bcb4f..82df93b706 100644
--- a/catalogue/RdbmsCatalogueGetArchiveFilesItor.cpp
+++ b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.cpp
@@ -16,7 +16,7 @@
  */
 
 #include "catalogue/CatalogueItor.hpp"
-#include "catalogue/RdbmsCatalogueGetArchiveFilesItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.hpp"
 #include "common/exception/Exception.hpp"
 #include "common/exception/LostDatabaseConnection.hpp"
 #include "common/exception/UserError.hpp"
diff --git a/catalogue/RdbmsCatalogueGetArchiveFilesItor.hpp b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.hpp
similarity index 98%
rename from catalogue/RdbmsCatalogueGetArchiveFilesItor.hpp
rename to catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.hpp
index 5d757b6865..10651a1dfd 100644
--- a/catalogue/RdbmsCatalogueGetArchiveFilesItor.hpp
+++ b/catalogue/rdbms/RdbmsCatalogueGetArchiveFilesItor.hpp
@@ -32,7 +32,7 @@ namespace catalogue {
 /**
  * RdbmsCatalogue::getArchiveFiles() implementation of ArchiveFileItorImpl.
  */
-class RdbmsCatalogueGetArchiveFilesItor: public Catalogue::ArchiveFileItor::Impl {
+class RdbmsCatalogueGetArchiveFilesItor: public catalogue::ArchiveFileItor::Impl {
 public:
 
   /**
diff --git a/catalogue/RdbmsCatalogueGetFileRecycleLogItor.cpp b/catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.cpp
similarity index 99%
rename from catalogue/RdbmsCatalogueGetFileRecycleLogItor.cpp
rename to catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.cpp
index 0a969372b5..c8170fba1c 100644
--- a/catalogue/RdbmsCatalogueGetFileRecycleLogItor.cpp
+++ b/catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.cpp
@@ -16,7 +16,7 @@
  */
 
 #include "catalogue/CatalogueItor.hpp"
-#include "catalogue/RdbmsCatalogueGetFileRecycleLogItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.hpp"
 #include "common/dataStructures/FileRecycleLog.hpp"
 #include "common/exception/UserError.hpp"
 #include "common/log/LogContext.hpp"
diff --git a/catalogue/RdbmsCatalogueGetFileRecycleLogItor.hpp b/catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.hpp
similarity index 96%
rename from catalogue/RdbmsCatalogueGetFileRecycleLogItor.hpp
rename to catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.hpp
index 5bf51cd53e..df9dcb76aa 100644
--- a/catalogue/RdbmsCatalogueGetFileRecycleLogItor.hpp
+++ b/catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.hpp
@@ -26,7 +26,7 @@
 namespace cta {
 namespace catalogue {
 
-class RdbmsCatalogueGetFileRecycleLogItor : public Catalogue::FileRecycleLogItor::Impl {
+class RdbmsCatalogueGetFileRecycleLogItor : public FileRecycleLogItor::Impl {
 public:
 /**
    * Constructor.
@@ -108,4 +108,4 @@ private:
 };
 
 }  // namespace catalogue
-}  // namespace cta
\ No newline at end of file
+}  // namespace cta
diff --git a/catalogue/RdbmsCatalogueTapeContentsItor.cpp b/catalogue/rdbms/RdbmsCatalogueTapeContentsItor.cpp
similarity index 99%
rename from catalogue/RdbmsCatalogueTapeContentsItor.cpp
rename to catalogue/rdbms/RdbmsCatalogueTapeContentsItor.cpp
index 9b90e56e59..dff694ad75 100644
--- a/catalogue/RdbmsCatalogueTapeContentsItor.cpp
+++ b/catalogue/rdbms/RdbmsCatalogueTapeContentsItor.cpp
@@ -16,7 +16,7 @@
  */
 
 #include "catalogue/CatalogueItor.hpp"
-#include "catalogue/RdbmsCatalogueTapeContentsItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueTapeContentsItor.hpp"
 #include "common/exception/Exception.hpp"
 #include "common/exception/LostDatabaseConnection.hpp"
 #include "common/exception/UserError.hpp"
diff --git a/catalogue/RdbmsCatalogueTapeContentsItor.hpp b/catalogue/rdbms/RdbmsCatalogueTapeContentsItor.hpp
similarity index 97%
rename from catalogue/RdbmsCatalogueTapeContentsItor.hpp
rename to catalogue/rdbms/RdbmsCatalogueTapeContentsItor.hpp
index e26494e3ba..bb89ab75c8 100644
--- a/catalogue/RdbmsCatalogueTapeContentsItor.hpp
+++ b/catalogue/rdbms/RdbmsCatalogueTapeContentsItor.hpp
@@ -32,7 +32,7 @@ namespace catalogue {
 /**
  * Iteratess across the tape files that make up the contents of a given tape.
  */
-class RdbmsCatalogueTapeContentsItor: public Catalogue::ArchiveFileItor::Impl {
+class RdbmsCatalogueTapeContentsItor: public ArchiveFileItor::Impl {
 public:
   /**
    * Constructor.
diff --git a/catalogue/rdbms/RdbmsCatalogueUtils.cpp b/catalogue/rdbms/RdbmsCatalogueUtils.cpp
new file mode 100644
index 0000000000..b0e5fda3d6
--- /dev/null
+++ b/catalogue/rdbms/RdbmsCatalogueUtils.cpp
@@ -0,0 +1,508 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/log/Logger.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+bool RdbmsCatalogueUtils::diskSystemExists(rdbms::Conn &conn, const std::string &name) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME "
+      "FROM "
+        "DISK_SYSTEM "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_SYSTEM_NAME", name);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(const std::optional<std::string>& str, log::Logger* log) {
+  const size_t MAX_CHAR_COMMENT = 1000;
+  if (!str.has_value()) return;
+  if (str.value().length() > MAX_CHAR_COMMENT) {
+    log::LogContext lc(*log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("Large_Message: ", str.value());
+    lc.log(log::ERR, "The reason or comment has more characters than the maximun allowed.");
+    throw CommentOrReasonWithMoreSizeThanMaximunAllowed(
+      "The comment or reason string value has more than 1000 characters");
+  }
+}
+
+bool RdbmsCatalogueUtils::storageClassExists(rdbms::Conn &conn, const std::string &storageClassName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
+      "FROM "
+        "STORAGE_CLASS "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::virtualOrganizationExists(rdbms::Conn &conn, const std::string &voName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "WHERE "
+        "UPPER(VIRTUAL_ORGANIZATION_NAME) = UPPER(:VIRTUAL_ORGANIZATION_NAME)";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::mediaTypeExists(rdbms::Conn &conn, const std::string &name) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MEDIA_TYPE_NAME AS MEDIA_TYPE_NAME "
+      "FROM "
+        "MEDIA_TYPE "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::diskInstanceExists(rdbms::Conn &conn, const std::string &name) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME "
+      "FROM "
+        "DISK_INSTANCE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", name);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::tapePoolExists(rdbms::Conn &conn, const std::string &tapePoolName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE_POOL_NAME AS TAPE_POOL_NAME "
+      "FROM "
+        "TAPE_POOL "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::logicalLibraryExists(rdbms::Conn &conn, const std::string &logicalLibraryName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME "
+      "FROM "
+        "LOGICAL_LIBRARY "
+      "WHERE "
+        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::tapeExists(rdbms::Conn &conn, const std::string &vid) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VID AS VID "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::archiveFileIdExists(rdbms::Conn &conn, const uint64_t archiveFileId) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
+      "FROM "
+        "ARCHIVE_FILE "
+      "WHERE "
+        "ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::mountPolicyExists(rdbms::Conn &conn, const std::string &mountPolicyName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME "
+      "FROM "
+        "MOUNT_POLICY "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::archiveRouteExists(rdbms::Conn &conn, const std::string &storageClassName,
+  const uint32_t copyNb) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID AS STORAGE_CLASS_ID,"
+        "ARCHIVE_ROUTE.COPY_NB AS COPY_NB "
+      "FROM "
+        "ARCHIVE_ROUTE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "WHERE "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND "
+        "ARCHIVE_ROUTE.COPY_NB = :COPY_NB";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt.bindUint64(":COPY_NB", copyNb);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::requesterActivityMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+  const std::string &requesterName, const std::string &activityRegex) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
+        "REQUESTER_NAME AS REQUESTER_NAME, "
+        "ACTIVITY_REGEX AS ACTIVITY_REGEX "
+      "FROM "
+        "REQUESTER_ACTIVITY_MOUNT_RULE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME AND "
+        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::diskFileIdExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+  const std::string &diskFileId) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
+        "DISK_FILE_ID AS DISK_FILE_ID "
+      "FROM "
+        "ARCHIVE_FILE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "DISK_FILE_ID = :DISK_FILE_ID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":DISK_FILE_ID", diskFileId);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::diskFileUserExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+  uint32_t diskFileOwnerUid) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
+        "DISK_FILE_UID AS DISK_FILE_UID "
+      "FROM "
+        "ARCHIVE_FILE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "DISK_FILE_UID = :DISK_FILE_UID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindUint64(":DISK_FILE_UID", diskFileOwnerUid);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::diskFileGroupExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+  uint32_t diskFileGid) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
+        "DISK_FILE_GID AS DISK_FILE_GID "
+      "FROM "
+        "ARCHIVE_FILE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "DISK_FILE_GID = :DISK_FILE_GID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindUint64(":DISK_FILE_GID", diskFileGid);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::requesterMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+  const std::string &requesterName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "REQUESTER_NAME AS REQUESTER_NAME "
+      "FROM "
+        "REQUESTER_MOUNT_RULE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsCatalogueUtils::requesterGroupMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+  const std::string &requesterGroupName) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, "
+        "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME "
+      "FROM "
+        "REQUESTER_GROUP_MOUNT_RULE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+bool RdbmsCatalogueUtils::isSetAndEmpty(const std::optional<std::string> &optionalStr) {
+  return optionalStr && optionalStr->empty();
+}
+
+bool RdbmsCatalogueUtils::isSetAndEmpty(const std::optional<std::vector<std::string>> &optionalStrList) {
+  return optionalStrList && optionalStrList->empty();
+}
+
+void RdbmsCatalogueUtils::setTapeDirty(rdbms::Conn& conn, const std::string& vid) {
+  try {
+    const char * const sql =
+    "UPDATE TAPE SET DIRTY='1' WHERE VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsCatalogueUtils::setTapeDirty(rdbms::Conn& conn, const uint64_t & archiveFileId) {
+  try {
+    const char * const sql =
+    "UPDATE TAPE SET DIRTY='1' "
+    "WHERE VID IN "
+    "  (SELECT DISTINCT TAPE_FILE.VID AS VID FROM TAPE_FILE WHERE TAPE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID)";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    stmt.executeNonQuery();
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsCatalogueUtils::updateTape(rdbms::Conn &conn, const std::string &vid, const uint64_t lastFSeq,
+  const uint64_t compressedBytesWritten, const uint64_t filesWritten, const std::string &tapeDrive) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "LAST_FSEQ = :LAST_FSEQ,"
+        "DATA_IN_BYTES = DATA_IN_BYTES + :DATA_IN_BYTES,"
+        "MASTER_DATA_IN_BYTES = MASTER_DATA_IN_BYTES + :MASTER_DATA_IN_BYTES,"
+        "NB_MASTER_FILES = NB_MASTER_FILES + :MASTER_FILES,"
+        "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE,"
+        "LAST_WRITE_TIME = :LAST_WRITE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.bindUint64(":LAST_FSEQ", lastFSeq);
+    stmt.bindUint64(":DATA_IN_BYTES", compressedBytesWritten);
+    stmt.bindUint64(":MASTER_FILES", filesWritten);
+    stmt.bindUint64(":MASTER_DATA_IN_BYTES", compressedBytesWritten);
+    stmt.bindString(":LAST_WRITE_DRIVE", tapeDrive);
+    stmt.bindUint64(":LAST_WRITE_TIME", now);
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::string RdbmsCatalogueUtils::generateTapeStateModifiedBy( const common::dataStructures::SecurityIdentity & admin) {
+  return admin.username + "@" + admin.host;
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsCatalogueUtils.hpp b/catalogue/rdbms/RdbmsCatalogueUtils.hpp
new file mode 100644
index 0000000000..638cd2723f
--- /dev/null
+++ b/catalogue/rdbms/RdbmsCatalogueUtils.hpp
@@ -0,0 +1,87 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+}
+}
+
+namespace log {
+class Logger;
+}
+
+namespace rdbms {
+class Conn;
+}
+
+namespace catalogue {
+
+CTA_GENERATE_EXCEPTION_CLASS(CommentOrReasonWithMoreSizeThanMaximunAllowed);
+
+class RdbmsCatalogueUtils {
+public:
+  static bool diskSystemExists(rdbms::Conn &conn, const std::string &name);
+  static void checkCommentOrReasonMaxLength(const std::optional<std::string>& str, log::Logger* log);
+  static bool storageClassExists(rdbms::Conn &conn, const std::string &storageClassName);
+  static bool virtualOrganizationExists(rdbms::Conn &conn, const std::string &voName);
+  static bool mediaTypeExists(rdbms::Conn &conn, const std::string &name);
+  static bool diskInstanceExists(rdbms::Conn &conn, const std::string &name);
+  static bool tapePoolExists(rdbms::Conn &conn, const std::string &tapePoolName);
+  static bool logicalLibraryExists(rdbms::Conn &conn, const std::string &logicalLibraryName);
+  static bool tapeExists(rdbms::Conn &conn, const std::string &vid);
+  static bool archiveFileIdExists(rdbms::Conn &conn, const uint64_t archiveFileId);
+  static bool mountPolicyExists(rdbms::Conn &conn, const std::string &mountPolicyName);
+  static bool archiveRouteExists(rdbms::Conn &conn, const std::string &storageClassName, const uint32_t copyNb);
+  static bool requesterActivityMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+    const std::string &requesterName, const std::string &activityRegex);
+  static bool diskFileIdExists(rdbms::Conn &conn, const std::string &diskInstanceName, const std::string &diskFileId);
+  static bool diskFileUserExists(rdbms::Conn &conn, const std::string &diskInstanceName, uint32_t diskFileOwnerUid);
+  static bool diskFileGroupExists(rdbms::Conn &conn, const std::string &diskInstanceName, uint32_t diskFileGid);
+  static bool requesterMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+    const std::string &requesterName);
+  static bool requesterGroupMountRuleExists(rdbms::Conn &conn, const std::string &diskInstanceName,
+    const std::string &requesterGroupName);
+
+  static bool isSetAndEmpty(const std::optional<std::string> &optionalStr);
+  static bool isSetAndEmpty(const std::optional<std::vector<std::string>> &optionalStrList);
+
+  /**
+   * Set the DIRTY flag to true
+   * @param conn the database connection
+   * @param vid	the vid in which we want to set it as dirty
+   */
+  static void setTapeDirty(rdbms::Conn &conn, const std::string &vid);
+  static void setTapeDirty(rdbms::Conn& conn, const uint64_t & archiveFileId);
+  static void updateTape(rdbms::Conn &conn, const std::string &vid, const uint64_t lastFSeq,
+    const uint64_t compressedBytesWritten, const uint64_t filesWritten, const std::string &tapeDrive);
+  static std::string generateTapeStateModifiedBy(const common::dataStructures::SecurityIdentity & admin);
+};
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDiskInstanceCatalogue.cpp b/catalogue/rdbms/RdbmsDiskInstanceCatalogue.cpp
new file mode 100644
index 0000000000..4da97ff49e
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDiskInstanceCatalogue.cpp
@@ -0,0 +1,219 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsDiskInstanceCatalogue::RdbmsDiskInstanceCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool)
+  : m_log(log), m_connPool(connPool) {}
+
+void RdbmsDiskInstanceCatalogue::createDiskInstance(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create disk system because the name is an empty string");
+    }
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create disk system because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::diskInstanceExists(conn, name)) {
+      throw exception::UserError(std::string("Cannot create disk instance ") + name +
+        " because a disk instance with the same name identifier already exists");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO DISK_INSTANCE("
+        "DISK_INSTANCE_NAME,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":DISK_INSTANCE_NAME,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+   stmt.bindString(":DISK_INSTANCE_NAME", name);
+
+   stmt.bindString(":USER_COMMENT", comment);
+
+   stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+   stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+   stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+   stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+   stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+   stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceCatalogue::deleteDiskInstance(const std::string &name) {
+  try {
+    const char *const delete_sql =
+      "DELETE "
+      "FROM "
+        "DISK_INSTANCE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(delete_sql);
+      stmt.bindString(":DISK_INSTANCE_NAME", name);
+    stmt.executeNonQuery();
+
+    // The delete statement will effect no rows and will not raise an error if
+    // either the tape does not exist or if it still has tape files
+    if(0 == stmt.getNbAffectedRows()) {
+      if(RdbmsCatalogueUtils::diskInstanceExists(conn, name)) {
+        throw UserSpecifiedANonEmptyDiskInstanceAfterDelete(std::string("Cannot delete disk instance ") + name + " for unknown reason");
+      } else {
+        throw UserSpecifiedANonExistentDiskInstance(std::string("Cannot delete disk instance ") + name + " because it does not exist");
+      }
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceCatalogue::modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot modify disk instance"
+        " because the disk instance name is an empty string");
+    }
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot modify disk instance "
+        "because the new comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_INSTANCE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskInstance(std::string("Cannot modify disk instance ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::DiskInstance> RdbmsDiskInstanceCatalogue::getAllDiskInstances() const {
+  try {
+    std::list<common::dataStructures::DiskInstance> diskInstanceList;
+    std::string sql =
+      "SELECT "
+        "DISK_INSTANCE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+
+        "DISK_INSTANCE.USER_COMMENT AS USER_COMMENT,"
+
+        "DISK_INSTANCE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "DISK_INSTANCE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "DISK_INSTANCE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "DISK_INSTANCE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "DISK_INSTANCE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "DISK_INSTANCE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "DISK_INSTANCE";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::DiskInstance diskInstance;
+      diskInstance.name = rset.columnString("DISK_INSTANCE_NAME");
+      diskInstance.comment = rset.columnString("USER_COMMENT");
+      diskInstance.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      diskInstance.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      diskInstance.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      diskInstance.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      diskInstance.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      diskInstance.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+      diskInstanceList.push_back(diskInstance);
+    }
+    return diskInstanceList;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp b/catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp
new file mode 100644
index 0000000000..f17e4bd601
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp
@@ -0,0 +1,57 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/DiskInstanceCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsDiskInstanceCatalogue : public DiskInstanceCatalogue {
+public:
+  RdbmsDiskInstanceCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsDiskInstanceCatalogue() override = default;
+
+  void createDiskInstance(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+  void deleteDiskInstance(const std::string &name) override;
+
+  void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.cpp b/catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.cpp
new file mode 100644
index 0000000000..51ecbadd43
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.cpp
@@ -0,0 +1,405 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+#include <memory>
+
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsDiskInstanceCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp"
+#include "common/dataStructures/DiskInstanceSpace.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsDiskInstanceSpaceCatalogue::RdbmsDiskInstanceSpaceCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool)
+  : m_log(log), m_connPool(connPool) {}
+
+void RdbmsDiskInstanceSpaceCatalogue::deleteDiskInstanceSpace(const std::string &name,
+  const std::string &diskInstance) {
+  try {
+    const char *const delete_sql =
+      "DELETE "
+      "FROM "
+        "DISK_INSTANCE_SPACE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+      "AND "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(delete_sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+    stmt.executeNonQuery();
+
+    // The delete statement will effect no rows and will not raise an error if
+    // either the tape does not exist or if it still has tape files
+    if(0 == stmt.getNbAffectedRows()) {
+      if(diskInstanceSpaceExists(conn, name, diskInstance)) {
+        throw UserSpecifiedANonEmptyDiskInstanceSpaceAfterDelete(std::string("Cannot delete disk instance space")
+          + name + " for unknown reason");
+      } else {
+        throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot delete disk instance space ") 
+          + name + " because it does not exist");
+      }
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceSpaceCatalogue::createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL,
+  const uint64_t refreshInterval, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceSpaceName("Cannot create disk instance space because the name is an empty string");
+    }
+    if(freeSpaceQueryURL.empty()) {
+      throw UserSpecifiedAnEmptyStringFreeSpaceQueryURL("Cannot create disk instance space because the free space query URL is an empty string");
+    }
+    if(0 == refreshInterval) {
+      throw UserSpecifiedAZeroRefreshInterval("Cannot create disk instance space because the refresh interval is zero");
+    }
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create disk instance space because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    auto conn = m_connPool->getConn();
+    if(!RdbmsCatalogueUtils::diskInstanceExists(conn, diskInstance)) {
+      throw exception::UserError(std::string("Cannot create disk instance space ") + name +
+        " for disk instance " + diskInstance +
+        " because the disk instance does not exist");
+    }
+
+    if (diskInstanceSpaceExists(conn, name, diskInstance)) {
+      throw exception::UserError(std::string("Cannot create disk instance space ") + name +
+        " for disk instance " + diskInstance +
+        " because a disk instance space with the same name and disk instance already exists");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO DISK_INSTANCE_SPACE("
+        "DISK_INSTANCE_NAME,"
+        "DISK_INSTANCE_SPACE_NAME,"
+        "FREE_SPACE_QUERY_URL,"
+        "REFRESH_INTERVAL,"
+        "LAST_REFRESH_TIME,"
+        "FREE_SPACE,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":DISK_INSTANCE_NAME,"
+        ":DISK_INSTANCE_SPACE_NAME,"
+        ":FREE_SPACE_QUERY_URL,"
+        ":REFRESH_INTERVAL,"
+        ":LAST_REFRESH_TIME,"
+        ":FREE_SPACE,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+   stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+   stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+   stmt.bindString(":FREE_SPACE_QUERY_URL", freeSpaceQueryURL);
+   stmt.bindUint64(":REFRESH_INTERVAL", refreshInterval);
+   stmt.bindUint64(":LAST_REFRESH_TIME", 0);
+   stmt.bindUint64(":FREE_SPACE", 0);
+
+   stmt.bindString(":USER_COMMENT", comment);
+
+   stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+   stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+   stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+   stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+   stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+   stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::DiskInstanceSpace> RdbmsDiskInstanceSpaceCatalogue::getAllDiskInstanceSpaces() const {
+  try {
+    std::list<common::dataStructures::DiskInstanceSpace> diskInstanceSpaceList;
+    std::string sql =
+      "SELECT "
+        "DISK_INSTANCE_SPACE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "DISK_INSTANCE_SPACE.DISK_INSTANCE_SPACE_NAME AS DISK_INSTANCE_SPACE_NAME,"
+        "DISK_INSTANCE_SPACE.FREE_SPACE_QUERY_URL AS FREE_SPACE_QUERY_URL,"
+        "DISK_INSTANCE_SPACE.REFRESH_INTERVAL AS REFRESH_INTERVAL,"
+        "DISK_INSTANCE_SPACE.LAST_REFRESH_TIME AS LAST_REFRESH_TIME,"
+        "DISK_INSTANCE_SPACE.FREE_SPACE AS FREE_SPACE,"
+
+        "DISK_INSTANCE_SPACE.USER_COMMENT AS USER_COMMENT,"
+
+        "DISK_INSTANCE_SPACE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "DISK_INSTANCE_SPACE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "DISK_INSTANCE_SPACE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "DISK_INSTANCE_SPACE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "DISK_INSTANCE_SPACE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "DISK_INSTANCE_SPACE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "DISK_INSTANCE_SPACE";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::DiskInstanceSpace diskInstanceSpace;
+      diskInstanceSpace.name = rset.columnString("DISK_INSTANCE_SPACE_NAME");
+      diskInstanceSpace.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      diskInstanceSpace.freeSpaceQueryURL = rset.columnString("FREE_SPACE_QUERY_URL");
+      diskInstanceSpace.refreshInterval =  rset.columnUint64("REFRESH_INTERVAL");
+      diskInstanceSpace.freeSpace =  rset.columnUint64("FREE_SPACE");
+      diskInstanceSpace.lastRefreshTime =  rset.columnUint64("LAST_REFRESH_TIME");
+      diskInstanceSpace.comment = rset.columnString("USER_COMMENT");
+      diskInstanceSpace.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      diskInstanceSpace.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      diskInstanceSpace.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      diskInstanceSpace.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      diskInstanceSpace.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      diskInstanceSpace.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+      diskInstanceSpaceList.push_back(diskInstanceSpace);
+    }
+    return diskInstanceSpaceList;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const std::string &comment) {
+  try {
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot modify disk instance space "
+        "because the new comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_INSTANCE_SPACE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+      "AND "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name
+        + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceRefreshInterval(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const uint64_t refreshInterval) {
+  try {
+    if(0 == refreshInterval) {
+      throw UserSpecifiedAZeroRefreshInterval("Cannot modify disk instance space "
+        "because the new refreshInterval is zero");
+    }
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_INSTANCE_SPACE SET "
+        "REFRESH_INTERVAL = :REFRESH_INTERVAL,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+      "AND "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":REFRESH_INTERVAL", refreshInterval);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name
+        + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+  const std::string &diskInstance, const uint64_t freeSpace) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_INSTANCE_SPACE SET "
+        "FREE_SPACE = :FREE_SPACE,"
+        "LAST_REFRESH_TIME = :LAST_REFRESH_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+      "AND "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":FREE_SPACE", freeSpace);
+    stmt.bindUint64(":LAST_REFRESH_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name
+        + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskInstanceSpaceCatalogue::modifyDiskInstanceSpaceQueryURL(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const std::string &freeSpaceQueryURL) {
+  try {
+    if(freeSpaceQueryURL.empty()) {
+      throw UserSpecifiedAnEmptyStringFreeSpaceQueryURL("Cannot modify disk instance space "
+        "because the new freeSpaceQueryURL is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_INSTANCE_SPACE SET "
+        "FREE_SPACE_QUERY_URL = :FREE_SPACE_QUERY_URL,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+      "AND "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":FREE_SPACE_QUERY_URL", freeSpaceQueryURL);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskInstanceSpace(std::string("Cannot modify disk system ") + name
+        + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsDiskInstanceSpaceCatalogue::diskInstanceSpaceExists(rdbms::Conn &conn, const std::string &name,
+  const std::string &diskInstance) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_SPACE_NAME AS DISK_INSTANCE_SPACE_NAME "
+      "FROM "
+        "DISK_INSTANCE_SPACE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME "
+      " AND "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", name);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp b/catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp
new file mode 100644
index 0000000000..6fa4cab41a
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDiskInstanceSpaceCatalogue.hpp
@@ -0,0 +1,78 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct SecurityIdentity;
+}
+}
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsDiskInstanceSpaceCatalogue : public DiskInstanceSpaceCatalogue {
+public:
+  RdbmsDiskInstanceSpaceCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsDiskInstanceSpaceCatalogue() override = default;
+
+  void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) override;
+
+  void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name,
+    const std::string &diskInstance,
+    const std::string &freeSpaceQueryURL,
+    const uint64_t refreshInterval,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const override;
+
+  void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &comment) override;
+
+  void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) override;
+
+  void modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+    const std::string &diskInstance, const uint64_t freeSpace) override;
+
+  void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+
+  bool diskInstanceSpaceExists(rdbms::Conn &conn, const std::string &name, const std::string &diskInstance) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDiskSystemCatalogue.cpp b/catalogue/rdbms/RdbmsDiskSystemCatalogue.cpp
new file mode 100644
index 0000000000..83c4209164
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDiskSystemCatalogue.cpp
@@ -0,0 +1,492 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/interfaces/DiskInstanceCatalogue.hpp"
+#include "catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "disk/DiskSystem.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsDiskSystemCatalogue::RdbmsDiskSystemCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool)
+  : m_log(log), m_connPool(connPool) {}
+
+void RdbmsDiskSystemCatalogue::createDiskSystem(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &diskInstanceName, const std::string &diskInstanceSpaceName,
+  const std::string &fileRegexp, const uint64_t targetedFreeSpace, const time_t sleepTime, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot create disk system because the name is an empty string");
+    }
+    if(fileRegexp.empty()) {
+      throw UserSpecifiedAnEmptyStringFileRegexp("Cannot create disk system because the file regexp is an empty string");
+    }
+    if(diskInstanceName.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create disk system because the disk instance name is an empty string");
+    }
+    if(diskInstanceSpaceName.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceSpaceName("Cannot create disk system because the disk instance space name is an empty string");
+    }
+    if(0 == targetedFreeSpace) {
+      throw UserSpecifiedAZeroTargetedFreeSpace("Cannot create disk system because the targeted free space is zero");
+    }
+    if (0 == sleepTime) {
+      throw UserSpecifiedAZeroSleepTime("Cannot create disk system because the sleep time is zero");
+    }
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create disk system because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::diskSystemExists(conn, name)) {
+      throw exception::UserError(std::string("Cannot create disk system ") + name +
+        " because a disk system with the same name identifier already exists");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO DISK_SYSTEM("
+        "DISK_SYSTEM_NAME,"
+        "DISK_INSTANCE_NAME,"
+        "DISK_INSTANCE_SPACE_NAME,"
+        "FILE_REGEXP,"
+        "TARGETED_FREE_SPACE,"
+        "SLEEP_TIME,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":DISK_SYSTEM_NAME,"
+        ":DISK_INSTANCE_NAME,"
+        ":DISK_INSTANCE_SPACE_NAME,"
+        ":FILE_REGEXP,"
+        ":TARGETED_FREE_SPACE,"
+        ":SLEEP_TIME,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+   stmt.bindString(":DISK_SYSTEM_NAME", name);
+   stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+   stmt.bindString(":DISK_INSTANCE_SPACE_NAME", diskInstanceSpaceName);
+   stmt.bindString(":FILE_REGEXP", fileRegexp);
+   stmt.bindUint64(":TARGETED_FREE_SPACE", targetedFreeSpace);
+   stmt.bindUint64(":SLEEP_TIME", sleepTime);
+
+   stmt.bindString(":USER_COMMENT", comment);
+
+   stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+   stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+   stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+   stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+   stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+   stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::deleteDiskSystem(const std::string &name) {
+  try {
+    const char *const delete_sql =
+      "DELETE "
+      "FROM "
+        "DISK_SYSTEM "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(delete_sql);
+      stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    // The delete statement will effect no rows and will not raise an error if
+    // either the tape does not exist or if it still has tape files
+    if(0 == stmt.getNbAffectedRows()) {
+      if(RdbmsCatalogueUtils::diskSystemExists(conn, name)) {
+        throw UserSpecifiedANonEmptyDiskSystemAfterDelete(std::string("Cannot delete disk system ") + name + " for unknown reason");
+      } else {
+        throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot delete disk system ") + name + " because it does not exist");
+      }
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+disk::DiskSystemList RdbmsDiskSystemCatalogue::getAllDiskSystems() const {
+  try {
+    disk::DiskSystemList diskSystemList;
+    const std::string sql =
+      "SELECT "
+        "DISK_SYSTEM.DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME,"
+        "DISK_SYSTEM.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "DISK_SYSTEM.DISK_INSTANCE_SPACE_NAME AS DISK_INSTANCE_SPACE_NAME,"
+        "DISK_SYSTEM.FILE_REGEXP AS FILE_REGEXP,"
+        "DISK_SYSTEM.TARGETED_FREE_SPACE AS TARGETED_FREE_SPACE,"
+        "DISK_SYSTEM.SLEEP_TIME AS SLEEP_TIME,"
+
+        "DISK_SYSTEM.USER_COMMENT AS USER_COMMENT,"
+
+        "DISK_SYSTEM.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "DISK_SYSTEM.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "DISK_SYSTEM.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "DISK_SYSTEM.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "DISK_SYSTEM.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "DISK_SYSTEM.LAST_UPDATE_TIME AS LAST_UPDATE_TIME,"
+
+        "DISK_INSTANCE_SPACE.FREE_SPACE_QUERY_URL AS FREE_SPACE_QUERY_URL,"
+        "DISK_INSTANCE_SPACE.REFRESH_INTERVAL AS REFRESH_INTERVAL,"
+        "DISK_INSTANCE_SPACE.LAST_REFRESH_TIME AS LAST_REFRESH_TIME,"
+        "DISK_INSTANCE_SPACE.FREE_SPACE AS FREE_SPACE "
+      "FROM "
+        "DISK_SYSTEM "
+      "INNER JOIN DISK_INSTANCE_SPACE ON "
+        "DISK_SYSTEM.DISK_INSTANCE_NAME = DISK_INSTANCE_SPACE.DISK_INSTANCE_NAME "
+      "AND "
+        "DISK_SYSTEM.DISK_INSTANCE_SPACE_NAME = DISK_INSTANCE_SPACE.DISK_INSTANCE_SPACE_NAME"
+      ;
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      disk::DiskSystem diskSystem;
+      diskSystem.name = rset.columnString("DISK_SYSTEM_NAME");
+      diskSystem.fileRegexp = rset.columnString("FILE_REGEXP");
+      diskSystem.targetedFreeSpace =  rset.columnUint64("TARGETED_FREE_SPACE");
+      diskSystem.sleepTime =  rset.columnUint64("SLEEP_TIME");
+      diskSystem.comment = rset.columnString("USER_COMMENT");
+      diskSystem.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      diskSystem.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      diskSystem.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      diskSystem.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      diskSystem.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      diskSystem.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+      diskSystem.diskInstanceSpace.freeSpaceQueryURL = rset.columnString("FREE_SPACE_QUERY_URL");
+      diskSystem.diskInstanceSpace.refreshInterval = rset.columnUint64("REFRESH_INTERVAL");
+      diskSystem.diskInstanceSpace.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      diskSystem.diskInstanceSpace.name = rset.columnString("DISK_INSTANCE_SPACE_NAME");
+      diskSystem.diskInstanceSpace.lastRefreshTime = rset.columnUint64("LAST_REFRESH_TIME");
+      diskSystem.diskInstanceSpace.freeSpace = rset.columnUint64("FREE_SPACE");
+      diskSystemList.push_back(diskSystem);
+    }
+    return diskSystemList;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &fileRegexp) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
+        " because the disk system name is an empty string");
+    }
+    if(fileRegexp.empty()) {
+      throw UserSpecifiedAnEmptyStringFileRegexp("Cannot modify disk system "
+        "because the new fileRegexp is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_SYSTEM SET "
+        "FILE_REGEXP = :FILE_REGEXP,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":FILE_REGEXP", fileRegexp);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t targetedFreeSpace) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
+        " because the disk system name is an empty string");
+    }
+    if(0 == targetedFreeSpace) {
+      throw UserSpecifiedAZeroTargetedFreeSpace("Cannot modify disk system "
+        "because the new targeted free space has zero value");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_SYSTEM SET "
+        "TARGETED_FREE_SPACE = :TARGETED_FREE_SPACE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+        stmt.bindUint64(":TARGETED_FREE_SPACE", targetedFreeSpace);
+        stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+        stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+        stmt.bindUint64(":LAST_UPDATE_TIME", now);
+        stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
+        " because the disk system name is an empty string");
+    }
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot modify disk system "
+        "because the new comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_SYSTEM SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
+  const std::string& name, const uint64_t sleepTime) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
+        " because the disk system name is an empty string");
+    }
+    if(sleepTime == 0) {
+      throw UserSpecifiedAZeroSleepTime("Cannot modify disk system "
+        "because the new sleep time is zero");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_SYSTEM SET "
+        "SLEEP_TIME = :SLEEP_TIME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":SLEEP_TIME", sleepTime);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &diskInstanceName) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
+        " because the disk system name is an empty string");
+    }
+    if(diskInstanceName.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot modify disk system "
+        "because the new comment is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_SYSTEM SET "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDiskSystemCatalogue::modifyDiskSystemDiskInstanceSpaceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+  const std::string &diskInstanceSpaceName) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskSystemName("Cannot modify disk system"
+        " because the disk system name is an empty string");
+    }
+    if(diskInstanceSpaceName.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceSpaceName("Cannot modify disk system "
+        "because the new comment is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE DISK_SYSTEM SET "
+        "DISK_INSTANCE_SPACE_NAME = :DISK_INSTANCE_SPACE_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_SPACE_NAME", diskInstanceSpaceName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_SYSTEM_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentDiskSystem(std::string("Cannot modify disk system ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsDiskSystemCatalogue::diskSystemExists(const std::string &name) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return RdbmsCatalogueUtils::diskSystemExists(conn, name);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp b/catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp
new file mode 100644
index 0000000000..77b6aab19b
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDiskSystemCatalogue.hpp
@@ -0,0 +1,79 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/DiskSystemCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace disk {
+struct DiskSystemList;
+}
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsDiskSystemCatalogue : public DiskSystemCatalogue {
+public:
+  RdbmsDiskSystemCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsDiskSystemCatalogue() override = default;
+
+  void createDiskSystem(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &diskInstanceName, const std::string &diskInstanceSpaceName, const std::string &fileRegexp,
+    const uint64_t targetedFreeSpace, const time_t sleepTime, const std::string &comment) override;
+
+  void deleteDiskSystem(const std::string &name) override;
+
+  disk::DiskSystemList getAllDiskSystems() const override;
+
+  void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &fileRegexp) override;
+
+  void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t targetedFreeSpace) override;
+
+  void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
+    const std::string& name, const uint64_t sleepTime) override;
+
+  void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceName) override;
+
+  void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceSpaceName) override;
+
+  bool diskSystemExists(const std::string &name) const override;
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsDriveConfigCatalogue.cpp b/catalogue/rdbms/RdbmsDriveConfigCatalogue.cpp
new file mode 100644
index 0000000000..29a081e16f
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDriveConfigCatalogue.cpp
@@ -0,0 +1,248 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/CatalogueExceptions.hpp"
+#include "catalogue/CatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp"
+#include "common/dataStructures/DesiredDriveState.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/dataStructures/TapeDriveStatistics.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsDriveConfigCatalogue::RdbmsDriveConfigCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool)
+  : m_log(log), m_connPool(connPool) {
+}
+
+void RdbmsDriveConfigCatalogue::createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+  const std::string &keyName, const std::string &value, const std::string &source) {
+  try {
+    auto conn = m_connPool->getConn();
+    const char *const sql =
+    "INSERT INTO DRIVE_CONFIG(" "\n"
+      "DRIVE_NAME,"             "\n"
+      "CATEGORY,"               "\n"
+      "KEY_NAME,"               "\n"
+      "VALUE,"                  "\n"
+      "SOURCE)"                 "\n"
+    "VALUES("                   "\n"
+      ":DRIVE_NAME,"            "\n"
+      ":CATEGORY,"              "\n"
+      ":KEY_NAME,"              "\n"
+      ":VALUE,"                 "\n"
+      ":SOURCE"                 "\n"
+    ")";
+
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    stmt.bindString(":CATEGORY", category);
+    stmt.bindString(":KEY_NAME", keyName);
+    if (value.empty()) {
+      stmt.bindString(":VALUE", std::string("NULL"));
+    } else {
+      stmt.bindString(":VALUE", value);
+    }
+    if (source.empty()) {
+      stmt.bindString(":SOURCE", std::string("NULL"));
+    } else {
+      stmt.bindString(":SOURCE", source);
+    }
+
+    stmt.executeNonQuery();
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("driveName", tapeDriveName)
+      .add("category", category)
+      .add("keyName", keyName)
+      .add("value", value)
+      .add("source", source);
+    lc.log(log::INFO, "Catalogue - created drive configuration");
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<std::pair<std::string, std::string>> RdbmsDriveConfigCatalogue::getTapeDriveConfigNamesAndKeys() const {
+  try {
+    std::list<std::pair<std::string, std::string>> namesAndKeys;
+    const char *const sql =
+      "SELECT "
+        "DRIVE_NAME AS DRIVE_NAME,"
+        "KEY_NAME AS KEY_NAME "
+      "FROM "
+        "DRIVE_CONFIG ";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+
+    while (rset.next()) {
+      const std::string driveName = rset.columnString("DRIVE_NAME");
+      const std::string key = rset.columnString("KEY_NAME");
+      namesAndKeys.push_back(std::make_pair(driveName, key));
+    }
+    return namesAndKeys;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> RdbmsDriveConfigCatalogue::getTapeDriveConfigs() const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DRIVE_NAME AS DRIVE_NAME,"
+        "CATEGORY AS CATEGORY,"
+        "KEY_NAME AS KEY_NAME,"
+        "VALUE AS VALUE,"
+        "SOURCE AS SOURCE "
+      "FROM "
+        "DRIVE_CONFIG ";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> drivesConfigs;
+    while (rset.next()) {
+      const std::string tapeDriveName = rset.columnString("DRIVE_NAME");
+      const std::string category = rset.columnString("CATEGORY");
+      const std::string keyName = rset.columnString("KEY_NAME");
+      std::string value = rset.columnString("VALUE");
+      std::string source = rset.columnString("SOURCE");
+      if (value == "NULL") value.clear();
+      if (source == "NULL") source.clear();
+      cta::catalogue::DriveConfigCatalogue::DriveConfig driveConfig = {tapeDriveName, category, keyName, value, source};
+      drivesConfigs.push_back(driveConfig);
+    }
+    return drivesConfigs;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<std::tuple<std::string, std::string, std::string>> RdbmsDriveConfigCatalogue::getTapeDriveConfig(
+  const std::string &tapeDriveName, const std::string &keyName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DRIVE_NAME AS DRIVE_NAME,"
+        "CATEGORY AS CATEGORY,"
+        "KEY_NAME AS KEY_NAME,"
+        "VALUE AS VALUE,"
+        "SOURCE AS SOURCE "
+      "FROM "
+        "DRIVE_CONFIG "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME AND KEY_NAME = :KEY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    stmt.bindString(":KEY_NAME", keyName);
+    auto rset = stmt.executeQuery();
+    if (rset.next()) {
+      const std::string category = rset.columnString("CATEGORY");
+      std::string value = rset.columnString("VALUE");
+      std::string source = rset.columnString("SOURCE");
+      if (value == "NULL") value.clear();
+      if (source == "NULL") source.clear();
+      return std::make_tuple(category, value, source);
+    }
+    return std::nullopt;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveConfigCatalogue::modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+  const std::string &keyName, const std::string &value, const std::string &source) {
+  try {
+    const char *const sql =
+      "UPDATE DRIVE_CONFIG "
+      "SET "
+        "CATEGORY = :CATEGORY,"
+        "VALUE = :VALUE,"
+        "SOURCE = :SOURCE "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME AND KEY_NAME = :KEY_NAME";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    stmt.bindString(":CATEGORY", category);
+    stmt.bindString(":KEY_NAME", keyName);
+    if (value.empty()) {
+      stmt.bindString(":VALUE", std::string("NULL"));
+    } else {
+      stmt.bindString(":VALUE", value);
+    }
+    if (source.empty()) {
+      stmt.bindString(":SOURCE", std::string("NULL"));
+    } else {
+      stmt.bindString(":SOURCE", source);
+    }
+
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::Exception(std::string("Cannot modify Config Drive with name: ") + tapeDriveName +
+        " and key" + keyName + " because it doesn't exist");
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveConfigCatalogue::deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) {
+  try {
+    const char *const delete_sql =
+      "DELETE "
+      "FROM "
+        "DRIVE_CONFIG "
+      "WHERE "
+        "DRIVE_NAME = :DELETE_DRIVE_NAME AND KEY_NAME = :DELETE_KEY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(delete_sql);
+    stmt.bindString(":DELETE_DRIVE_NAME", tapeDriveName);
+    stmt.bindString(":DELETE_KEY_NAME", keyName);
+    stmt.executeNonQuery();
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp b/catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp
new file mode 100644
index 0000000000..fc23fcc943
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDriveConfigCatalogue.hpp
@@ -0,0 +1,65 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/interfaces/DriveConfigCatalogue.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsDriveConfigCatalogue : public DriveConfigCatalogue {
+public:
+  RdbmsDriveConfigCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsDriveConfigCatalogue() override = default;
+
+  void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::list<DriveConfig> getTapeDriveConfigs() const override;
+
+  std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const override;
+
+  void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig(const std::string &tapeDriveName,
+    const std::string &keyName) const override;
+
+  void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) override;
+
+private:
+  log::Logger &m_log;
+
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsDriveStateCatalogue.cpp b/catalogue/rdbms/RdbmsDriveStateCatalogue.cpp
new file mode 100644
index 0000000000..ac70123ac3
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDriveStateCatalogue.cpp
@@ -0,0 +1,1057 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <map>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/CatalogueExceptions.hpp"
+#include "catalogue/CatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsDriveStateCatalogue.hpp"
+#include "common/dataStructures/DesiredDriveState.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/dataStructures/TapeDriveStatistics.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsDriveStateCatalogue::RdbmsDriveStateCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool)
+  : m_log(log), m_connPool(connPool) {
+}
+
+void RdbmsDriveStateCatalogue::createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) {
+  try {
+    auto conn = m_connPool->getConn();
+    const char *const sql =
+    "INSERT INTO DRIVE_STATE("        "\n"
+      "DRIVE_NAME,"                   "\n"
+      "HOST,"                         "\n"
+      "LOGICAL_LIBRARY,"              "\n"
+      "SESSION_ID,"                   "\n"
+
+      "BYTES_TRANSFERED_IN_SESSION,"  "\n"
+      "FILES_TRANSFERED_IN_SESSION,"  "\n"
+
+      "SESSION_START_TIME,"           "\n"
+      "SESSION_ELAPSED_TIME,"         "\n"
+      "MOUNT_START_TIME,"             "\n"
+      "TRANSFER_START_TIME,"          "\n"
+      "UNLOAD_START_TIME,"            "\n"
+      "UNMOUNT_START_TIME,"           "\n"
+      "DRAINING_START_TIME,"          "\n"
+      "DOWN_OR_UP_START_TIME,"        "\n"
+      "PROBE_START_TIME,"             "\n"
+      "CLEANUP_START_TIME,"           "\n"
+      "START_START_TIME,"             "\n"
+      "SHUTDOWN_TIME,"                "\n"
+
+      "MOUNT_TYPE,"                   "\n"
+      "DRIVE_STATUS,"                 "\n"
+      "DESIRED_UP,"                   "\n"
+      "DESIRED_FORCE_DOWN,"           "\n"
+      "REASON_UP_DOWN,"               "\n"
+
+      "CURRENT_VID,"                  "\n"
+      "CTA_VERSION,"                  "\n"
+      "CURRENT_PRIORITY,"             "\n"
+      "CURRENT_ACTIVITY,"             "\n"
+      "CURRENT_TAPE_POOL,"            "\n"
+      "NEXT_MOUNT_TYPE,"              "\n"
+      "NEXT_VID,"                     "\n"
+      "NEXT_TAPE_POOL,"               "\n"
+      "NEXT_PRIORITY,"                "\n"
+      "NEXT_ACTIVITY,"                "\n"
+
+      "DEV_FILE_NAME,"                "\n"
+      "RAW_LIBRARY_SLOT,"             "\n"
+
+      "CURRENT_VO,"                   "\n"
+      "NEXT_VO,"                      "\n"
+      "USER_COMMENT,"                 "\n"
+
+      "CREATION_LOG_USER_NAME,"       "\n"
+      "CREATION_LOG_HOST_NAME,"       "\n"
+      "CREATION_LOG_TIME,"            "\n"
+      "LAST_UPDATE_USER_NAME,"        "\n"
+      "LAST_UPDATE_HOST_NAME,"        "\n"
+      "LAST_UPDATE_TIME,"             "\n"
+
+      "DISK_SYSTEM_NAME,"             "\n"
+      "RESERVED_BYTES,"               "\n"
+      "RESERVATION_SESSION_ID)"       "\n"
+    "VALUES("                         "\n"
+      ":DRIVE_NAME,"                  "\n"
+      ":HOST,"                        "\n"
+      ":LOGICAL_LIBRARY,"             "\n"
+      ":SESSION_ID,"                  "\n"
+
+      ":BYTES_TRANSFERED_IN_SESSION," "\n"
+      ":FILES_TRANSFERED_IN_SESSION," "\n"
+
+      ":SESSION_START_TIME,"          "\n"
+      ":SESSION_ELAPSED_TIME,"        "\n"
+      ":MOUNT_START_TIME,"            "\n"
+      ":TRANSFER_START_TIME,"         "\n"
+      ":UNLOAD_START_TIME,"           "\n"
+      ":UNMOUNT_START_TIME,"          "\n"
+      ":DRAINING_START_TIME,"         "\n"
+      ":DOWN_OR_UP_START_TIME,"       "\n"
+      ":PROBE_START_TIME,"            "\n"
+      ":CLEANUP_START_TIME,"          "\n"
+      ":START_START_TIME,"            "\n"
+      ":SHUTDOWN_TIME,"               "\n"
+
+      ":MOUNT_TYPE,"                  "\n"
+      ":DRIVE_STATUS,"                "\n"
+      ":DESIRED_UP,"                  "\n"
+      ":DESIRED_FORCE_DOWN,"          "\n"
+      ":REASON_UP_DOWN,"              "\n"
+
+      ":CURRENT_VID,"                 "\n"
+      ":CTA_VERSION,"                 "\n"
+      ":CURRENT_PRIORITY,"            "\n"
+      ":CURRENT_ACTIVITY,"            "\n"
+      ":CURRENT_TAPE_POOL,"           "\n"
+      ":NEXT_MOUNT_TYPE,"             "\n"
+      ":NEXT_VID,"                    "\n"
+      ":NEXT_TAPE_POOL,"              "\n"
+      ":NEXT_PRIORITY,"               "\n"
+      ":NEXT_ACTIVITY,"               "\n"
+
+      ":DEV_FILE_NAME,"               "\n"
+      ":RAW_LIBRARY_SLOT,"            "\n"
+
+      ":CURRENT_VO,"                  "\n"
+      ":NEXT_VO,"                     "\n"
+      ":USER_COMMENT,"                "\n"
+
+      ":CREATION_LOG_USER_NAME,"      "\n"
+      ":CREATION_LOG_HOST_NAME,"      "\n"
+      ":CREATION_LOG_TIME,"           "\n"
+      ":LAST_UPDATE_USER_NAME,"       "\n"
+      ":LAST_UPDATE_HOST_NAME,"       "\n"
+      ":LAST_UPDATE_TIME,"            "\n"
+
+      ":DISK_SYSTEM_NAME,"            "\n"
+      ":RESERVED_BYTES,"              "\n"
+      ":RESERVATION_SESSION_ID"       "\n"
+    ")";
+
+    auto stmt = conn.createStmt(sql);
+
+    settingSqlTapeDriveValues(&stmt, tapeDrive);
+
+    stmt.executeNonQuery();
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("driveName", tapeDrive.driveName)
+      .add("host", tapeDrive.host)
+      .add("logicalLibrary", tapeDrive.logicalLibrary)
+      .add("sessionId", tapeDrive.sessionId ? tapeDrive.sessionId.value() : 0)
+
+      .add("bytesTransferedInSession", tapeDrive.bytesTransferedInSession
+        ? tapeDrive.bytesTransferedInSession.value() : 0)
+      .add("filesTransferedInSession", tapeDrive.filesTransferedInSession
+        ? tapeDrive.filesTransferedInSession.value() : 0)
+
+      .add("sessionStartTime", tapeDrive.sessionStartTime ? tapeDrive.sessionStartTime.value() : 0)
+      .add("sessionElapsedTime", tapeDrive.sessionElapsedTime ? tapeDrive.sessionElapsedTime.value() : 0)
+      .add("mountStartTime", tapeDrive.mountStartTime ? tapeDrive.mountStartTime.value() : 0)
+      .add("transferStartTime", tapeDrive.transferStartTime
+        ? tapeDrive.transferStartTime.value() : 0)
+      .add("unloadStartTime", tapeDrive.unloadStartTime ? tapeDrive.unloadStartTime.value() : 0)
+      .add("unmountStartTime", tapeDrive.unmountStartTime ? tapeDrive.unmountStartTime.value() : 0)
+      .add("drainingStartTime", tapeDrive.drainingStartTime
+        ? tapeDrive.drainingStartTime.value() : 0)
+      .add("downOrUpStartTime", tapeDrive.downOrUpStartTime
+        ? tapeDrive.downOrUpStartTime.value() : 0)
+      .add("probeStartTime", tapeDrive.probeStartTime ? tapeDrive.probeStartTime.value() : 0)
+      .add("cleanupStartTime", tapeDrive.cleanupStartTime ? tapeDrive.cleanupStartTime.value() : 0)
+      .add("startStartTime", tapeDrive.startStartTime ? tapeDrive.startStartTime.value() : 0)
+      .add("shutdownTime", tapeDrive.shutdownTime ? tapeDrive.shutdownTime.value() : 0)
+
+      .add("mountType", common::dataStructures::toString(tapeDrive.mountType))
+      .add("driveStatus", common::dataStructures::TapeDrive::stateToString(tapeDrive.driveStatus))
+
+      .add("desiredUp", tapeDrive.desiredUp ? 1 : 0)
+      .add("desiredForceDown", tapeDrive.desiredForceDown ? 1 : 0)
+      .add("reasonUpDown", tapeDrive.reasonUpDown ? tapeDrive.reasonUpDown.value() : "")
+
+      .add("currentVo", tapeDrive.currentVo ? tapeDrive.currentVo.value() : "")
+      .add("nextVo", tapeDrive.nextVo ? tapeDrive.nextVo.value() : "")
+      .add("userComment", tapeDrive.userComment ? tapeDrive.userComment.value() : "")
+
+      .add("creationLog_username", tapeDrive.creationLog
+        ? tapeDrive.creationLog.value().username : "")
+      .add("creationLog_host", tapeDrive.creationLog ? tapeDrive.creationLog.value().host : "")
+      .add("creationLog_time", tapeDrive.creationLog ? tapeDrive.creationLog.value().time : 0)
+      .add("lastModificationLog_username", tapeDrive.lastModificationLog
+        ? tapeDrive.lastModificationLog.value().username : "")
+      .add("lastModificationLog_username", tapeDrive.lastModificationLog
+        ? tapeDrive.lastModificationLog.value().host : "")
+      .add("lastModificationLog_username", tapeDrive.lastModificationLog
+        ? tapeDrive.lastModificationLog.value().time : 0)
+
+      .add("diskSystemName", tapeDrive.diskSystemName ? tapeDrive.diskSystemName.value() : "")
+      .add("reservedBytes", tapeDrive.reservedBytes ? tapeDrive.reservedBytes.value() : 0)
+      .add("reservationSessionId", tapeDrive.reservationSessionId ? tapeDrive.reservationSessionId.value() : 0);
+
+    lc.log(log::INFO, "Catalogue - created tape drive");
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveStateCatalogue::settingSqlTapeDriveValues(cta::rdbms::Stmt *stmt,
+  const common::dataStructures::TapeDrive &tapeDrive) const {
+  auto setOptionalString = [&stmt](const std::string& sqlField, const std::optional<std::string>& optionalField) {
+    if (optionalField && !optionalField.value().empty()) {
+      stmt->bindString(sqlField, optionalField.value());
+    } else {
+      stmt->bindString(sqlField, std::nullopt);
+    }
+  };
+  auto setOptionalTime = [&stmt](const std::string &sqlField, const std::optional<time_t>& optionalField) {
+    if (optionalField) {
+      stmt->bindUint64(sqlField, optionalField.value());
+    } else {
+      stmt->bindUint64(sqlField, std::nullopt);
+    }
+  };
+
+  stmt->bindString(":DRIVE_NAME", tapeDrive.driveName);
+  stmt->bindString(":HOST", tapeDrive.host);
+  stmt->bindString(":LOGICAL_LIBRARY", tapeDrive.logicalLibrary);
+  stmt->bindUint64(":SESSION_ID", tapeDrive.sessionId);
+
+  stmt->bindUint64(":BYTES_TRANSFERED_IN_SESSION", tapeDrive.bytesTransferedInSession);
+  stmt->bindUint64(":FILES_TRANSFERED_IN_SESSION", tapeDrive.filesTransferedInSession);
+
+  setOptionalTime(":SESSION_START_TIME", tapeDrive.sessionStartTime);
+  setOptionalTime(":SESSION_ELAPSED_TIME", tapeDrive.sessionElapsedTime);
+  setOptionalTime(":MOUNT_START_TIME", tapeDrive.mountStartTime);
+  setOptionalTime(":TRANSFER_START_TIME", tapeDrive.transferStartTime);
+  setOptionalTime(":UNLOAD_START_TIME", tapeDrive.unloadStartTime);
+  setOptionalTime(":UNMOUNT_START_TIME", tapeDrive.unmountStartTime);
+  setOptionalTime(":DRAINING_START_TIME", tapeDrive.drainingStartTime);
+  setOptionalTime(":DOWN_OR_UP_START_TIME", tapeDrive.downOrUpStartTime);
+  setOptionalTime(":PROBE_START_TIME", tapeDrive.probeStartTime);
+  setOptionalTime(":CLEANUP_START_TIME", tapeDrive.cleanupStartTime);
+  setOptionalTime(":START_START_TIME", tapeDrive.startStartTime);
+  setOptionalTime(":SHUTDOWN_TIME", tapeDrive.shutdownTime);
+
+  stmt->bindString(":MOUNT_TYPE", common::dataStructures::toString(tapeDrive.mountType));
+  stmt->bindString(":DRIVE_STATUS", common::dataStructures::TapeDrive::stateToString(tapeDrive.driveStatus));
+
+  stmt->bindBool(":DESIRED_UP", tapeDrive.desiredUp);
+  stmt->bindBool(":DESIRED_FORCE_DOWN", tapeDrive.desiredForceDown);
+  setOptionalString(":REASON_UP_DOWN", tapeDrive.reasonUpDown);
+
+  setOptionalString(":CURRENT_VID", tapeDrive.currentVid);
+  setOptionalString(":CTA_VERSION", tapeDrive.ctaVersion);
+  stmt->bindUint64(":CURRENT_PRIORITY", tapeDrive.currentPriority);
+  setOptionalString(":CURRENT_ACTIVITY", tapeDrive.currentActivity);
+  setOptionalString(":CURRENT_TAPE_POOL", tapeDrive.currentTapePool);
+  stmt->bindString(":NEXT_MOUNT_TYPE", common::dataStructures::toString(tapeDrive.nextMountType));
+  setOptionalString(":NEXT_VID", tapeDrive.nextVid);
+  setOptionalString(":NEXT_TAPE_POOL", tapeDrive.nextTapePool);
+  stmt->bindUint64(":NEXT_PRIORITY", tapeDrive.nextPriority);
+  setOptionalString(":NEXT_ACTIVITY", tapeDrive.nextActivity);
+
+  setOptionalString(":DEV_FILE_NAME", tapeDrive.devFileName);
+  setOptionalString(":RAW_LIBRARY_SLOT", tapeDrive.rawLibrarySlot);
+
+  setOptionalString(":CURRENT_VO", tapeDrive.currentVo);
+  setOptionalString(":NEXT_VO", tapeDrive.nextVo);
+  setOptionalString(":USER_COMMENT", tapeDrive.userComment);
+
+  auto setEntryLog = [stmt, setOptionalString, setOptionalTime](const std::string &field,
+    const std::optional<std::string> &username, const std::optional<std::string> &host, const std::optional<time_t> &time) {
+      setOptionalString(field + "_USER_NAME", username);
+      setOptionalString(field + "_HOST_NAME", host);
+      setOptionalTime(field + "_TIME", time);
+  };
+
+  if (tapeDrive.creationLog) {
+    setEntryLog(":CREATION_LOG", tapeDrive.creationLog.value().username,
+      tapeDrive.creationLog.value().host, tapeDrive.creationLog.value().time);
+  } else {
+    setEntryLog(":CREATION_LOG", std::nullopt, std::nullopt, std::nullopt);
+  }
+
+  if (tapeDrive.lastModificationLog) {
+    setEntryLog(":LAST_UPDATE", tapeDrive.lastModificationLog.value().username,
+      tapeDrive.lastModificationLog.value().host, tapeDrive.lastModificationLog.value().time);
+  } else {
+    setEntryLog(":LAST_UPDATE", std::nullopt, std::nullopt, std::nullopt);
+  }
+
+  setOptionalString(":DISK_SYSTEM_NAME", tapeDrive.diskSystemName);
+  stmt->bindUint64(":RESERVED_BYTES", tapeDrive.reservedBytes);
+  stmt->bindUint64(":RESERVATION_SESSION_ID", tapeDrive.reservationSessionId);
+}
+
+void RdbmsDriveStateCatalogue::deleteTapeDrive(const std::string &tapeDriveName) {
+  try {
+    const char *const delete_sql =
+      "DELETE FROM "
+        "DRIVE_STATE "
+      "WHERE "
+        "DRIVE_NAME = :DELETE_DRIVE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(delete_sql);
+    stmt.bindString(":DELETE_DRIVE_NAME", tapeDriveName);
+    stmt.executeNonQuery();
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<std::string> RdbmsDriveStateCatalogue::getTapeDriveNames() const {
+  try {
+    std::list<std::string> tapeDriveNames;
+    const char *const sql =
+      "SELECT "
+        "DRIVE_NAME AS DRIVE_NAME "
+      "FROM "
+        "DRIVE_STATE ";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+
+    while (rset.next()) {
+      const std::string driveName = rset.columnString("DRIVE_NAME");
+      tapeDriveNames.push_back(driveName);
+    }
+    return tapeDriveNames;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::TapeDrive RdbmsDriveStateCatalogue::gettingSqlTapeDriveValues(cta::rdbms::Rset* rset) const {
+  common::dataStructures::TapeDrive tapeDrive;
+  auto getOptionalTime = [](const std::optional<uint64_t> &time) -> std::optional<time_t> {
+    if (!time) return std::nullopt;
+    return time.value();
+  };
+  tapeDrive.driveName = rset->columnString("DRIVE_NAME");
+  tapeDrive.host = rset->columnString("HOST");
+  tapeDrive.logicalLibrary = rset->columnString("LOGICAL_LIBRARY");
+  tapeDrive.sessionId = rset->columnOptionalUint64("SESSION_ID");
+  tapeDrive.logicalLibraryDisabled = rset->columnOptionalBool("IS_DISABLED") ? rset->columnOptionalBool("IS_DISABLED").value() : false;
+
+  tapeDrive.bytesTransferedInSession = rset->columnOptionalUint64("BYTES_TRANSFERED_IN_SESSION");
+  tapeDrive.filesTransferedInSession = rset->columnOptionalUint64("FILES_TRANSFERED_IN_SESSION");
+
+  tapeDrive.sessionStartTime = getOptionalTime(rset->columnOptionalUint64("SESSION_START_TIME"));
+  tapeDrive.sessionElapsedTime = getOptionalTime(rset->columnOptionalUint64("SESSION_ELAPSED_TIME"));
+  tapeDrive.mountStartTime = getOptionalTime(rset->columnOptionalUint64("MOUNT_START_TIME"));
+  tapeDrive.transferStartTime = getOptionalTime(rset->columnOptionalUint64("TRANSFER_START_TIME"));
+  tapeDrive.unloadStartTime = getOptionalTime(rset->columnOptionalUint64("UNLOAD_START_TIME"));
+  tapeDrive.unmountStartTime = getOptionalTime(rset->columnOptionalUint64("UNMOUNT_START_TIME"));
+  tapeDrive.drainingStartTime = getOptionalTime(rset->columnOptionalUint64("DRAINING_START_TIME"));
+  tapeDrive.downOrUpStartTime = getOptionalTime(rset->columnOptionalUint64("DOWN_OR_UP_START_TIME"));
+  tapeDrive.probeStartTime = getOptionalTime(rset->columnOptionalUint64("PROBE_START_TIME"));
+  tapeDrive.cleanupStartTime = getOptionalTime(rset->columnOptionalUint64("CLEANUP_START_TIME"));
+  tapeDrive.startStartTime = getOptionalTime(rset->columnOptionalUint64("START_START_TIME"));
+  tapeDrive.shutdownTime = getOptionalTime(rset->columnOptionalUint64("SHUTDOWN_TIME"));
+
+  tapeDrive.mountType = common::dataStructures::strToMountType(rset->columnString("MOUNT_TYPE"));
+  tapeDrive.driveStatus = common::dataStructures::TapeDrive::stringToState(rset->columnString("DRIVE_STATUS"));
+  tapeDrive.desiredUp = rset->columnBool("DESIRED_UP");
+  tapeDrive.desiredForceDown = rset->columnBool("DESIRED_FORCE_DOWN");
+  tapeDrive.reasonUpDown = rset->columnOptionalString("REASON_UP_DOWN");
+
+  tapeDrive.currentVid = rset->columnOptionalString("CURRENT_VID");
+  tapeDrive.ctaVersion = rset->columnOptionalString("CTA_VERSION");
+  tapeDrive.currentPriority = rset->columnOptionalUint64("CURRENT_PRIORITY");
+  tapeDrive.currentActivity = rset->columnOptionalString("CURRENT_ACTIVITY");
+  tapeDrive.currentTapePool = rset->columnOptionalString("CURRENT_TAPE_POOL");
+  tapeDrive.nextMountType = common::dataStructures::strToMountType(rset->columnString("NEXT_MOUNT_TYPE"));
+  tapeDrive.nextVid = rset->columnOptionalString("NEXT_VID");
+  tapeDrive.nextTapePool = rset->columnOptionalString("NEXT_TAPE_POOL");
+  tapeDrive.nextPriority = rset->columnOptionalUint64("NEXT_PRIORITY");
+  tapeDrive.nextActivity = rset->columnOptionalString("NEXT_ACTIVITY");
+
+  tapeDrive.devFileName = rset->columnOptionalString("DEV_FILE_NAME");
+  tapeDrive.rawLibrarySlot = rset->columnOptionalString("RAW_LIBRARY_SLOT");
+
+  tapeDrive.currentVo = rset->columnOptionalString("CURRENT_VO");
+  tapeDrive.nextVo = rset->columnOptionalString("NEXT_VO");
+
+  tapeDrive.diskSystemName = rset->columnOptionalString("DISK_SYSTEM_NAME");
+  tapeDrive.reservedBytes = rset->columnOptionalUint64("RESERVED_BYTES");
+  tapeDrive.reservationSessionId = rset->columnOptionalUint64("RESERVATION_SESSION_ID");
+
+  tapeDrive.userComment = rset->columnOptionalString("USER_COMMENT");
+  auto setOptionEntryLog = [&rset](const std::string &username, const std::string &host,
+    const std::string &time) -> std::optional<common::dataStructures::EntryLog> {
+    if (!rset->columnOptionalString(username)) {
+      return std::nullopt;
+    } else {
+      return common::dataStructures::EntryLog(
+        rset->columnString(username),
+        rset->columnString(host),
+        rset->columnUint64(time));
+    }
+  };
+  tapeDrive.creationLog = setOptionEntryLog("CREATION_LOG_USER_NAME", "CREATION_LOG_HOST_NAME",
+    "CREATION_LOG_TIME");
+  tapeDrive.lastModificationLog = setOptionEntryLog("LAST_UPDATE_USER_NAME", "LAST_UPDATE_HOST_NAME",
+    "LAST_UPDATE_TIME");
+
+  // Log warning for operators that tape drive logical library does not exist (cta/CTA#1163)
+  if (!rset->columnOptionalBool("IS_DISABLED")) {
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("driveName", tapeDrive.driveName)
+       .add("logicalLibrary", tapeDrive.logicalLibrary);
+    lc.log(log::WARNING, "RdbmsCatalogue::gettingSqlTapeDriveValues(): Logical library for tape drive does not exist in the catalogue");
+  }
+  return tapeDrive;
+}
+
+std::list<common::dataStructures::TapeDrive> RdbmsDriveStateCatalogue::getTapeDrives() const {
+  try {
+    std::list<common::dataStructures::TapeDrive> tapeDrives;
+    const char *const sql =
+      "SELECT "
+        "DRIVE_STATE.DRIVE_NAME AS DRIVE_NAME,"
+        "DRIVE_STATE.HOST AS HOST,"
+        "DRIVE_STATE.LOGICAL_LIBRARY AS LOGICAL_LIBRARY,"
+        "DRIVE_STATE.SESSION_ID AS SESSION_ID,"
+
+        "DRIVE_STATE.BYTES_TRANSFERED_IN_SESSION AS BYTES_TRANSFERED_IN_SESSION,"
+        "DRIVE_STATE.FILES_TRANSFERED_IN_SESSION AS FILES_TRANSFERED_IN_SESSION,"
+
+        "DRIVE_STATE.SESSION_START_TIME AS SESSION_START_TIME,"
+        "DRIVE_STATE.SESSION_ELAPSED_TIME AS SESSION_ELAPSED_TIME,"
+        "DRIVE_STATE.MOUNT_START_TIME AS MOUNT_START_TIME,"
+        "DRIVE_STATE.TRANSFER_START_TIME AS TRANSFER_START_TIME,"
+        "DRIVE_STATE.UNLOAD_START_TIME AS UNLOAD_START_TIME,"
+        "DRIVE_STATE.UNMOUNT_START_TIME AS UNMOUNT_START_TIME,"
+        "DRIVE_STATE.DRAINING_START_TIME AS DRAINING_START_TIME,"
+        "DRIVE_STATE.DOWN_OR_UP_START_TIME AS DOWN_OR_UP_START_TIME,"
+        "DRIVE_STATE.PROBE_START_TIME AS PROBE_START_TIME,"
+        "DRIVE_STATE.CLEANUP_START_TIME AS CLEANUP_START_TIME,"
+        "DRIVE_STATE.START_START_TIME AS START_START_TIME,"
+        "DRIVE_STATE.SHUTDOWN_TIME AS SHUTDOWN_TIME,"
+
+        "DRIVE_STATE.MOUNT_TYPE AS MOUNT_TYPE,"
+        "DRIVE_STATE.DRIVE_STATUS AS DRIVE_STATUS,"
+        "DRIVE_STATE.DESIRED_UP AS DESIRED_UP,"
+        "DRIVE_STATE.DESIRED_FORCE_DOWN AS DESIRED_FORCE_DOWN,"
+        "DRIVE_STATE.REASON_UP_DOWN AS REASON_UP_DOWN,"
+
+        "DRIVE_STATE.CURRENT_VID AS CURRENT_VID,"
+        "DRIVE_STATE.CTA_VERSION AS CTA_VERSION,"
+        "DRIVE_STATE.CURRENT_PRIORITY AS CURRENT_PRIORITY,"
+        "DRIVE_STATE.CURRENT_ACTIVITY AS CURRENT_ACTIVITY,"
+        "DRIVE_STATE.CURRENT_TAPE_POOL AS CURRENT_TAPE_POOL,"
+        "DRIVE_STATE.NEXT_MOUNT_TYPE AS NEXT_MOUNT_TYPE,"
+        "DRIVE_STATE.NEXT_VID AS NEXT_VID,"
+        "DRIVE_STATE.NEXT_TAPE_POOL AS NEXT_TAPE_POOL,"
+        "DRIVE_STATE.NEXT_PRIORITY AS NEXT_PRIORITY,"
+        "DRIVE_STATE.NEXT_ACTIVITY AS NEXT_ACTIVITY,"
+
+        "DRIVE_STATE.DEV_FILE_NAME AS DEV_FILE_NAME,"
+        "DRIVE_STATE.RAW_LIBRARY_SLOT AS RAW_LIBRARY_SLOT,"
+
+        "DRIVE_STATE.CURRENT_VO AS CURRENT_VO,"
+        "DRIVE_STATE.NEXT_VO AS NEXT_VO,"
+        "DRIVE_STATE.USER_COMMENT AS USER_COMMENT,"
+
+        "DRIVE_STATE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "DRIVE_STATE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "DRIVE_STATE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "DRIVE_STATE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "DRIVE_STATE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "DRIVE_STATE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME,"
+
+        "DRIVE_STATE.DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME,"
+        "DRIVE_STATE.RESERVED_BYTES AS RESERVED_BYTES,"
+        "DRIVE_STATE.RESERVATION_SESSION_ID AS RESERVATION_SESSION_ID,"
+        "LOGICAL_LIBRARY.IS_DISABLED AS IS_DISABLED "
+      "FROM "
+        "DRIVE_STATE "
+      "LEFT JOIN "
+        "LOGICAL_LIBRARY "
+      "ON "
+        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = DRIVE_STATE.LOGICAL_LIBRARY "
+      "ORDER BY "
+        "DRIVE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+
+    while (rset.next()) {
+      auto tapeDrive = gettingSqlTapeDriveValues(&rset);
+      tapeDrives.push_back(tapeDrive);
+    }
+    return tapeDrives;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<common::dataStructures::TapeDrive> RdbmsDriveStateCatalogue::getTapeDrive(
+  const std::string &tapeDriveName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "DRIVE_STATE.DRIVE_NAME AS DRIVE_NAME,"
+        "DRIVE_STATE.HOST AS HOST,"
+        "DRIVE_STATE.LOGICAL_LIBRARY AS LOGICAL_LIBRARY,"
+        "DRIVE_STATE.SESSION_ID AS SESSION_ID,"
+
+        "DRIVE_STATE.BYTES_TRANSFERED_IN_SESSION AS BYTES_TRANSFERED_IN_SESSION,"
+        "DRIVE_STATE.FILES_TRANSFERED_IN_SESSION AS FILES_TRANSFERED_IN_SESSION,"
+
+        "DRIVE_STATE.SESSION_START_TIME AS SESSION_START_TIME,"
+        "DRIVE_STATE.SESSION_ELAPSED_TIME AS SESSION_ELAPSED_TIME,"
+        "DRIVE_STATE.MOUNT_START_TIME AS MOUNT_START_TIME,"
+        "DRIVE_STATE.TRANSFER_START_TIME AS TRANSFER_START_TIME,"
+        "DRIVE_STATE.UNLOAD_START_TIME AS UNLOAD_START_TIME,"
+        "DRIVE_STATE.UNMOUNT_START_TIME AS UNMOUNT_START_TIME,"
+        "DRIVE_STATE.DRAINING_START_TIME AS DRAINING_START_TIME,"
+        "DRIVE_STATE.DOWN_OR_UP_START_TIME AS DOWN_OR_UP_START_TIME,"
+        "DRIVE_STATE.PROBE_START_TIME AS PROBE_START_TIME,"
+        "DRIVE_STATE.CLEANUP_START_TIME AS CLEANUP_START_TIME,"
+        "DRIVE_STATE.START_START_TIME AS START_START_TIME,"
+        "DRIVE_STATE.SHUTDOWN_TIME AS SHUTDOWN_TIME,"
+
+        "DRIVE_STATE.MOUNT_TYPE AS MOUNT_TYPE,"
+        "DRIVE_STATE.DRIVE_STATUS AS DRIVE_STATUS,"
+        "DRIVE_STATE.DESIRED_UP AS DESIRED_UP,"
+        "DRIVE_STATE.DESIRED_FORCE_DOWN AS DESIRED_FORCE_DOWN,"
+        "DRIVE_STATE.REASON_UP_DOWN AS REASON_UP_DOWN,"
+
+        "DRIVE_STATE.CURRENT_VID AS CURRENT_VID,"
+        "DRIVE_STATE.CTA_VERSION AS CTA_VERSION,"
+        "DRIVE_STATE.CURRENT_PRIORITY AS CURRENT_PRIORITY,"
+        "DRIVE_STATE.CURRENT_ACTIVITY AS CURRENT_ACTIVITY,"
+        "DRIVE_STATE.CURRENT_TAPE_POOL AS CURRENT_TAPE_POOL,"
+        "DRIVE_STATE.NEXT_MOUNT_TYPE AS NEXT_MOUNT_TYPE,"
+        "DRIVE_STATE.NEXT_VID AS NEXT_VID,"
+        "DRIVE_STATE.NEXT_TAPE_POOL AS NEXT_TAPE_POOL,"
+        "DRIVE_STATE.NEXT_PRIORITY AS NEXT_PRIORITY,"
+        "DRIVE_STATE.NEXT_ACTIVITY AS NEXT_ACTIVITY,"
+
+        "DRIVE_STATE.DEV_FILE_NAME AS DEV_FILE_NAME,"
+        "DRIVE_STATE.RAW_LIBRARY_SLOT AS RAW_LIBRARY_SLOT,"
+
+        "DRIVE_STATE.CURRENT_VO AS CURRENT_VO,"
+        "DRIVE_STATE.NEXT_VO AS NEXT_VO,"
+        "DRIVE_STATE.USER_COMMENT AS USER_COMMENT,"
+
+        "DRIVE_STATE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "DRIVE_STATE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "DRIVE_STATE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "DRIVE_STATE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "DRIVE_STATE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "DRIVE_STATE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME,"
+
+        "DRIVE_STATE.DISK_SYSTEM_NAME AS DISK_SYSTEM_NAME,"
+        "DRIVE_STATE.RESERVED_BYTES AS RESERVED_BYTES,"
+        "DRIVE_STATE.RESERVATION_SESSION_ID AS RESERVATION_SESSION_ID,"
+        "LOGICAL_LIBRARY.IS_DISABLED AS IS_DISABLED "
+      "FROM "
+        "DRIVE_STATE "
+      "LEFT JOIN "
+        "LOGICAL_LIBRARY "
+      "ON "
+        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = DRIVE_STATE.LOGICAL_LIBRARY "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    auto rset = stmt.executeQuery();
+
+    if (rset.next()) {
+      return gettingSqlTapeDriveValues(&rset);
+    }
+    return std::nullopt;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveStateCatalogue::setDesiredTapeDriveState(const std::string& tapeDriveName,
+  const common::dataStructures::DesiredDriveState &desiredState) {
+  try {
+    CatalogueUtils::checkCommentOrReasonMaxLength(desiredState.reason, m_log);
+    std::string sql =
+      "UPDATE DRIVE_STATE SET "
+        "DESIRED_UP = :DESIRED_UP,"
+        "DESIRED_FORCE_DOWN = :DESIRED_FORCE_DOWN,";
+    if (desiredState.reason) {
+      sql += "REASON_UP_DOWN = ";
+      sql += desiredState.reason.value().empty() ? "''," : ":REASON_UP_DOWN,";
+    }
+
+    // Remove last ',' character
+    sql.erase(sql.find_last_of(','), 1);
+    sql += " WHERE "
+      "DRIVE_NAME = :DRIVE_NAME";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql.c_str());
+
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    stmt.bindBool(":DESIRED_UP", desiredState.up);
+    stmt.bindBool(":DESIRED_FORCE_DOWN", desiredState.forceDown);
+    if (desiredState.reason && !desiredState.reason.value().empty()) {
+      stmt.bindString(":REASON_UP_DOWN", desiredState.reason.value());
+    }
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify Tape Drive: ") + tapeDriveName +
+        " because it doesn't exist");
+    }
+  } catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveStateCatalogue::setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+  const std::string &comment) {
+  try {
+    const char* const sql =
+      "UPDATE DRIVE_STATE "
+      "SET "
+        "USER_COMMENT = :USER_COMMENT "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    auto bindOptionalStringIfSet = [&stmt](const std::string& sqlField,
+      const std::optional<std::string>& optionalString) {
+      if (optionalString) {
+        if (optionalString.value().empty()) {
+          stmt.bindString(sqlField, std::nullopt);
+        } else {
+          stmt.bindString(sqlField, optionalString.value());
+        }
+      }
+    };
+
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    bindOptionalStringIfSet(":USER_COMMENT", comment);
+
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify Tape Drive: ") + tapeDriveName +
+        " because it doesn't exist");
+    }
+  } catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveStateCatalogue::updateTapeDriveStatistics(const std::string& tapeDriveName,
+  const std::string& host, const std::string& logicalLibrary,
+  const common::dataStructures::TapeDriveStatistics& statistics) {
+  try {
+    const char *const sql =
+      "UPDATE DRIVE_STATE "
+      "SET "
+        "HOST = :HOST,"
+        "LOGICAL_LIBRARY = :LOGICAL_LIBRARY,"
+        "BYTES_TRANSFERED_IN_SESSION = :BYTES_TRANSFERED_IN_SESSION,"
+        "FILES_TRANSFERED_IN_SESSION = :FILES_TRANSFERED_IN_SESSION,"
+        "SESSION_ELAPSED_TIME = :REPORT_TIME-SESSION_START_TIME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME AND DRIVE_STATUS = 'TRANSFERING'";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":DRIVE_NAME", tapeDriveName);
+    stmt.bindString(":HOST", host);
+    stmt.bindString(":LOGICAL_LIBRARY", logicalLibrary);
+    stmt.bindUint64(":BYTES_TRANSFERED_IN_SESSION", statistics.bytesTransferedInSession);
+    stmt.bindUint64(":FILES_TRANSFERED_IN_SESSION", statistics.filesTransferedInSession);
+    stmt.bindUint64(":REPORT_TIME", statistics.reportTime);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", statistics.lastModificationLog.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", statistics.lastModificationLog.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", statistics.lastModificationLog.time);
+
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      log::LogContext lc(m_log);
+      lc.log(log::DEBUG, "RdbmsCatalogue::updateTapeDriveStatistics(): It didn't update statistics");
+    }
+  } catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsDriveStateCatalogue::updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) {
+  try {
+    const std::string driveStatusStr = common::dataStructures::TapeDrive::stateToString(tapeDrive.driveStatus);
+
+    // Case 1 : Drive status stays the same
+    std::string sql =
+      "UPDATE DRIVE_STATE SET "
+        "HOST = :HOST,"
+        "LOGICAL_LIBRARY = :LOGICAL_LIBRARY,";
+
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Transferring) {
+      sql +=
+        "BYTES_TRANSFERED_IN_SESSION = :BYTES_TRANSFERED_IN_SESSION,"
+        "FILES_TRANSFERED_IN_SESSION = :FILES_TRANSFERED_IN_SESSION,"
+        "SESSION_ELAPSED_TIME = CASE WHEN SESSION_START_TIME IS NULL THEN 0 ELSE :REPORT_TIME - SESSION_START_TIME END,";
+    }
+    sql +=
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME AND DRIVE_STATUS = :DRIVE_STATUS";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql.c_str());
+
+    stmt.bindString(":DRIVE_NAME", tapeDrive.driveName);
+    stmt.bindString(":HOST", tapeDrive.host);
+    stmt.bindString(":LOGICAL_LIBRARY", tapeDrive.logicalLibrary);
+    stmt.bindString(":DRIVE_STATUS", driveStatusStr);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", tapeDrive.lastModificationLog.value().username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", tapeDrive.lastModificationLog.value().host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", tapeDrive.lastModificationLog.value().time);
+
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Transferring) {
+      stmt.bindUint64(":BYTES_TRANSFERED_IN_SESSION", tapeDrive.bytesTransferedInSession.value());
+      stmt.bindUint64(":FILES_TRANSFERED_IN_SESSION", tapeDrive.filesTransferedInSession.value());
+      stmt.bindUint64(":REPORT_TIME", tapeDrive.transferStartTime.value());
+    }
+    stmt.executeNonQuery();
+
+    // If the update succeeded, we are done. Otherwise proceed to Case 2.
+    if (stmt.getNbAffectedRows() > 0) return;
+
+    // Case 2 : Drive status is changing
+    sql =
+      "UPDATE DRIVE_STATE SET "
+        "HOST = :HOST,"
+        "LOGICAL_LIBRARY = :LOGICAL_LIBRARY,"
+        "SESSION_ID = :SESSION_ID,"
+        "BYTES_TRANSFERED_IN_SESSION = :BYTES_TRANSFERED_IN_SESSION,"
+        "FILES_TRANSFERED_IN_SESSION = :FILES_TRANSFERED_IN_SESSION,"
+        "TRANSFER_START_TIME = :TRANSFER_START_TIME,"
+        "SESSION_ELAPSED_TIME = :SESSION_ELAPSED_TIME,"
+        "UNLOAD_START_TIME = :UNLOAD_START_TIME,"
+        "UNMOUNT_START_TIME = :UNMOUNT_START_TIME,"
+        "DRAINING_START_TIME = :DRAINING_START_TIME,"
+        "DOWN_OR_UP_START_TIME = :DOWN_OR_UP_START_TIME,"
+        "PROBE_START_TIME = :PROBE_START_TIME,"
+        "CLEANUP_START_TIME = :CLEANUP_START_TIME,"
+        "SHUTDOWN_TIME = :SHUTDOWN_TIME,"
+        "MOUNT_TYPE = :MOUNT_TYPE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME,";
+
+    if (tapeDrive.driveStatus != common::dataStructures::DriveStatus::Transferring) {
+      if (tapeDrive.driveStatus != common::dataStructures::DriveStatus::Mounting) {
+        sql += "SESSION_START_TIME = :SESSION_START_TIME,";
+      }
+      sql += "MOUNT_START_TIME = :MOUNT_START_TIME,";
+    }
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Starting) {
+      sql += "START_START_TIME = :START_START_TIME,";
+    }
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Down) {
+      sql += "DESIRED_UP = :DESIRED_UP,"
+             "DESIRED_FORCE_DOWN = :DESIRED_FORCE_DOWN,";
+    }
+    // If the drive is a state incompatible with space reservation, make sure there is none:
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Up) {
+      sql += "DISK_SYSTEM_NAME = NULL,";
+      sql += "RESERVED_BYTES = NULL,";
+      sql += "RESERVATION_SESSION_ID = NULL,";
+      sql += "DRIVE_STATUS = CASE WHEN DESIRED_UP = '0' THEN 'DOWN' ELSE 'UP' END,";
+    } else {
+      sql += "DRIVE_STATUS = '" + driveStatusStr + "',";
+    }
+    if (tapeDrive.reasonUpDown) {
+      sql += "REASON_UP_DOWN = :REASON_UP_DOWN,";
+    }
+    if (tapeDrive.currentVid) {
+      sql += "CURRENT_VID = :CURRENT_VID,";
+    }
+    if (tapeDrive.currentActivity) {
+      sql += "CURRENT_ACTIVITY = :CURRENT_ACTIVITY,";
+    }
+    if (tapeDrive.currentTapePool) {
+      sql += "CURRENT_TAPE_POOL = :CURRENT_TAPE_POOL,";
+    }
+    if (tapeDrive.currentVo) {
+      sql += "CURRENT_VO = :CURRENT_VO,";
+    }
+    if (tapeDrive.userComment) {
+      sql += "USER_COMMENT = :USER_COMMENT,";
+    }
+    // Remove last ',' character
+    sql.erase(sql.find_last_of(','), 1);
+    sql +=
+     " WHERE "
+        "DRIVE_NAME = :DRIVE_NAME";
+
+    stmt.reset();
+    stmt = conn.createStmt(sql.c_str());
+
+    auto setOptionalTime = [&stmt](const std::string& sqlField, const std::optional<time_t>& optionalField) {
+      if (optionalField) {
+        stmt.bindUint64(sqlField, optionalField.value());
+      } else {
+        stmt.bindUint64(sqlField, std::nullopt);
+      }
+    };
+    auto bindOptionalStringIfSet = [&stmt](const std::string& sqlField, const std::optional<std::string>& optionalString) {
+      if (optionalString) {
+        if (optionalString.value().empty()) {
+          stmt.bindString(sqlField, std::nullopt);
+        } else {
+          stmt.bindString(sqlField, optionalString.value());
+        }
+      }
+    };
+
+    stmt.bindString(":HOST", tapeDrive.host);
+    stmt.bindString(":LOGICAL_LIBRARY", tapeDrive.logicalLibrary);
+    stmt.bindUint64(":SESSION_ID", tapeDrive.sessionId);
+    stmt.bindUint64(":BYTES_TRANSFERED_IN_SESSION", tapeDrive.bytesTransferedInSession);
+    stmt.bindUint64(":FILES_TRANSFERED_IN_SESSION", tapeDrive.filesTransferedInSession);
+    setOptionalTime(":TRANSFER_START_TIME", tapeDrive.transferStartTime);
+    setOptionalTime(":SESSION_ELAPSED_TIME", tapeDrive.sessionElapsedTime);
+    setOptionalTime(":UNLOAD_START_TIME", tapeDrive.unloadStartTime);
+    setOptionalTime(":UNMOUNT_START_TIME", tapeDrive.unmountStartTime);
+    setOptionalTime(":DRAINING_START_TIME", tapeDrive.drainingStartTime);
+    setOptionalTime(":DOWN_OR_UP_START_TIME", tapeDrive.downOrUpStartTime);
+    setOptionalTime(":PROBE_START_TIME", tapeDrive.probeStartTime);
+    setOptionalTime(":CLEANUP_START_TIME", tapeDrive.cleanupStartTime);
+    setOptionalTime(":SHUTDOWN_TIME", tapeDrive.shutdownTime);
+    stmt.bindString(":MOUNT_TYPE", toString(tapeDrive.mountType));
+    stmt.bindString(":LAST_UPDATE_USER_NAME", tapeDrive.lastModificationLog.value().username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", tapeDrive.lastModificationLog.value().host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", tapeDrive.lastModificationLog.value().time);
+
+    if (tapeDrive.driveStatus != common::dataStructures::DriveStatus::Transferring) {
+      if (tapeDrive.driveStatus != common::dataStructures::DriveStatus::Mounting) {
+        setOptionalTime(":SESSION_START_TIME", tapeDrive.sessionStartTime);
+      }
+      setOptionalTime(":MOUNT_START_TIME", tapeDrive.mountStartTime);
+    }
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Starting) {
+      setOptionalTime(":START_START_TIME", tapeDrive.startStartTime);
+    }
+    if (tapeDrive.driveStatus == common::dataStructures::DriveStatus::Down) {
+      stmt.bindBool(":DESIRED_UP", tapeDrive.desiredUp);
+      stmt.bindBool(":DESIRED_FORCE_DOWN", tapeDrive.desiredForceDown);
+    }
+    bindOptionalStringIfSet(":REASON_UP_DOWN", tapeDrive.reasonUpDown);
+    bindOptionalStringIfSet(":CURRENT_VID", tapeDrive.currentVid);
+    bindOptionalStringIfSet(":CURRENT_ACTIVITY", tapeDrive.currentActivity);
+    bindOptionalStringIfSet(":CURRENT_TAPE_POOL", tapeDrive.currentTapePool);
+    bindOptionalStringIfSet(":CURRENT_VO", tapeDrive.currentVo);
+    bindOptionalStringIfSet(":USER_COMMENT", tapeDrive.userComment);
+    stmt.bindString(":DRIVE_NAME", tapeDrive.driveName);
+
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot update status for drive ") + tapeDrive.driveName + ". Drive not found.");
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getDiskSpaceReservations
+//------------------------------------------------------------------------------
+std::map<std::string, uint64_t> RdbmsDriveStateCatalogue::getDiskSpaceReservations() const {
+  std::map<std::string, uint64_t> ret;
+  const auto tdNames = getTapeDriveNames();
+  for (const auto& driveName : tdNames) {
+    const auto tdStatus = getTapeDrive(driveName);
+    if (tdStatus.value().diskSystemName) {
+      // no need to check key, operator[] initializes missing values at zero for scalar types
+      ret[tdStatus.value().diskSystemName.value()] += tdStatus.value().reservedBytes.value();
+    }
+  }
+  return ret;
+}
+
+//------------------------------------------------------------------------------
+// reserveDiskSpace
+//------------------------------------------------------------------------------
+void RdbmsDriveStateCatalogue::reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
+  if (diskSpaceReservation.empty()) return;
+
+  try {
+    {
+      log::ScopedParamContainer params(lc);
+      params.add("driveName", driveName)
+            .add("diskSystem", diskSpaceReservation.begin()->first)
+            .add("reservationBytes", diskSpaceReservation.begin()->second)
+            .add("mountId", mountId);
+      lc.log(log::DEBUG, "In RetrieveMount::reserveDiskSpace(): reservation request.");
+    }
+
+    // Normally the disk system name will not change. It can change in some rare circumstances, e.g. the tape server is
+    // assigned to a new VO.
+    //
+    // The disk system name is allowed to be updated when RESERVED_BYTES is zero (initial disk reservation, or previous
+    // disk reservations have been released). Otherwise, to update RESERVED_BYTES, the disk system name has to match.
+    const char* const sql =
+      "UPDATE DRIVE_STATE SET "
+        "RESERVED_BYTES = RESERVED_BYTES + :BYTES_TO_ADD "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME "
+        "AND DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME "
+        "AND RESERVATION_SESSION_ID = :RESERVATION_SESSION_ID ";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DRIVE_NAME", driveName);
+    stmt.bindString(":DISK_SYSTEM_NAME", diskSpaceReservation.begin()->first);
+    stmt.bindUint64(":BYTES_TO_ADD", diskSpaceReservation.begin()->second);
+    stmt.bindUint64(":RESERVATION_SESSION_ID", mountId);
+    stmt.executeNonQuery();
+
+    // If the reservation does not match the <driveName, diskSystem> pair in the DRIVE_STATE table,
+    // log an error and drop the previous reservation
+    if (stmt.getNbAffectedRows() != 1) {
+      {
+        log::ScopedParamContainer params(lc);
+        params.add("driveName", driveName)
+              .add("diskSystem", diskSpaceReservation.begin()->first)
+              .add("reservationBytes", diskSpaceReservation.begin()->second)
+              .add("mountId", mountId);
+        lc.log(log::INFO, "In RetrieveMount::releaseDiskSpace(): creating reservation for new mount");
+      }
+      const char* const sql_reset =
+        "UPDATE DRIVE_STATE SET "
+          "DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME,"
+          "RESERVED_BYTES = :BYTES_TO_ADD,"
+          "RESERVATION_SESSION_ID = :RESERVATION_SESSION_ID "
+        "WHERE "
+          "DRIVE_NAME = :DRIVE_NAME";
+      stmt.reset();
+      stmt = conn.createStmt(sql_reset);
+      stmt.bindString(":DRIVE_NAME", driveName);
+      stmt.bindString(":DISK_SYSTEM_NAME", diskSpaceReservation.begin()->first);
+      stmt.bindUint64(":BYTES_TO_ADD", diskSpaceReservation.begin()->second);
+      stmt.bindUint64(":RESERVATION_SESSION_ID", mountId);
+      stmt.executeNonQuery();
+      if (stmt.getNbAffectedRows() != 1) {
+        log::ScopedParamContainer params(lc);
+        params.add("driveName", driveName)
+              .add("diskSystem", diskSpaceReservation.begin()->first)
+              .add("reservationBytes", diskSpaceReservation.begin()->second)
+              .add("mountId", mountId);
+        lc.log(log::ERR, "In RetrieveMount::releaseDiskSpace(): failed to create disk reservation for new mount.");
+      }
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// releaseDiskSpace
+//------------------------------------------------------------------------------
+void RdbmsDriveStateCatalogue::releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
+  if (diskSpaceReservation.empty()) return;
+
+  try {
+    {
+      log::ScopedParamContainer params(lc);
+      params.add("driveName", driveName)
+            .add("diskSystem", diskSpaceReservation.begin()->first)
+            .add("reservationBytes", diskSpaceReservation.begin()->second)
+            .add("mountId", mountId);
+      lc.log(log::DEBUG, "In RetrieveMount::releaseDiskSpace(): reservation release request.");
+    }
+
+    // If the amount being released exceeds the amount of the reservation, set the reservation to zero
+    const char* const sql =
+      "UPDATE DRIVE_STATE SET "
+        "RESERVED_BYTES = CASE WHEN RESERVED_BYTES > :BYTES_TO_SUBTRACT1 THEN RESERVED_BYTES-:BYTES_TO_SUBTRACT2 ELSE 0 END "
+      "WHERE "
+        "DRIVE_NAME = :DRIVE_NAME "
+        "AND DISK_SYSTEM_NAME = :DISK_SYSTEM_NAME "
+        "AND RESERVATION_SESSION_ID = :RESERVATION_SESSION_ID";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DRIVE_NAME", driveName);
+    stmt.bindString(":DISK_SYSTEM_NAME", diskSpaceReservation.begin()->first);
+    stmt.bindUint64(":BYTES_TO_SUBTRACT1", diskSpaceReservation.begin()->second);
+    stmt.bindUint64(":BYTES_TO_SUBTRACT2", diskSpaceReservation.begin()->second);
+    stmt.bindUint64(":RESERVATION_SESSION_ID", mountId);
+    stmt.executeNonQuery();
+    if (stmt.getNbAffectedRows() != 1) {
+      // If the reservation does not match the <driveName, diskSystem> pair in the DRIVE_STATE table, log an error and carry on
+      log::ScopedParamContainer params(lc);
+      params.add("driveName", driveName)
+            .add("diskSystem", diskSpaceReservation.begin()->first)
+            .add("reservationBytes", diskSpaceReservation.begin()->second)
+            .add("mountId", mountId);
+      lc.log(log::ERR, "In RetrieveMount::releaseDiskSpace(): reservation release request failed, driveName, diskSystem and mountId do not match.");
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsDriveStateCatalogue.hpp b/catalogue/rdbms/RdbmsDriveStateCatalogue.hpp
new file mode 100644
index 0000000000..697b8dbc08
--- /dev/null
+++ b/catalogue/rdbms/RdbmsDriveStateCatalogue.hpp
@@ -0,0 +1,92 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/interfaces/DriveStateCatalogue.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+
+// namespace log {
+// class Logger;
+// }
+
+namespace rdbms {
+class ConnPool;
+class Login;
+class Rset;
+class Stmt;
+}
+
+namespace catalogue {
+
+class RdbmsDriveStateCatalogue : public DriveStateCatalogue {
+public:
+  RdbmsDriveStateCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsDriveStateCatalogue() override = default;
+
+  void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) override;
+
+  std::list<std::string> getTapeDriveNames() const override;
+
+  std::list<common::dataStructures::TapeDrive> getTapeDrives() const override;
+
+  std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const override;
+
+  void setDesiredTapeDriveState(const std::string& tapeDriveName,
+      const common::dataStructures::DesiredDriveState &desiredState) override;
+
+  void setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+    const std::string &comment) override;
+
+  void updateTapeDriveStatistics(const std::string& tapeDriveName,
+    const std::string& host, const std::string& logicalLibrary,
+    const common::dataStructures::TapeDriveStatistics& statistics) override;
+
+  void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) override;
+
+  void deleteTapeDrive(const std::string &tapeDriveName) override;
+
+  std::map<std::string, uint64_t> getDiskSpaceReservations() const override;
+
+  void reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
+
+  void releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
+
+private:
+  log::Logger &m_log;
+
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+
+  void settingSqlTapeDriveValues(cta::rdbms::Stmt *stmt, const common::dataStructures::TapeDrive &tapeDrive) const;
+
+  common::dataStructures::TapeDrive gettingSqlTapeDriveValues(cta::rdbms::Rset* rset) const;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsFileRecycleLogCatalogue.cpp b/catalogue/rdbms/RdbmsFileRecycleLogCatalogue.cpp
new file mode 100644
index 0000000000..8da3d6387e
--- /dev/null
+++ b/catalogue/rdbms/RdbmsFileRecycleLogCatalogue.cpp
@@ -0,0 +1,389 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "catalogue/ArchiveFileRow.hpp"
+#include "catalogue/ArchiveFileRowWithoutTimestamps.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/InsertFileRecycleLog.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueGetFileRecycleLogItor.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsFileRecycleLogCatalogue::RdbmsFileRecycleLogCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {
+}
+
+FileRecycleLogItor RdbmsFileRecycleLogCatalogue::getFileRecycleLogItor(
+  const RecycleTapeFileSearchCriteria & searchCriteria) const {
+  try {
+    auto conn = m_rdbmsCatalogue->m_archiveFileListingConnPool->getConn();
+    checkRecycleTapeFileSearchCriteria(conn, searchCriteria);
+    const auto tempDiskFxidsTableName = m_rdbmsCatalogue->createAndPopulateTempTableFxid(
+      conn, searchCriteria.diskFileIds);
+    auto impl = new RdbmsCatalogueGetFileRecycleLogItor(m_log, std::move(conn), searchCriteria, tempDiskFxidsTableName);
+    return FileRecycleLogItor(impl);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+  const std::string &newFid) {
+  try {
+    auto fileRecycleLogitor = getFileRecycleLogItor(searchCriteria);
+    auto conn = m_connPool->getConn();
+    log::LogContext lc(m_log);
+    restoreEntryInRecycleLog(conn, fileRecycleLogitor, newFid, lc);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::restoreArchiveFileInRecycleLog(rdbms::Conn &conn,
+  const cta::common::dataStructures::FileRecycleLog &fileRecycleLog, const std::string &newFid, log::LogContext & lc) {
+  cta::catalogue::ArchiveFileRowWithoutTimestamps row;
+  row.diskFileId = newFid;
+  row.archiveFileId = fileRecycleLog.archiveFileId;
+  row.checksumBlob = fileRecycleLog.checksumBlob;
+  row.diskFileOwnerUid = fileRecycleLog.diskFileUid;
+  row.diskFileGid = fileRecycleLog.diskFileGid;
+  row.diskInstance = fileRecycleLog.diskInstanceName;
+  row.size = fileRecycleLog.sizeInBytes;
+  row.storageClassName = fileRecycleLog.storageClassName;
+  static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get())->insertArchiveFile(conn, row);
+}
+
+void RdbmsFileRecycleLogCatalogue::checkRecycleTapeFileSearchCriteria(cta::rdbms::Conn &conn,
+  const RecycleTapeFileSearchCriteria & searchCriteria) const {
+  if (searchCriteria.vid) {
+    if (!RdbmsCatalogueUtils::tapeExists(conn, searchCriteria.vid.value())) {
+      throw exception::UserError(std::string("Tape ") + searchCriteria.vid.value() + " does not exist");
+    }
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::restoreFileCopyInRecycleLog(rdbms::Conn & conn,
+  const common::dataStructures::FileRecycleLog &fileRecycleLog, log::LogContext & lc) const {
+  try {
+    utils::Timer timer;
+    log::TimingList timingList;
+    cta::common::dataStructures::TapeFile tapeFile;
+    tapeFile.vid = fileRecycleLog.vid;
+    tapeFile.fSeq = fileRecycleLog.fSeq;
+    tapeFile.copyNb = fileRecycleLog.copyNb;
+    tapeFile.blockId = fileRecycleLog.blockId;
+    tapeFile.fileSize = fileRecycleLog.sizeInBytes;
+    tapeFile.creationTime = fileRecycleLog.tapeFileCreationTime;
+
+    static_cast<RdbmsTapeFileCatalogue*>(m_rdbmsCatalogue->TapeFile().get())->insertTapeFile(conn, tapeFile,
+      fileRecycleLog.archiveFileId);
+    timingList.insertAndReset("insertTapeFileTime", timer);
+
+    deleteTapeFileCopyFromRecycleBin(conn, fileRecycleLog);
+    timingList.insertAndReset("deleteTapeFileCopyFromRecycleBinTime", timer);
+
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", tapeFile.vid);
+    spc.add("archiveFileId", fileRecycleLog.archiveFileId);
+    spc.add("fSeq", tapeFile.fSeq);
+    spc.add("copyNb", tapeFile.copyNb);
+    spc.add("fileSize", tapeFile.fileSize);
+    timingList.addToLog(spc);
+    lc.log(log::INFO, "In RdbmsFileRecycleLogCatalogue::restoreFileCopyInRecycleLog: "
+      "File restored from the recycle log.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::copyTapeFilesToFileRecycleLog(rdbms::Conn & conn,
+  const common::dataStructures::ArchiveFile &archiveFile, const std::string &reason) const {
+  try {
+    for(auto &tapeFile: archiveFile.tapeFiles) {
+      //Create one file recycle log entry per tape file
+      InsertFileRecycleLog fileRecycleLog;
+      fileRecycleLog.vid = tapeFile.vid;
+      fileRecycleLog.fSeq = tapeFile.fSeq;
+      fileRecycleLog.blockId = tapeFile.blockId;
+      fileRecycleLog.copyNb = tapeFile.copyNb;
+      fileRecycleLog.tapeFileCreationTime = tapeFile.creationTime;
+      fileRecycleLog.archiveFileId = archiveFile.archiveFileID;
+      fileRecycleLog.diskFilePath = archiveFile.diskFileInfo.path;
+      fileRecycleLog.reasonLog = "(Deleted using cta-admin tf rm) " + reason;
+      fileRecycleLog.recycleLogTime = time(nullptr);
+      insertFileInFileRecycleLog(conn, fileRecycleLog);
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) {
+  try {
+    auto conn = m_connPool->getConn();
+    deleteFilesFromRecycleLog(conn,vid,lc);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::deleteFilesFromRecycleLog(rdbms::Conn & conn, const std::string& vid, log::LogContext& lc) {
+  try {
+    const char *const deleteFilesFromRecycleLogSql =
+    "DELETE FROM "
+      "FILE_RECYCLE_LOG "
+    "WHERE "
+      "VID=:VID";
+
+    cta::utils::Timer t;
+    log::TimingList tl;
+    auto selectFileStmt = conn.createStmt(deleteFilesFromRecycleLogSql);
+    selectFileStmt.bindString(":VID",vid);
+    selectFileStmt.executeNonQuery();
+    uint64_t nbAffectedRows = selectFileStmt.getNbAffectedRows();
+    if(nbAffectedRows){
+      tl.insertAndReset("deleteFilesFromRecycleLogTime",t);
+      log::ScopedParamContainer spc(lc);
+      spc.add("vid",vid);
+      spc.add("nbFileRecycleLogDeleted",nbAffectedRows);
+      tl.addToLog(spc);
+      lc.log(cta::log::INFO,"In RdbmsCatalogue::deleteFilesFromRecycleLog(), file recycle log entries have been deleted.");
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+//------------------------------------------------------------------------------
+// deleteTapeFileCopyFromRecycleBin
+//------------------------------------------------------------------------------
+void RdbmsFileRecycleLogCatalogue::deleteTapeFileCopyFromRecycleBin(cta::rdbms::Conn & conn,
+  const common::dataStructures::FileRecycleLog fileRecycleLog) const {
+  try {
+    const char *const deleteTapeFilesSql =
+    "DELETE FROM "
+      "FILE_RECYCLE_LOG "
+    "WHERE FILE_RECYCLE_LOG.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID AND FILE_RECYCLE_LOG.VID = :VID AND "
+    "FILE_RECYCLE_LOG.FSEQ = :FSEQ AND FILE_RECYCLE_LOG.COPY_NB = :COPY_NB AND "
+    "FILE_RECYCLE_LOG.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME";
+
+    auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
+    deleteTapeFilesStmt.bindUint64(":ARCHIVE_FILE_ID", fileRecycleLog.archiveFileId);
+    deleteTapeFilesStmt.bindString(":VID", fileRecycleLog.vid);
+    deleteTapeFilesStmt.bindUint64(":FSEQ", fileRecycleLog.fSeq);
+    deleteTapeFilesStmt.bindUint64(":COPY_NB", fileRecycleLog.copyNb);
+    deleteTapeFilesStmt.bindString(":DISK_INSTANCE_NAME", fileRecycleLog.diskInstanceName);
+    deleteTapeFilesStmt.executeNonQuery();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsFileRecycleLogCatalogue::insertFileInFileRecycleLog(rdbms::Conn& conn,
+  const InsertFileRecycleLog& fileRecycleLog) const {
+  try{
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(fileRecycleLog.reasonLog, &m_log);
+    uint64_t fileRecycleLogId = getNextFileRecyleLogId(conn);
+    const char *const sql =
+    "INSERT INTO FILE_RECYCLE_LOG("
+      "FILE_RECYCLE_LOG_ID,"
+      "VID,"
+      "FSEQ,"
+      "BLOCK_ID,"
+      "COPY_NB,"
+      "TAPE_FILE_CREATION_TIME,"
+      "ARCHIVE_FILE_ID,"
+      "DISK_INSTANCE_NAME,"
+      "DISK_FILE_ID,"
+      "DISK_FILE_ID_WHEN_DELETED,"
+      "DISK_FILE_UID,"
+      "DISK_FILE_GID,"
+      "SIZE_IN_BYTES,"
+      "CHECKSUM_BLOB,"
+      "CHECKSUM_ADLER32,"
+      "STORAGE_CLASS_ID,"
+      "ARCHIVE_FILE_CREATION_TIME,"
+      "RECONCILIATION_TIME,"
+      "COLLOCATION_HINT,"
+      "DISK_FILE_PATH,"
+      "REASON_LOG,"
+      "RECYCLE_LOG_TIME"
+    ") SELECT "
+      ":FILE_RECYCLE_LOG_ID,"
+      ":VID,"
+      ":FSEQ,"
+      ":BLOCK_ID,"
+      ":COPY_NB,"
+      ":TAPE_FILE_CREATION_TIME,"
+      ":ARCHIVE_FILE_ID,"
+      "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+      "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+      "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID_2,"
+      "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
+      "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
+      "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+      "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+      "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
+      "ARCHIVE_FILE.STORAGE_CLASS_ID AS STORAGE_CLASS_ID,"
+      "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
+      "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
+      "ARCHIVE_FILE.COLLOCATION_HINT AS COLLOCATION_HINT,"
+      ":DISK_FILE_PATH,"
+      ":REASON_LOG,"
+      ":RECYCLE_LOG_TIME "
+    "FROM "
+      "ARCHIVE_FILE "
+    "WHERE "
+      "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID_2";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":FILE_RECYCLE_LOG_ID",fileRecycleLogId);
+    stmt.bindString(":VID",fileRecycleLog.vid);
+    stmt.bindUint64(":FSEQ",fileRecycleLog.fSeq);
+    stmt.bindUint64(":BLOCK_ID",fileRecycleLog.blockId);
+    stmt.bindUint8(":COPY_NB",fileRecycleLog.copyNb);
+    stmt.bindUint64(":TAPE_FILE_CREATION_TIME",fileRecycleLog.tapeFileCreationTime);
+    stmt.bindString(":DISK_FILE_PATH",fileRecycleLog.diskFilePath);
+    stmt.bindUint64(":ARCHIVE_FILE_ID",fileRecycleLog.archiveFileId);
+    stmt.bindString(":REASON_LOG",fileRecycleLog.reasonLog);
+    stmt.bindUint64(":RECYCLE_LOG_TIME",fileRecycleLog.recycleLogTime);
+    stmt.bindUint64(":ARCHIVE_FILE_ID_2",fileRecycleLog.archiveFileId);
+    stmt.executeNonQuery();
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// copyArchiveFileToFileRecycleLog
+//------------------------------------------------------------------------------
+void RdbmsFileRecycleLogCatalogue::copyArchiveFileToFileRecycleLog(rdbms::Conn & conn,
+  const common::dataStructures::DeleteArchiveRequest & request) {
+  try{
+    if(!request.archiveFile){
+      throw cta::exception::Exception("No archiveFile object has been set in the DeleteArchiveRequest object.");
+    }
+    const common::dataStructures::ArchiveFile & archiveFile = request.archiveFile.value();
+
+    for(auto &tapeFile: archiveFile.tapeFiles){
+      //Create one file recycle log entry per tape file
+      InsertFileRecycleLog fileRecycleLog;
+      fileRecycleLog.vid = tapeFile.vid;
+      fileRecycleLog.fSeq = tapeFile.fSeq;
+      fileRecycleLog.blockId = tapeFile.blockId;
+      fileRecycleLog.copyNb = tapeFile.copyNb;
+      fileRecycleLog.tapeFileCreationTime = tapeFile.creationTime;
+      fileRecycleLog.archiveFileId = archiveFile.archiveFileID;
+      fileRecycleLog.diskFilePath = request.diskFilePath;
+      fileRecycleLog.reasonLog = InsertFileRecycleLog::getDeletionReasonLog(request.requester.name,request.diskInstance);
+      fileRecycleLog.recycleLogTime = time(nullptr);
+      insertFileInFileRecycleLog(conn,fileRecycleLog);
+    }
+
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<InsertFileRecycleLog> RdbmsFileRecycleLogCatalogue::insertOldCopiesOfFilesIfAnyOnFileRecycleLog(
+  rdbms::Conn & conn,const common::dataStructures::TapeFile & tapefile, const uint64_t archiveFileId){
+  std::list<InsertFileRecycleLog> fileRecycleLogsToInsert;
+  try {
+    //First, get the file to insert on the FILE_RECYCLE_LOG table
+    {
+      const char *const sql =
+      "SELECT "
+        "TAPE_FILE.VID AS VID,"
+        "TAPE_FILE.FSEQ AS FSEQ,"
+        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
+        "TAPE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
+      "FROM "
+        "TAPE_FILE "
+      "WHERE "
+        "TAPE_FILE.COPY_NB=:COPY_NB AND TAPE_FILE.ARCHIVE_FILE_ID=:ARCHIVE_FILE_ID AND (TAPE_FILE.VID<>:VID OR TAPE_FILE.FSEQ<>:FSEQ)";
+
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint8(":COPY_NB",tapefile.copyNb);
+      stmt.bindUint64(":ARCHIVE_FILE_ID",archiveFileId);
+      stmt.bindString(":VID",tapefile.vid);
+      stmt.bindUint64(":FSEQ",tapefile.fSeq);
+
+      auto rset = stmt.executeQuery();
+      while(rset.next()){
+        cta::catalogue::InsertFileRecycleLog fileRecycleLog;
+        fileRecycleLog.vid = rset.columnString("VID");
+        fileRecycleLog.fSeq = rset.columnUint64("FSEQ");
+        fileRecycleLog.blockId = rset.columnUint64("BLOCK_ID");
+        fileRecycleLog.copyNb = rset.columnUint8("COPY_NB");
+        fileRecycleLog.tapeFileCreationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
+        fileRecycleLog.archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
+        fileRecycleLog.reasonLog = InsertFileRecycleLog::getRepackReasonLog();
+        fileRecycleLog.recycleLogTime = time(nullptr);
+        fileRecycleLogsToInsert.push_back(fileRecycleLog);
+      }
+    }
+    {
+      for(auto & fileRecycleLog: fileRecycleLogsToInsert){
+        insertFileInFileRecycleLog(conn, fileRecycleLog);
+      }
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+  return fileRecycleLogsToInsert;
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp b/catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp
new file mode 100644
index 0000000000..260019f9a7
--- /dev/null
+++ b/catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp
@@ -0,0 +1,178 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct ArchiveFile;
+struct DeleteArchiveRequest;
+struct TapeFile;
+}  // namespace dataStructures
+}  // namespace common
+
+namespace log {
+class TimingList;
+}
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace utils {
+class Timer;
+}
+
+namespace catalogue {
+
+class InsertFileRecycleLog;
+class RdbmsCatalogue;
+
+class RdbmsFileRecycleLogCatalogue : public FileRecycleLogCatalogue {
+public:
+  ~RdbmsFileRecycleLogCatalogue() override = default;
+
+  FileRecycleLogItor getFileRecycleLogItor(
+    const RecycleTapeFileSearchCriteria & searchCriteria = RecycleTapeFileSearchCriteria()) const override;
+
+  void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+    const std::string &newFid) override;
+
+  void deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) override;
+
+protected:
+  RdbmsFileRecycleLogCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  /**
+   * Copy the fileRecycleLog to the ARCHIVE_FILE with a new eos fxid
+   * @param conn the database connection
+   * @param fileRecycleLog the fileRecycleLog we want to restore
+   * @param newFid the new eos file id of the archive file
+   * @param lc the log context
+   */
+  void restoreArchiveFileInRecycleLog(rdbms::Conn &conn,
+    const cta::common::dataStructures::FileRecycleLog &fileRecycleLog,
+    const std::string &newFid, log::LogContext & lc);
+
+  // TapeFile
+  friend class OracleTapeFileCatalogue;
+  friend class PostgresTapeFileCatalogue;
+  friend class SqliteTapeFileCatalogue;
+  // ArchiveFile
+  friend class OracleArchiveFileCatalogue;
+  friend class PostgresArchiveFileCatalogue;
+  friend class SqliteArchiveFileCatalogue;
+  /**
+   * Copies the TAPE_FILE entries to the recycle-bin tables
+   * @param conn the database connection
+   * @param file the archiveFile whose tapefiles we want to copy
+   * @param reason The reason for deleting the tape file copy
+   */
+  void copyTapeFilesToFileRecycleLog(rdbms::Conn & conn, const common::dataStructures::ArchiveFile &file,
+    const std::string &reason) const;
+
+  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLog,
+    log::LogContext & lc) const;
+
+
+  /**
+   * Copy the files in fileRecycleLogItor to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entries
+   * @param conn the database connection
+   * @param fileRecycleLogItor the collection of fileRecycleLogs we want to restore
+   * @param lc the log context
+   */
+  virtual void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor,
+    const std::string &newFid, log::LogContext & lc) = 0;
+
+  friend class RdbmsTapeFileCatalogue;
+
+  /**
+   * Returns a unique file recycle log ID that can be used by a new entry of file recycle log within
+   * the catalogue.
+   *
+   * This method must be implemented by the sub-classes of RdbmsCatalogue
+   * because different database technologies propose different solution to the
+   * problem of generating ever increasing numeric identifiers.
+   *
+   * @param conn The database connection.
+   * @return a unique file recycle log ID that can be used by a new entry of file recycle log within
+   * the catalogue.
+   */
+  virtual uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) const = 0;
+
+protected:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+private:
+  /**
+   * Throws a UserError exception if the specified searchCriteria is not valid
+   * due to a user error.
+   * @param conn The database connection.
+   * @param searchCriteria The search criteria.
+   */
+  void checkRecycleTapeFileSearchCriteria(cta::rdbms::Conn &conn,
+    const RecycleTapeFileSearchCriteria & searchCriteria) const;
+
+  void deleteTapeFileCopyFromRecycleBin(cta::rdbms::Conn & conn,
+    const common::dataStructures::FileRecycleLog fileRecycleLog) const;
+
+  friend class RdbmsTapeCatalogue;
+  void deleteFilesFromRecycleLog(rdbms::Conn & conn, const std::string& vid, log::LogContext& lc);
+
+  /**
+   * Insert the file passed in parameter in the FILE_RECYCLE_LOG table
+   * @param conn the database connection
+   * @param fileRecycleLog the file to insert on the FILE_RECYCLE_LOG table
+   */
+  void insertFileInFileRecycleLog(rdbms::Conn & conn, const InsertFileRecycleLog & fileRecycleLog) const;
+
+  /**
+   * Copies the ARCHIVE_FILE and TAPE_FILE entries to the recycle-bin tables
+   * @param conn the database connection
+   * @param request the request that contains the necessary informations to identify the archiveFile to copy to the recycle-bin
+   */
+  void copyArchiveFileToFileRecycleLog(rdbms::Conn & conn,
+    const common::dataStructures::DeleteArchiveRequest & request);
+
+  /**
+   * In the case we insert a TAPE_FILE that already has a copy on the catalogue (same copyNb),
+   * this TAPE_FILE will go to the FILE_RECYCLE_LOG table.
+   *
+   * This case happens always during the repacking of a tape: the new TAPE_FILE created
+   * will replace the old one, the old one will then be moved to the FILE_RECYCLE_LOG table
+   *
+   * @param conn The database connection.
+   * @returns the list of inserted fileRecycleLog
+   */
+  std::list<cta::catalogue::InsertFileRecycleLog> insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn & conn,
+    const common::dataStructures::TapeFile &tapeFile, const uint64_t archiveFileId);
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsLogicalLibraryCatalogue.cpp b/catalogue/rdbms/RdbmsLogicalLibraryCatalogue.cpp
new file mode 100644
index 0000000000..8be68dea41
--- /dev/null
+++ b/catalogue/rdbms/RdbmsLogicalLibraryCatalogue.cpp
@@ -0,0 +1,361 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp"
+#include "common/dataStructures/LogicalLibrary.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/log/Logger.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsLogicalLibraryCatalogue::RdbmsLogicalLibraryCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsLogicalLibraryCatalogue::createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool isDisabled, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::logicalLibraryExists(conn, name)) {
+      throw exception::UserError(std::string("Cannot create logical library ") + name +
+        " because a logical library with the same name already exists");
+    }
+    const uint64_t logicalLibraryId = getNextLogicalLibraryId(conn);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO LOGICAL_LIBRARY("
+        "LOGICAL_LIBRARY_ID,"
+        "LOGICAL_LIBRARY_NAME,"
+        "IS_DISABLED,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":LOGICAL_LIBRARY_ID,"
+        ":LOGICAL_LIBRARY_NAME,"
+        ":IS_DISABLED,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindUint64(":LOGICAL_LIBRARY_ID", logicalLibraryId);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
+    stmt.bindBool(":IS_DISABLED", isDisabled);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsLogicalLibraryCatalogue::deleteLogicalLibrary(const std::string &name) {
+  try {
+    const char *const sql =
+      "DELETE FROM LOGICAL_LIBRARY "
+      "WHERE "
+        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME AND "
+        "NOT EXISTS ("
+          "SELECT "
+            "TAPE.LOGICAL_LIBRARY_ID "
+          "FROM "
+            "TAPE "
+          "WHERE "
+            "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID)";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
+    stmt.executeNonQuery();
+
+    // The delete statement will effect no rows and will not raise an error if
+    // either the logical library does not exist or if it still contains tapes
+    if(0 == stmt.getNbAffectedRows()) {
+      if(RdbmsCatalogueUtils::logicalLibraryExists(conn, name)) {
+        throw UserSpecifiedANonEmptyLogicalLibrary(std::string("Cannot delete logical library ") + name +
+          " because it contains one or more tapes");
+      } else {
+        throw UserSpecifiedANonExistentLogicalLibrary(std::string("Cannot delete logical library ") + name +
+          " because it does not exist");
+      }
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::LogicalLibrary> RdbmsLogicalLibraryCatalogue::getLogicalLibraries() const {
+  try {
+    std::list<common::dataStructures::LogicalLibrary> libs;
+    const char *const sql =
+      "SELECT "
+        "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
+        "IS_DISABLED AS IS_DISABLED,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+        "DISABLED_REASON AS DISABLED_REASON,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "LOGICAL_LIBRARY "
+      "ORDER BY "
+        "LOGICAL_LIBRARY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::LogicalLibrary lib;
+
+      lib.name = rset.columnString("LOGICAL_LIBRARY_NAME");
+      lib.isDisabled = rset.columnBool("IS_DISABLED");
+      lib.comment = rset.columnString("USER_COMMENT");
+      lib.disabledReason = rset.columnOptionalString("DISABLED_REASON");
+      lib.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      lib.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      lib.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      lib.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      lib.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      lib.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      libs.push_back(lib);
+    }
+
+    return libs;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsLogicalLibraryCatalogue::modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  try {
+    if(currentName.empty()) {
+      throw UserSpecifiedAnEmptyStringLogicalLibraryName(
+        "Cannot modify logical library because the logical library name is an empty string");
+    }
+
+    if(newName.empty()) {
+      throw UserSpecifiedAnEmptyStringLogicalLibraryName(
+        "Cannot modify logical library because the new name is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE LOGICAL_LIBRARY SET "
+        "LOGICAL_LIBRARY_NAME = :NEW_LOGICAL_LIBRARY_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "LOGICAL_LIBRARY_NAME = :CURRENT_LOGICAL_LIBRARY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":NEW_LOGICAL_LIBRARY_NAME", newName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":CURRENT_LOGICAL_LIBRARY_NAME", currentName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify logical library ") + currentName
+        + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsLogicalLibraryCatalogue::modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE LOGICAL_LIBRARY SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsLogicalLibraryCatalogue::modifyLogicalLibraryDisabledReason(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &disabledReason) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(disabledReason, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE LOGICAL_LIBRARY SET "
+        "DISABLED_REASON = :DISABLED_REASON,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISABLED_REASON",
+      disabledReason.empty() ? std::nullopt : std::optional<std::string>(disabledReason));
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsLogicalLibraryCatalogue::setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool disabledValue) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE LOGICAL_LIBRARY SET "
+        "IS_DISABLED = :IS_DISABLED,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindBool(":IS_DISABLED", disabledValue);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<uint64_t> RdbmsLogicalLibraryCatalogue::getLogicalLibraryId(rdbms::Conn &conn,
+  const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LOGICAL_LIBRARY_ID AS LOGICAL_LIBRARY_ID "
+      "FROM "
+        "LOGICAL_LIBRARY "
+      "WHERE "
+        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", name);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      return std::nullopt;
+    }
+    return rset.columnUint64("LOGICAL_LIBRARY_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp b/catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp
new file mode 100644
index 0000000000..feec140f93
--- /dev/null
+++ b/catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp
@@ -0,0 +1,93 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "catalogue/interfaces/LogicalLibraryCatalogue.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsLogicalLibraryCatalogue: public LogicalLibraryCatalogue {
+public:
+  ~RdbmsLogicalLibraryCatalogue() override = default;
+
+  void createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool isDisabled, const std::string &comment) override;
+
+  void deleteLogicalLibrary(const std::string &name) override;
+
+  std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const override;
+
+  void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) override;
+
+  void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &disabledReason) override;
+
+  void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool disabledValue) override;
+
+protected:
+  RdbmsLogicalLibraryCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  /**
+   * Returns a unique logical library ID that can be used by a new logical
+   * library within the catalogue.
+   *
+   * This method must be implemented by the sub-classes of RdbmsCatalogue
+   * because different database technologies propose different solution to the
+   * problem of generating ever increasing numeric identifiers.
+   *
+   * @param conn The database connection.
+   * @return a unique logical library ID that can be used by a new logical
+   * library storage class within the catalogue.
+   */
+  virtual uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) const = 0;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue *m_rdbmsCatalogue;
+
+  friend class RdbmsTapeCatalogue;
+  std::optional<uint64_t> getLogicalLibraryId(rdbms::Conn &conn, const std::string &name) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsMediaTypeCatalogue.cpp b/catalogue/rdbms/RdbmsMediaTypeCatalogue.cpp
new file mode 100644
index 0000000000..55fe09a493
--- /dev/null
+++ b/catalogue/rdbms/RdbmsMediaTypeCatalogue.cpp
@@ -0,0 +1,624 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsMediaTypeCatalogue::RdbmsMediaTypeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsMediaTypeCatalogue::createMediaType(const common::dataStructures::SecurityIdentity &admin,
+  const MediaType &mediaType) {
+  try {
+    if (mediaType.name.empty()) {
+      throw UserSpecifiedAnEmptyStringMediaTypeName("Cannot create media type because the media type name is an"
+        " empty string");
+    }
+
+    if (mediaType.cartridge.empty()) {
+      throw UserSpecifiedAnEmptyStringCartridge(std::string("Cannot create media type ") + mediaType.name +
+        " because the cartridge is an empty string");
+    }
+
+    if (mediaType.comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment(std::string("Cannot create media type ") + mediaType.name +
+        " because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(mediaType.comment, &m_log);
+    if (mediaType.capacityInBytes == 0){
+      throw UserSpecifiedAZeroCapacity(std::string("Cannot create media type ") + mediaType.name
+        + " because the capacity is zero");
+    }
+    auto conn = m_connPool->getConn();
+    if (RdbmsCatalogueUtils::mediaTypeExists(conn, mediaType.name)) {
+      throw exception::UserError(std::string("Cannot create media type ") + mediaType.name +
+        " because it already exists");
+    }
+    const uint64_t mediaTypeId = getNextMediaTypeId(conn);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO MEDIA_TYPE("
+        "MEDIA_TYPE_ID,"
+        "MEDIA_TYPE_NAME,"
+        "CARTRIDGE,"
+        "CAPACITY_IN_BYTES,"
+        "PRIMARY_DENSITY_CODE,"
+        "SECONDARY_DENSITY_CODE,"
+        "NB_WRAPS,"
+        "MIN_LPOS,"
+        "MAX_LPOS,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":MEDIA_TYPE_ID,"
+        ":MEDIA_TYPE_NAME,"
+        ":CARTRIDGE,"
+        ":CAPACITY_IN_BYTES,"
+        ":PRIMARY_DENSITY_CODE,"
+        ":SECONDARY_DENSITY_CODE,"
+        ":NB_WRAPS,"
+        ":MIN_LPOS,"
+        ":MAX_LPOS,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindUint64(":MEDIA_TYPE_ID", mediaTypeId);
+    stmt.bindString(":MEDIA_TYPE_NAME", mediaType.name);
+    stmt.bindString(":CARTRIDGE", mediaType.cartridge);
+    stmt.bindUint64(":CAPACITY_IN_BYTES", mediaType.capacityInBytes);
+    stmt.bindUint8(":PRIMARY_DENSITY_CODE", mediaType.primaryDensityCode);
+    stmt.bindUint8(":SECONDARY_DENSITY_CODE", mediaType.secondaryDensityCode);
+    stmt.bindUint32(":NB_WRAPS", mediaType.nbWraps);
+    stmt.bindUint64(":MIN_LPOS", mediaType.minLPos);
+    stmt.bindUint64(":MAX_LPOS", mediaType.maxLPos);
+
+    stmt.bindString(":USER_COMMENT", mediaType.comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::deleteMediaType(const std::string &name) {
+  try {
+    auto conn = m_connPool->getConn();
+
+    if(mediaTypeIsUsedByTapes(conn, name)) {
+      throw UserSpecifiedMediaTypeUsedByTapes(std::string("The ") + name +
+        " media type is being used by one or more tapes");
+    }
+
+    const char *const sql =
+      "DELETE FROM"                          "\n"
+        "MEDIA_TYPE"                         "\n"
+      "WHERE"                                "\n"
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+
+    stmt.executeNonQuery();
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<MediaTypeWithLogs> RdbmsMediaTypeCatalogue::getMediaTypes() const {
+  try {
+    std::list<MediaTypeWithLogs> mediaTypes;
+    const char *const sql =
+      "SELECT"                                              "\n"
+        "MEDIA_TYPE_NAME AS MEDIA_TYPE_NAME,"               "\n"
+        "CARTRIDGE AS CARTRIDGE,"                           "\n"
+        "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"           "\n"
+        "PRIMARY_DENSITY_CODE AS PRIMARY_DENSITY_CODE,"     "\n"
+        "SECONDARY_DENSITY_CODE AS SECONDARY_DENSITY_CODE," "\n"
+        "NB_WRAPS AS NB_WRAPS,"                             "\n"
+        "MIN_LPOS AS MIN_LPOS,"                             "\n"
+        "MAX_LPOS AS MAX_LPOS,"                             "\n"
+
+        "USER_COMMENT AS USER_COMMENT,"                     "\n"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," "\n"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," "\n"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"           "\n"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"   "\n"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"   "\n"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME"              "\n"
+      "FROM"                                                "\n"
+        "MEDIA_TYPE"                                        "\n"
+      "ORDER BY"                                            "\n"
+        "MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      MediaTypeWithLogs mediaType;
+
+      mediaType.name = rset.columnString("MEDIA_TYPE_NAME");
+      mediaType.cartridge = rset.columnString("CARTRIDGE");
+      mediaType.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+      mediaType.primaryDensityCode = rset.columnOptionalUint8("PRIMARY_DENSITY_CODE");
+      mediaType.secondaryDensityCode = rset.columnOptionalUint8("SECONDARY_DENSITY_CODE");
+      mediaType.nbWraps = rset.columnOptionalUint32("NB_WRAPS");
+      mediaType.minLPos = rset.columnOptionalUint64("MIN_LPOS");
+      mediaType.maxLPos = rset.columnOptionalUint64("MAX_LPOS");
+      mediaType.comment = rset.columnString("USER_COMMENT");
+      mediaType.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      mediaType.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      mediaType.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      mediaType.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      mediaType.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      mediaType.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      mediaTypes.push_back(mediaType);
+    }
+
+    return mediaTypes;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+MediaType RdbmsMediaTypeCatalogue::getMediaTypeByVid(const std::string & vid) const {
+  try {
+    std::list<MediaTypeWithLogs> mediaTypes;
+    const char *const sql =
+      "SELECT"                                              "\n"
+        "MEDIA_TYPE_NAME AS MEDIA_TYPE_NAME,"               "\n"
+        "CARTRIDGE AS CARTRIDGE,"                           "\n"
+        "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"           "\n"
+        "PRIMARY_DENSITY_CODE AS PRIMARY_DENSITY_CODE,"     "\n"
+        "SECONDARY_DENSITY_CODE AS SECONDARY_DENSITY_CODE," "\n"
+        "NB_WRAPS AS NB_WRAPS,"                             "\n"
+        "MIN_LPOS AS MIN_LPOS,"                             "\n"
+        "MAX_LPOS AS MAX_LPOS,"                             "\n"
+
+        "MEDIA_TYPE.USER_COMMENT AS USER_COMMENT "          "\n"
+      "FROM"                                                "\n"
+        "MEDIA_TYPE "                                       "\n"
+      "INNER JOIN TAPE "                                    "\n"
+        "ON MEDIA_TYPE.MEDIA_TYPE_ID = TAPE.MEDIA_TYPE_ID " "\n"
+      "WHERE "                                              "\n"
+        "TAPE.VID = :VID"                                   "\n";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID",vid);
+    auto rset = stmt.executeQuery();
+    if(rset.next()){
+      MediaType mediaType;
+
+      mediaType.name = rset.columnString("MEDIA_TYPE_NAME");
+      mediaType.cartridge = rset.columnString("CARTRIDGE");
+      mediaType.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+      mediaType.primaryDensityCode = rset.columnOptionalUint8("PRIMARY_DENSITY_CODE");
+      mediaType.secondaryDensityCode = rset.columnOptionalUint8("SECONDARY_DENSITY_CODE");
+      mediaType.nbWraps = rset.columnOptionalUint32("NB_WRAPS");
+      mediaType.minLPos = rset.columnOptionalUint64("MIN_LPOS");
+      mediaType.maxLPos = rset.columnOptionalUint64("MAX_LPOS");
+      mediaType.comment = rset.columnString("USER_COMMENT");
+
+      return mediaType;
+    } else {
+      throw exception::Exception("The tape vid "+vid+" does not exist.");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "MEDIA_TYPE_NAME = :NEW_MEDIA_TYPE_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :CURRENT_MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    if(newName != currentName){
+      if(RdbmsCatalogueUtils::mediaTypeExists(conn, newName)){
+        throw exception::UserError(std::string("Cannot modify the media type name ") + currentName +". The new name : "
+        + newName + " already exists in the database.");
+      }
+    }
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":NEW_MEDIA_TYPE_NAME", newName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":CURRENT_MEDIA_TYPE_NAME", currentName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + currentName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &cartridge) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "CARTRIDGE = :CARTRIDGE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":CARTRIDGE", cartridge);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t capacityInBytes) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":CAPACITY_IN_BYTES", capacityInBytes);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint8_t primaryDensityCode) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "PRIMARY_DENSITY_CODE = :PRIMARY_DENSITY_CODE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint8(":PRIMARY_DENSITY_CODE", primaryDensityCode);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint8_t secondaryDensityCode) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "SECONDARY_DENSITY_CODE = :SECONDARY_DENSITY_CODE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint8(":SECONDARY_DENSITY_CODE", secondaryDensityCode);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint32_t> &nbWraps) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "NB_WRAPS = :NB_WRAPS,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint32(":NB_WRAPS", nbWraps);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint64_t> &minLPos) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "MIN_LPOS = :MIN_LPOS,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":MIN_LPOS", minLPos);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint64_t> &maxLPos) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "MAX_LPOS = :MAX_LPOS,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":MAX_LPOS", maxLPos);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMediaTypeCatalogue::modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MEDIA_TYPE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify media type ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsMediaTypeCatalogue::mediaTypeIsUsedByTapes(rdbms::Conn &conn, const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MEDIA_TYPE.MEDIA_TYPE_NAME "
+      "FROM "
+        "TAPE "
+      "INNER JOIN "
+        "MEDIA_TYPE "
+      "ON "
+        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
+      "WHERE "
+        "MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<uint64_t> RdbmsMediaTypeCatalogue::getMediaTypeId(rdbms::Conn &conn, const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MEDIA_TYPE.MEDIA_TYPE_ID AS MEDIA_TYPE_ID "
+      "FROM "
+        "MEDIA_TYPE "
+      "WHERE "
+        "MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MEDIA_TYPE_NAME", name);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      return std::nullopt;
+    }
+    return rset.columnUint64("MEDIA_TYPE_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp b/catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp
new file mode 100644
index 0000000000..94ad4e3946
--- /dev/null
+++ b/catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp
@@ -0,0 +1,91 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/MediaTypeCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsMediaTypeCatalogue : public MediaTypeCatalogue {
+public:
+  ~RdbmsMediaTypeCatalogue() override = default;
+
+  void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) override;
+
+  void deleteMediaType(const std::string &name) override;
+
+  std::list<MediaTypeWithLogs> getMediaTypes() const override;
+
+  MediaType getMediaTypeByVid(const std::string & vid) const override;
+
+  void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) override;
+
+  void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &cartridge) override;
+
+  void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const uint64_t capacityInBytes) override;
+
+  void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+   const uint8_t primaryDensityCode) override;
+
+  void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint8_t secondaryDensityCode) override;
+
+  void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint32_t> &nbWraps) override;
+
+  void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &minLPos) override;
+
+  void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &maxLPos) override;
+
+  void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+protected:
+  RdbmsMediaTypeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue);
+
+  virtual uint64_t getNextMediaTypeId(rdbms::Conn &conn) const = 0;
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+  bool mediaTypeIsUsedByTapes(rdbms::Conn &conn, const std::string &name) const;
+
+  friend class RdbmsTapeCatalogue;
+  std::optional<uint64_t> getMediaTypeId(rdbms::Conn &conn, const std::string &name) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsMountPolicyCatalogue.cpp b/catalogue/rdbms/RdbmsMountPolicyCatalogue.cpp
new file mode 100644
index 0000000000..b494941223
--- /dev/null
+++ b/catalogue/rdbms/RdbmsMountPolicyCatalogue.cpp
@@ -0,0 +1,861 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/MountPolicy.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/Logger.hpp"
+#include "common/utils/Regex.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsMountPolicyCatalogue::RdbmsMountPolicyCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue):
+  m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsMountPolicyCatalogue::createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+  const CreateMountPolicyAttributes & mountPolicy) {
+  std::string name = mountPolicy.name;
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(mountPolicy.comment, &m_log);
+    auto conn = m_connPool->getConn();
+    if (RdbmsCatalogueUtils::mountPolicyExists(conn, name)) {
+      throw exception::UserError(std::string("Cannot create mount policy ") + name +
+        " because a mount policy with the same name already exists");
+    }
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO MOUNT_POLICY("
+        "MOUNT_POLICY_NAME,"
+
+        "ARCHIVE_PRIORITY,"
+        "ARCHIVE_MIN_REQUEST_AGE,"
+
+        "RETRIEVE_PRIORITY,"
+        "RETRIEVE_MIN_REQUEST_AGE,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":MOUNT_POLICY_NAME,"
+
+        ":ARCHIVE_PRIORITY,"
+        ":ARCHIVE_MIN_REQUEST_AGE,"
+
+        ":RETRIEVE_PRIORITY,"
+        ":RETRIEVE_MIN_REQUEST_AGE,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+
+    stmt.bindUint64(":ARCHIVE_PRIORITY", mountPolicy.archivePriority);
+    stmt.bindUint64(":ARCHIVE_MIN_REQUEST_AGE", mountPolicy.minArchiveRequestAge);
+
+    stmt.bindUint64(":RETRIEVE_PRIORITY", mountPolicy.retrievePriority);
+    stmt.bindUint64(":RETRIEVE_MIN_REQUEST_AGE", mountPolicy.minRetrieveRequestAge);
+
+    stmt.bindString(":USER_COMMENT", mountPolicy.comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+void RdbmsMountPolicyCatalogue::deleteMountPolicy(const std::string &name) {
+  try {
+    const char *const sql = "DELETE FROM MOUNT_POLICY WHERE MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete mount policy ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+std::list<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getMountPolicies() const {
+  try {
+    auto conn = m_connPool->getConn();
+    return getMountPolicies(conn);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getMountPolicies(rdbms::Conn & conn) const {
+try {
+    std::list<common::dataStructures::MountPolicy> policies;
+    const char *const sql =
+      "SELECT "
+        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+
+        "ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+
+        "RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "MOUNT_POLICY "
+      "ORDER BY "
+        "MOUNT_POLICY_NAME";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::MountPolicy policy;
+
+      policy.name = rset.columnString("MOUNT_POLICY_NAME");
+
+      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
+      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+
+      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+
+      policy.comment = rset.columnString("USER_COMMENT");
+
+      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+
+      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      policies.push_back(policy);
+    }
+    return policies;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getMountPolicy(const std::string &mountPolicyName) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return getMountPolicy(conn, mountPolicyName);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getMountPolicy(rdbms::Conn &conn, const std::string &mountPolicyName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+
+        "ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+
+        "RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "MOUNT_POLICY "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
+    auto rset = stmt.executeQuery();
+    if (rset.next()) {
+      common::dataStructures::MountPolicy policy;
+
+      policy.name = rset.columnString("MOUNT_POLICY_NAME");
+
+      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
+      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+
+      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+
+      policy.comment = rset.columnString("USER_COMMENT");
+
+      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+
+      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      return policy;
+    }
+    return std::nullopt;
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getCachedMountPolicies() const {
+  try {
+    auto getNonCachedValue = [&] {
+      auto conn = m_connPool->getConn();
+      return getMountPolicies(conn);
+    };
+    return m_rdbmsCatalogue->m_allMountPoliciesCache.getCachedValue("all",getNonCachedValue).value;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsMountPolicyCatalogue::modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t archivePriority) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MOUNT_POLICY SET "
+        "ARCHIVE_PRIORITY = :ARCHIVE_PRIORITY,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_PRIORITY", archivePriority);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+void RdbmsMountPolicyCatalogue::modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t minArchiveRequestAge) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MOUNT_POLICY SET "
+        "ARCHIVE_MIN_REQUEST_AGE = :ARCHIVE_MIN_REQUEST_AGE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":ARCHIVE_MIN_REQUEST_AGE", minArchiveRequestAge);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+void RdbmsMountPolicyCatalogue::modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t retrievePriority) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MOUNT_POLICY SET "
+        "RETRIEVE_PRIORITY = :RETRIEVE_PRIORITY,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":RETRIEVE_PRIORITY", retrievePriority);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+void RdbmsMountPolicyCatalogue::modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t minRetrieveRequestAge) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MOUNT_POLICY SET "
+        "RETRIEVE_MIN_REQUEST_AGE = :RETRIEVE_MIN_REQUEST_AGE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":RETRIEVE_MIN_REQUEST_AGE", minRetrieveRequestAge);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+void RdbmsMountPolicyCatalogue::modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE MOUNT_POLICY SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":MOUNT_POLICY_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+  m_rdbmsCatalogue->m_allMountPoliciesCache.invalidate();
+}
+
+//------------------------------------------------------------------------------
+// getRequesterGroupMountPolicy
+//------------------------------------------------------------------------------
+std::optional<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getRequesterGroupMountPolicy(
+  rdbms::Conn &conn, const Group &group) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "MOUNT_POLICY "
+      "INNER JOIN "
+        "REQUESTER_GROUP_MOUNT_RULE "
+      "ON "
+        "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", group.diskInstanceName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", group.groupName);
+    auto rset = stmt.executeQuery();
+    if(rset.next()) {
+      common::dataStructures::MountPolicy policy;
+
+      policy.name = rset.columnString("MOUNT_POLICY_NAME");
+
+      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
+      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+
+      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+
+      policy.comment = rset.columnString("USER_COMMENT");
+      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      return policy;
+    } else {
+      return std::nullopt;
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<common::dataStructures::MountPolicy> RdbmsMountPolicyCatalogue::getRequesterMountPolicy(rdbms::Conn &conn,
+  const User &user) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "MOUNT_POLICY "
+      "INNER JOIN "
+        "REQUESTER_MOUNT_RULE "
+      "ON "
+        "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", user.diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", user.username);
+    auto rset = stmt.executeQuery();
+    if(rset.next()) {
+      common::dataStructures::MountPolicy policy;
+
+      policy.name = rset.columnString("MOUNT_POLICY_NAME");
+
+      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
+      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+
+      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+
+      policy.comment = rset.columnString("USER_COMMENT");
+
+      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+
+      common::dataStructures::EntryLog updateLog;
+      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      return policy;
+    } else {
+      return std::nullopt;
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+RequesterAndGroupMountPolicies RdbmsMountPolicyCatalogue::getMountPolicies(
+  rdbms::Conn &conn,
+  const std::string &diskInstanceName,
+  const std::string &requesterName,
+  const std::string &requesterGroupName,
+  const std::string &activity) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "'ACTIVITY' AS RULE_TYPE,"
+        "REQUESTER_ACTIVITY_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE,"
+        "REQUESTER_ACTIVITY_MOUNT_RULE.ACTIVITY_REGEX AS ACTIVITY_REGEX,"
+
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_ACTIVITY_MOUNT_RULE "
+      "INNER JOIN "
+        "MOUNT_POLICY "
+      "ON "
+        "REQUESTER_ACTIVITY_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_ACTIVITY_MOUNT_RULE.DISK_INSTANCE_NAME = :ACTIVITY_DISK_INSTANCE_NAME AND "
+        "REQUESTER_ACTIVITY_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_ACTIVITY_NAME "
+      "UNION "
+      "SELECT "
+        "'REQUESTER' AS RULE_TYPE,"
+        "REQUESTER_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE,"
+        "'' AS ACTIVITY_REGEX,"
+
+
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_MOUNT_RULE "
+      "INNER JOIN "
+        "MOUNT_POLICY "
+      "ON "
+        "REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :REQUESTER_DISK_INSTANCE_NAME AND "
+        "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME "
+      "UNION "
+      "SELECT "
+        "'REQUESTER_GROUP' AS RULE_TYPE,"
+        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME AS ASSIGNEE,"
+        "'' AS ACTIVITY_REGEX,"
+
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_GROUP_MOUNT_RULE "
+      "INNER JOIN "
+        "MOUNT_POLICY "
+      "ON "
+        "REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :GROUP_DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":ACTIVITY_DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":GROUP_DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_ACTIVITY_NAME", requesterName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    auto rset = stmt.executeQuery();
+
+    RequesterAndGroupMountPolicies policies;
+    while(rset.next()) {
+      common::dataStructures::MountPolicy policy;
+
+      policy.name = rset.columnString("MOUNT_POLICY_NAME");
+      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
+      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+      policy.comment = rset.columnString("USER_COMMENT");
+      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      if(rset.columnString("RULE_TYPE") == "ACTIVITY") {
+        auto activityRegexString = rset.columnString("ACTIVITY_REGEX");
+        cta::utils::Regex activityRegex(activityRegexString);
+        if (activityRegex.has_match(activity)) {
+          policies.requesterActivityMountPolicies.push_back(policy);
+        }
+      } else if(rset.columnString("RULE_TYPE") == "REQUESTER") {
+        policies.requesterMountPolicies.push_back(policy);
+      } else {
+        policies.requesterGroupMountPolicies.push_back(policy);
+      }
+    }
+
+    return policies;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+RequesterAndGroupMountPolicies RdbmsMountPolicyCatalogue::getMountPolicies(
+  rdbms::Conn &conn,
+  const std::string &diskInstanceName,
+  const std::string &requesterName,
+  const std::string &requesterGroupName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "'REQUESTER' AS RULE_TYPE,"
+        "REQUESTER_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE,"
+
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_MOUNT_RULE "
+      "INNER JOIN "
+        "MOUNT_POLICY "
+      "ON "
+        "REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :REQUESTER_DISK_INSTANCE_NAME AND "
+        "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME "
+      "UNION "
+      "SELECT "
+        "'REQUESTER_GROUP' AS RULE_TYPE,"
+        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME AS ASSIGNEE,"
+
+        "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+        "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY,"
+        "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY,"
+        "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE,"
+        "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT,"
+        "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+        "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_GROUP_MOUNT_RULE "
+      "INNER JOIN "
+        "MOUNT_POLICY "
+      "ON "
+        "REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :GROUP_DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":REQUESTER_DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":GROUP_DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    auto rset = stmt.executeQuery();
+
+    RequesterAndGroupMountPolicies policies;
+    while(rset.next()) {
+      common::dataStructures::MountPolicy policy;
+
+      policy.name = rset.columnString("MOUNT_POLICY_NAME");
+      policy.archivePriority = rset.columnUint64("ARCHIVE_PRIORITY");
+      policy.archiveMinRequestAge = rset.columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+      policy.retrievePriority = rset.columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset.columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+      policy.comment = rset.columnString("USER_COMMENT");
+      policy.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      policy.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      if(rset.columnString("RULE_TYPE") == "REQUESTER") {
+        policies.requesterMountPolicies.push_back(policy);
+      } else {
+        policies.requesterGroupMountPolicies.push_back(policy);
+      }
+    }
+
+    return policies;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp b/catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp
new file mode 100644
index 0000000000..6076acffba
--- /dev/null
+++ b/catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp
@@ -0,0 +1,153 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "catalogue/interfaces/MountPolicyCatalogue.hpp"
+#include "catalogue/RequesterAndGroupMountPolicies.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsMountPolicyCatalogue : public MountPolicyCatalogue {
+public:
+  RdbmsMountPolicyCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+  ~RdbmsMountPolicyCatalogue() override = default;
+
+  void createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+    const CreateMountPolicyAttributes & mountPolicy) override;
+
+  std::list<common::dataStructures::MountPolicy> getMountPolicies() const override;
+
+  std::optional<common::dataStructures::MountPolicy> getMountPolicy(
+    const std::string &mountPolicyName) const override;
+
+  std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const override;
+
+  void deleteMountPolicy(const std::string &name) override;
+
+  void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t archivePriority) override;
+
+  void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minArchiveRequestAge) override;
+
+  void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t retrievePriority) override;
+
+  void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minRetrieveRequestAge) override;
+
+  void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+  friend class RdbmsArchiveFileCatalogue;
+  friend class RdbmsRequesterMountRuleCatalogue;
+  friend class RdbmsRequesterGroupMountRuleCatalogue;
+
+  std::optional<common::dataStructures::MountPolicy> getMountPolicy(rdbms::Conn &conn,
+    const std::string &mountPolicyName) const;
+
+  std::list<common::dataStructures::MountPolicy> getMountPolicies(rdbms::Conn & conn) const;
+
+  /**
+   * Returns the specified requester mount-policy or std::nullopt if one does not
+   * exist.
+   *
+   * @param conn The database connection.
+   * @param user The fully qualified user, in other words the name of the disk
+   * instance and the name of the group.
+   * @return The mount policy or std::nullopt if one does not exists.
+   */
+  std::optional<common::dataStructures::MountPolicy> getRequesterMountPolicy(rdbms::Conn &conn, const User &user) const;
+
+  /**
+   * Returns the specified requester-group mount-policy or nullptr if one does
+   * not exist.
+   *
+   * @param conn The database connection.
+   * @param group The fully qualified group, in other words the name of the disk
+   * instance and the name of the group.
+   * @return The mount policy or std::nullopt if one does not exists.
+   */
+  std::optional<common::dataStructures::MountPolicy> getRequesterGroupMountPolicy(rdbms::Conn &conn,
+    const Group &group) const;
+
+  friend class RdbmsTapeFileCatalogue;
+  /**
+   * Returns the mount policies for the specified requester and requester group.
+   *
+   * @param conn The database connection.
+   * @param diskInstanceName The name of the disk instance to which the
+   * requester and requester group belong.
+   * @param requesterName The name of the requester which is only guaranteed to
+   * be unique within its disk instance.
+   * @param requesterGroupName The name of the requester group which is only
+   * guaranteed to be unique within its disk instance.
+   * @return The mount policies.
+   */
+  RequesterAndGroupMountPolicies getMountPolicies(
+    rdbms::Conn &conn,
+    const std::string &diskInstanceName,
+    const std::string &requesterName,
+    const std::string &requesterGroupName) const;
+
+
+  /**
+   * Returns the mount policies for the specified requester, requester group and requester activity.
+   *
+   * @param conn The database connection.
+   * @param diskInstanceName The name of the disk instance to which the
+   * requester and requester group belong.
+   * @param requesterName The name of the requester which is only guaranteed to
+   * be unique within its disk instance.
+   * @param requesterGroupName The name of the requester group which is only
+   * guaranteed to be unique within its disk instance.
+   * @param activity The name of the activity to match the requester activity
+   * mount rules against
+   * @return The mount policies.
+   */
+  RequesterAndGroupMountPolicies getMountPolicies(
+    rdbms::Conn &conn,
+    const std::string &diskInstanceName,
+    const std::string &requesterName,
+    const std::string &requesterGroupName,
+    const std::string &activity) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.cpp b/catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.cpp
new file mode 100644
index 0000000000..e086d471f6
--- /dev/null
+++ b/catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.cpp
@@ -0,0 +1,282 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsRequesterActivityMountRuleCatalogue::RdbmsRequesterActivityMountRuleCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue):
+  m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsRequesterActivityMountRuleCatalogue::modifyRequesterActivityMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &activityRegex, const std::string &mountPolicy) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE REQUESTER_ACTIVITY_MOUNT_RULE SET "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME AND "
+        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicy);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify requester  activity mount rule ") + instanceName + ":" +
+        requesterName + "for activities matching " + activityRegex + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterActivityMountRuleCatalogue::modifyRequesterActivityMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &activityRegex, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE REQUESTER_ACTIVITY_MOUNT_RULE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME AND "
+        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify requester  activity mount rule ") + instanceName + ":" +
+        requesterName + "for activities matching " + activityRegex + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterActivityMountRuleCatalogue::createRequesterActivityMountRule(
+  const common::dataStructures::SecurityIdentity &admin,
+  const std::string &mountPolicyName, const std::string &diskInstanceName, const std::string &requesterName,
+  const std::string &activityRegex, const std::string &comment) {
+   try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::requesterActivityMountRuleExists(conn, diskInstanceName, requesterName, activityRegex)) {
+      throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName +
+        " to requester " + diskInstanceName + ":" + requesterName + " for activities matching " + activityRegex +
+        " because that requester-activity mount rule already exists");
+    }
+    if(!RdbmsCatalogueUtils::mountPolicyExists(conn, mountPolicyName)) {
+      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
+        " to requester " + diskInstanceName + ":" + requesterName + " for activities matching " + activityRegex +
+        " because mount-policy " + mountPolicyName + " does not exist");
+    }
+    if(!RdbmsCatalogueUtils::diskInstanceExists(conn, diskInstanceName)) {
+      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
+        " to requester " + diskInstanceName + ":" + requesterName + " for activities matching " + activityRegex +
+        " because disk-instance " + diskInstanceName + " does not exist");
+    }
+
+    const uint64_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO REQUESTER_ACTIVITY_MOUNT_RULE("
+        "DISK_INSTANCE_NAME,"
+        "REQUESTER_NAME,"
+        "MOUNT_POLICY_NAME,"
+        "ACTIVITY_REGEX,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":DISK_INSTANCE_NAME,"
+        ":REQUESTER_NAME,"
+        ":MOUNT_POLICY_NAME,"
+        ":ACTIVITY_REGEX,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
+    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+}
+
+std::list<common::dataStructures::RequesterActivityMountRule>
+  RdbmsRequesterActivityMountRuleCatalogue::getRequesterActivityMountRules() const {
+  try {
+    std::list<common::dataStructures::RequesterActivityMountRule> rules;
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "REQUESTER_NAME AS REQUESTER_NAME,"
+        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+        "ACTIVITY_REGEX AS ACTIVITY_REGEX,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_ACTIVITY_MOUNT_RULE "
+      "ORDER BY "
+        "DISK_INSTANCE_NAME, REQUESTER_NAME, ACTIVITY_REGEX, MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while(rset.next()) {
+      common::dataStructures::RequesterActivityMountRule rule;
+
+      rule.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      rule.name = rset.columnString("REQUESTER_NAME");
+      rule.mountPolicy = rset.columnString("MOUNT_POLICY_NAME");
+      rule.activityRegex = rset.columnString("ACTIVITY_REGEX");
+      rule.comment = rset.columnString("USER_COMMENT");
+      rule.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      rule.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      rule.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      rule.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      rule.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      rule.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      rules.push_back(rule);
+    }
+
+    return rules;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterActivityMountRuleCatalogue::deleteRequesterActivityMountRule(const std::string &diskInstanceName,
+  const std::string &requesterName, const std::string &activityRegex) {
+  try {
+    const char *const sql =
+      "DELETE FROM "
+        "REQUESTER_ACTIVITY_MOUNT_RULE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME AND "
+        "ACTIVITY_REGEX = :ACTIVITY_REGEX";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":ACTIVITY_REGEX", activityRegex);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete mount rule for requester ") + diskInstanceName + ":"
+        + requesterName + " and activity regex " + activityRegex + " because the rule does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.hpp b/catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..8b9d89b30d
--- /dev/null
+++ b/catalogue/rdbms/RdbmsRequesterActivityMountRuleCatalogue.hpp
@@ -0,0 +1,66 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsRequesterActivityMountRuleCatalogue : public RequesterActivityMountRuleCatalogue {
+public:
+  RdbmsRequesterActivityMountRuleCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  ~RdbmsRequesterActivityMountRuleCatalogue() override = default;
+
+  void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &mountPolicy) override;
+
+  void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &comment) override;
+
+  void createRequesterActivityMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+    const std::string &activityRegex, const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const override;
+
+  void deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName,
+    const std::string &activityRegex) override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.cpp b/catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.cpp
new file mode 100644
index 0000000000..a5f6036177
--- /dev/null
+++ b/catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.cpp
@@ -0,0 +1,279 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.hpp"
+#include "common/dataStructures/RequesterGroupMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsRequesterGroupMountRuleCatalogue::RdbmsRequesterGroupMountRuleCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue):
+  m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsRequesterGroupMountRuleCatalogue::modifyRequesterGroupMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterGroupName, const std::string &mountPolicy) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE REQUESTER_GROUP_MOUNT_RULE SET "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicy);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify requester group mount rule ") + instanceName + ":" +
+        requesterGroupName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterGroupMountRuleCatalogue::modifyRequesterGroupMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterGroupName, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE REQUESTER_GROUP_MOUNT_RULE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify requester group mount rule ") + instanceName + ":" +
+        requesterGroupName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterGroupMountRuleCatalogue::createRequesterGroupMountRule(
+  const common::dataStructures::SecurityIdentity &admin,
+  const std::string &mountPolicyName,
+  const std::string &diskInstanceName,
+  const std::string &requesterGroupName,
+  const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    auto conn = m_connPool->getConn();
+    {
+      const auto group = Group(diskInstanceName, requesterGroupName);
+      const auto mountPolicyCatalogue = static_cast<RdbmsMountPolicyCatalogue*>(m_rdbmsCatalogue->MountPolicy().get());
+      const auto mountPolicy = mountPolicyCatalogue->getRequesterGroupMountPolicy(conn, group);
+      if (mountPolicy) {
+        throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName +
+                                   " to requester-group " + diskInstanceName + ":" + requesterGroupName +
+                                   " because a rule already exists assigning the requester-group to mount-policy " +
+                                   mountPolicy->name);
+      }
+    }
+    if(!RdbmsCatalogueUtils::mountPolicyExists(conn, mountPolicyName)) {
+      throw exception::UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester-group " +
+        diskInstanceName + ":" + requesterGroupName + " because mount-policy " + mountPolicyName + " does not exist");
+    }
+    if(!RdbmsCatalogueUtils::diskInstanceExists(conn, diskInstanceName)) {
+      throw exception::UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester-group " +
+        diskInstanceName + ":" + requesterGroupName + " because disk-instance " + diskInstanceName + " does not exist");
+    }
+
+    const uint64_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO REQUESTER_GROUP_MOUNT_RULE("
+        "DISK_INSTANCE_NAME,"
+        "REQUESTER_GROUP_NAME,"
+        "MOUNT_POLICY_NAME,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":DISK_INSTANCE_NAME,"
+        ":REQUESTER_GROUP_NAME,"
+        ":MOUNT_POLICY_NAME,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+}
+
+std::list<common::dataStructures::RequesterGroupMountRule>
+  RdbmsRequesterGroupMountRuleCatalogue::getRequesterGroupMountRules() const {
+  try {
+    std::list<common::dataStructures::RequesterGroupMountRule> rules;
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME,"
+        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_GROUP_MOUNT_RULE "
+      "ORDER BY "
+        "DISK_INSTANCE_NAME, REQUESTER_GROUP_NAME, MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while(rset.next()) {
+      common::dataStructures::RequesterGroupMountRule rule;
+
+      rule.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      rule.name = rset.columnString("REQUESTER_GROUP_NAME");
+      rule.mountPolicy = rset.columnString("MOUNT_POLICY_NAME");
+
+      rule.comment = rset.columnString("USER_COMMENT");
+      rule.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      rule.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      rule.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      rule.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      rule.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      rule.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      rules.push_back(rule);
+    }
+
+    return rules;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterGroupMountRuleCatalogue::deleteRequesterGroupMountRule(const std::string &diskInstanceName,
+  const std::string &requesterGroupName) {
+  try {
+    const char *const sql =
+      "DELETE FROM "
+        "REQUESTER_GROUP_MOUNT_RULE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete the mount rule for requester group ") + diskInstanceName
+        + ":" + requesterGroupName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_groupMountPolicyCache.invalidate();
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.hpp b/catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..267f2c12b9
--- /dev/null
+++ b/catalogue/rdbms/RdbmsRequesterGroupMountRuleCatalogue.hpp
@@ -0,0 +1,65 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsRequesterGroupMountRuleCatalogue : public RequesterGroupMountRuleCatalogue {
+public:
+  RdbmsRequesterGroupMountRuleCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  ~RdbmsRequesterGroupMountRuleCatalogue() override = default;
+
+  void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) override;
+
+  void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) override;
+
+  void createRequesterGroupMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstanceName, const std::string &requesterGroupName,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const override;
+
+
+  void deleteRequesterGroupMountRule(const std::string &diskInstanceName,
+    const std::string &requesterGroupName) override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.cpp b/catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.cpp
new file mode 100644
index 0000000000..90f2dfe967
--- /dev/null
+++ b/catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.cpp
@@ -0,0 +1,273 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsRequesterMountRuleCatalogue::RdbmsRequesterMountRuleCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue):
+  m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsRequesterMountRuleCatalogue::modifyRequesterMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &mountPolicy) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE REQUESTER_MOUNT_RULE SET "
+        "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicy);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify requester mount rule ") + instanceName + ":" +
+        requesterName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterMountRuleCatalogue::modifyRequesteMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE REQUESTER_MOUNT_RULE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":DISK_INSTANCE_NAME", instanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify requester mount rule ") + instanceName + ":" +
+        requesterName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterMountRuleCatalogue::createRequesterMountRule(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &mountPolicyName, const std::string &diskInstanceName, const std::string &requesterName,
+  const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const auto user = User(diskInstanceName, requesterName);
+    auto conn = m_connPool->getConn();
+    const auto mountPolicyCatalogue = static_cast<RdbmsMountPolicyCatalogue*>(m_rdbmsCatalogue->MountPolicy().get());
+    const auto mountPolicy = mountPolicyCatalogue->getRequesterMountPolicy(conn, user);
+    if(mountPolicy) {
+      throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName +
+        " to requester " + diskInstanceName + ":" + requesterName +
+        " because the requester is already assigned to mount-policy " + mountPolicy->name);
+    }
+    if(!RdbmsCatalogueUtils::mountPolicyExists(conn, mountPolicyName)) {
+      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
+        " to requester " + diskInstanceName + ":" + requesterName + " because mount-policy " + mountPolicyName +
+        " does not exist");
+    }
+    if(!RdbmsCatalogueUtils::diskInstanceExists(conn, diskInstanceName)) {
+      throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName +
+        " to requester " + diskInstanceName + ":" + requesterName + " because disk-instance " + diskInstanceName +
+        " does not exist");
+    }
+
+    const uint64_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO REQUESTER_MOUNT_RULE("
+        "DISK_INSTANCE_NAME,"
+        "REQUESTER_NAME,"
+        "MOUNT_POLICY_NAME,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":DISK_INSTANCE_NAME,"
+        ":REQUESTER_NAME,"
+        ":MOUNT_POLICY_NAME,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.bindString(":MOUNT_POLICY_NAME", mountPolicyName);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+}
+
+std::list<common::dataStructures::RequesterMountRule> RdbmsRequesterMountRuleCatalogue::getRequesterMountRules() const {
+  try {
+    std::list<common::dataStructures::RequesterMountRule> rules;
+    const char *const sql =
+      "SELECT "
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "REQUESTER_NAME AS REQUESTER_NAME,"
+        "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "REQUESTER_MOUNT_RULE "
+      "ORDER BY "
+        "DISK_INSTANCE_NAME, REQUESTER_NAME, MOUNT_POLICY_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while(rset.next()) {
+      common::dataStructures::RequesterMountRule rule;
+
+      rule.diskInstance = rset.columnString("DISK_INSTANCE_NAME");
+      rule.name = rset.columnString("REQUESTER_NAME");
+      rule.mountPolicy = rset.columnString("MOUNT_POLICY_NAME");
+      rule.comment = rset.columnString("USER_COMMENT");
+      rule.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      rule.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      rule.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      rule.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      rule.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      rule.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      rules.push_back(rule);
+    }
+
+    return rules;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsRequesterMountRuleCatalogue::deleteRequesterMountRule(const std::string &diskInstanceName,
+  const std::string &requesterName) {
+  try {
+    const char *const sql =
+      "DELETE FROM "
+        "REQUESTER_MOUNT_RULE "
+      "WHERE "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND "
+        "REQUESTER_NAME = :REQUESTER_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstanceName);
+    stmt.bindString(":REQUESTER_NAME", requesterName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete mount rule for requester ") + diskInstanceName + ":" + requesterName +
+        " because the rule does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+
+  m_rdbmsCatalogue->m_userMountPolicyCache.invalidate();
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.hpp b/catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.hpp
new file mode 100644
index 0000000000..b0264171d7
--- /dev/null
+++ b/catalogue/rdbms/RdbmsRequesterMountRuleCatalogue.hpp
@@ -0,0 +1,63 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/RequesterMountRuleCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsRequesterMountRuleCatalogue : public RequesterMountRuleCatalogue {
+public:
+  RdbmsRequesterMountRuleCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  ~RdbmsRequesterMountRuleCatalogue() override = default;
+
+  void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) override;
+
+  void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &comment) override;
+
+  void createRequesterMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const override;
+
+  void deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) override;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsSchemaCatalogue.cpp b/catalogue/rdbms/RdbmsSchemaCatalogue.cpp
new file mode 100644
index 0000000000..889883c3f5
--- /dev/null
+++ b/catalogue/rdbms/RdbmsSchemaCatalogue.cpp
@@ -0,0 +1,124 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsSchemaCatalogue.hpp"
+#include "catalogue/SchemaVersion.hpp"
+#include "common/Constants.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsSchemaCatalogue::RdbmsSchemaCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool):
+  m_log(log), m_connPool(connPool) {}
+
+SchemaVersion RdbmsSchemaCatalogue::getSchemaVersion() const {
+  try {
+    std::map<std::string, uint64_t> schemaVersion;
+    const char *const sql =
+      "SELECT "
+        "CTA_CATALOGUE.SCHEMA_VERSION_MAJOR AS SCHEMA_VERSION_MAJOR,"
+        "CTA_CATALOGUE.SCHEMA_VERSION_MINOR AS SCHEMA_VERSION_MINOR,"
+        "CTA_CATALOGUE.NEXT_SCHEMA_VERSION_MAJOR AS NEXT_SCHEMA_VERSION_MAJOR,"
+        "CTA_CATALOGUE.NEXT_SCHEMA_VERSION_MINOR AS NEXT_SCHEMA_VERSION_MINOR,"
+        "CTA_CATALOGUE.STATUS AS STATUS "
+      "FROM "
+        "CTA_CATALOGUE";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+
+    if(rset.next()) {
+      SchemaVersion::Builder schemaVersionBuilder;
+      schemaVersionBuilder.schemaVersionMajor(rset.columnUint64("SCHEMA_VERSION_MAJOR"))
+                          .schemaVersionMinor(rset.columnUint64("SCHEMA_VERSION_MINOR"))
+                          .status(rset.columnString("STATUS"));
+      auto schemaVersionMajorNext = rset.columnOptionalUint64("NEXT_SCHEMA_VERSION_MAJOR");
+      auto schemaVersionMinorNext = rset.columnOptionalUint64("NEXT_SCHEMA_VERSION_MINOR");
+      if(schemaVersionMajorNext && schemaVersionMinorNext){
+        schemaVersionBuilder.nextSchemaVersionMajor(schemaVersionMajorNext.value())
+                            .nextSchemaVersionMinor(schemaVersionMinorNext.value());
+      }
+      return schemaVersionBuilder.build();
+    } else {
+      throw exception::Exception("CTA_CATALOGUE does not contain any row");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsSchemaCatalogue::verifySchemaVersion() {
+  try {
+    SchemaVersion schemaVersion = getSchemaVersion();
+    if(auto [major, minor] = schemaVersion.getSchemaVersion<SchemaVersion::MajorMinor>();
+      major != static_cast<uint64_t>(CTA_CATALOGUE_SCHEMA_VERSION_MAJOR)){
+      std::ostringstream exceptionMsg;
+      exceptionMsg << "Catalogue schema MAJOR version differ : Database schema version is "
+        << major << "." << minor
+        << ", CTA schema version is " << CTA_CATALOGUE_SCHEMA_VERSION_MAJOR << "."
+        << CTA_CATALOGUE_SCHEMA_VERSION_MINOR;
+      throw WrongSchemaVersionException(exceptionMsg.str());
+    }
+    if(schemaVersion.getStatus<SchemaVersion::Status>() == SchemaVersion::Status::UPGRADING){
+      std::ostringstream exceptionMsg;
+      exceptionMsg << "Catalogue schema is in status " + schemaVersion.getStatus<std::string>()
+        + ", next schema version is " << schemaVersion.getSchemaVersionNext<std::string>();
+    }
+  } catch (exception::UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// ping
+//------------------------------------------------------------------------------
+void RdbmsSchemaCatalogue::ping() {
+  try {
+    verifySchemaVersion();
+  } catch (WrongSchemaVersionException &){
+    throw;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getTableNames
+//------------------------------------------------------------------------------
+std::list<std::string> RdbmsSchemaCatalogue::getTableNames() const {
+  auto conn = m_connPool->getConn();
+  return conn.getTableNames();
+}
+
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsSchemaCatalogue.hpp b/catalogue/rdbms/RdbmsSchemaCatalogue.hpp
new file mode 100644
index 0000000000..8b6314d1b6
--- /dev/null
+++ b/catalogue/rdbms/RdbmsSchemaCatalogue.hpp
@@ -0,0 +1,60 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/SchemaCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsSchemaCatalogue : public SchemaCatalogue {
+public:
+  RdbmsSchemaCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool);
+  ~RdbmsSchemaCatalogue() override = default;
+
+  SchemaVersion getSchemaVersion() const override;
+
+  void verifySchemaVersion() override;
+
+  void ping() override;
+
+  // This method is for unit tests only in InMemoryCatalogueTest.cpp, it's not defined in the interface
+  /**
+   * Returns the names of all the tables in the database schema in alphabetical
+   * order.
+   *
+   * @return The names of all the tables in the database schema in alphabetical
+   * order.
+   */
+  std::list<std::string> getTableNames() const;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsStorageClassCatalogue.cpp b/catalogue/rdbms/RdbmsStorageClassCatalogue.cpp
new file mode 100644
index 0000000000..738088a698
--- /dev/null
+++ b/catalogue/rdbms/RdbmsStorageClassCatalogue.cpp
@@ -0,0 +1,502 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <string>
+
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+#include "common/log/Logger.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsStorageClassCatalogue::RdbmsStorageClassCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+   RdbmsCatalogue *rdbmsCatalogue):
+  m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsStorageClassCatalogue::createStorageClass(
+  const common::dataStructures::SecurityIdentity &admin,
+  const common::dataStructures::StorageClass &storageClass) {
+  try {
+    if(storageClass.name.empty()) {
+      throw UserSpecifiedAnEmptyStringStorageClassName("Cannot create storage class because the storage class name is"
+        " an empty string");
+    }
+
+    if (storageClass.comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create storage class because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(storageClass.comment, &m_log);
+    std::string vo = storageClass.vo.name;
+
+    if(vo.empty()) {
+      throw UserSpecifiedAnEmptyStringVo("Cannot create storage class because the vo is an empty string");
+    }
+
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::storageClassExists(conn, storageClass.name)) {
+      throw exception::UserError(std::string("Cannot create storage class : ") +
+        storageClass.name + " because it already exists");
+    }
+    if(!RdbmsCatalogueUtils::virtualOrganizationExists(conn,vo)) {
+      throw exception::UserError(std::string("Cannot create storage class : ") +
+        storageClass.name + " because the vo : " + vo + " does not exist");
+    }
+    const uint64_t storageClassId = getNextStorageClassId(conn);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO STORAGE_CLASS("
+        "STORAGE_CLASS_ID,"
+        "STORAGE_CLASS_NAME,"
+        "NB_COPIES,"
+        "VIRTUAL_ORGANIZATION_ID,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":STORAGE_CLASS_ID,"
+        ":STORAGE_CLASS_NAME,"
+        ":NB_COPIES,"
+        "(SELECT VIRTUAL_ORGANIZATION_ID FROM VIRTUAL_ORGANIZATION WHERE VIRTUAL_ORGANIZATION_NAME = :VO),"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindUint64(":STORAGE_CLASS_ID", storageClassId);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClass.name);
+    stmt.bindUint64(":NB_COPIES", storageClass.nbCopies);
+    stmt.bindString(":VO",vo);
+
+    stmt.bindString(":USER_COMMENT", storageClass.comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsStorageClassCatalogue::deleteStorageClass(const std::string &storageClassName) {
+  try {
+    auto conn = m_connPool->getConn();
+
+    if(storageClassIsUsedByArchiveRoutes(conn, storageClassName)) {
+      throw UserSpecifiedStorageClassUsedByArchiveRoutes(std::string("The ") + storageClassName +
+        " storage class is being used by one or more archive routes");
+    }
+
+    if(storageClassIsUsedByArchiveFiles(conn, storageClassName)) {
+      throw UserSpecifiedStorageClassUsedByArchiveFiles(std::string("The ") + storageClassName +
+        " storage class is being used by one or more archive files");
+    }
+
+    if(storageClassIsUsedByFileRecyleLogs(conn,storageClassName)){
+      throw UserSpecifiedStorageClassUsedByFileRecycleLogs(std::string("The ") + storageClassName +
+        " storage class is being used by one or more file in the recycle logs");
+    }
+
+    const char *const sql =
+      "DELETE FROM "
+        "STORAGE_CLASS "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+
+    stmt.executeNonQuery();
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete storage-class : ") +
+        storageClassName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::StorageClass> RdbmsStorageClassCatalogue::getStorageClasses() const {
+  try {
+    std::list<common::dataStructures::StorageClass> storageClasses;
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "NB_COPIES AS NB_COPIES,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
+
+        "STORAGE_CLASS.USER_COMMENT AS USER_COMMENT,"
+
+        "STORAGE_CLASS.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "STORAGE_CLASS.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "STORAGE_CLASS.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "STORAGE_CLASS.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "STORAGE_CLASS.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "STORAGE_CLASS.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "STORAGE_CLASS "
+      "INNER JOIN "
+        "VIRTUAL_ORGANIZATION ON STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+      "ORDER BY "
+        "STORAGE_CLASS_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::StorageClass storageClass;
+
+      storageClass.name = rset.columnString("STORAGE_CLASS_NAME");
+      storageClass.nbCopies = rset.columnUint64("NB_COPIES");
+      storageClass.vo.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
+      storageClass.comment = rset.columnString("USER_COMMENT");
+      storageClass.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      storageClass.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      storageClass.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      storageClass.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      storageClass.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      storageClass.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      storageClasses.push_back(storageClass);
+    }
+
+    return storageClasses;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::StorageClass RdbmsStorageClassCatalogue::getStorageClass(const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "NB_COPIES AS NB_COPIES,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
+        "VIRTUAL_ORGANIZATION.MAX_FILE_SIZE AS MAX_FILE_SIZE,"
+        "STORAGE_CLASS.USER_COMMENT AS USER_COMMENT,"
+
+        "STORAGE_CLASS.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "STORAGE_CLASS.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "STORAGE_CLASS.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "STORAGE_CLASS.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "STORAGE_CLASS.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "STORAGE_CLASS.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "STORAGE_CLASS "
+      "INNER JOIN "
+        "VIRTUAL_ORGANIZATION ON STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", name);
+    auto rset = stmt.executeQuery();
+    if (rset.isEmpty()) {
+      throw exception::UserError(std::string("Cannot get storage class : ") + name +
+        " because it does not exist");
+    }
+    rset.next();
+    common::dataStructures::StorageClass storageClass;
+
+    storageClass.name = rset.columnString("STORAGE_CLASS_NAME");
+    storageClass.nbCopies = rset.columnUint64("NB_COPIES");
+    storageClass.vo.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
+    storageClass.vo.maxFileSize = rset.columnUint64("MAX_FILE_SIZE");
+    storageClass.comment = rset.columnString("USER_COMMENT");
+    storageClass.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+    storageClass.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+    storageClass.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+    storageClass.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+    storageClass.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+    storageClass.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+    return storageClass;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsStorageClassCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t nbCopies) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE STORAGE_CLASS SET "
+        "NB_COPIES = :NB_COPIES,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":NB_COPIES", nbCopies);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":STORAGE_CLASS_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
+        " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsStorageClassCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE STORAGE_CLASS SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":STORAGE_CLASS_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
+        " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsStorageClassCatalogue::modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE STORAGE_CLASS SET "
+        "VIRTUAL_ORGANIZATION_ID = (SELECT VIRTUAL_ORGANIZATION_ID FROM VIRTUAL_ORGANIZATION WHERE VIRTUAL_ORGANIZATION_NAME = :VO),"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto conn = m_connPool->getConn();
+    if(vo.empty()){
+      throw UserSpecifiedAnEmptyStringVo(std::string("Cannot modify the vo of the storage class : ") + name + " because the vo is an empty string");
+    }
+    if(!RdbmsCatalogueUtils::virtualOrganizationExists(conn,vo)){
+      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
+        " because the vo " + vo + " does not exist");
+    }
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VO", vo);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":STORAGE_CLASS_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify storage class : ") + name +
+        " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsStorageClassCatalogue::modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE STORAGE_CLASS SET "
+        "STORAGE_CLASS_NAME = :NEW_STORAGE_CLASS_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :CURRENT_STORAGE_CLASS_NAME";
+    auto conn = m_connPool->getConn();
+    if(newName != currentName){
+      if(RdbmsCatalogueUtils::storageClassExists(conn,newName)){
+        throw exception::UserError(std::string("Cannot modify the storage class name ") + currentName +". The new name : " + newName+" already exists in the database.");
+      }
+    }
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":NEW_STORAGE_CLASS_NAME", newName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":CURRENT_STORAGE_CLASS_NAME", currentName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify storage class : ") + currentName +
+        " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsStorageClassCatalogue::storageClassIsUsedByArchiveRoutes(rdbms::Conn &conn,
+  const std::string &storageClassName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
+      "FROM "
+        "ARCHIVE_ROUTE "
+      "INNER JOIN "
+        "STORAGE_CLASS "
+      "ON "
+        "ARCHIVE_ROUTE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsStorageClassCatalogue::storageClassIsUsedByArchiveFiles(rdbms::Conn &conn,
+  const std::string &storageClassName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN "
+        "STORAGE_CLASS "
+      "ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsStorageClassCatalogue::storageClassIsUsedByFileRecyleLogs(rdbms::Conn &conn,
+  const std::string &storageClassName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME "
+      "FROM "
+        "FILE_RECYCLE_LOG "
+      "INNER JOIN "
+        "STORAGE_CLASS "
+      "ON "
+        "FILE_RECYCLE_LOG.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STORAGE_CLASS_NAME", storageClassName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/RdbmsStorageClassCatalogue.hpp b/catalogue/rdbms/RdbmsStorageClassCatalogue.hpp
new file mode 100644
index 0000000000..9591cc88a9
--- /dev/null
+++ b/catalogue/rdbms/RdbmsStorageClassCatalogue.hpp
@@ -0,0 +1,145 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/StorageClassCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+/**
+  * A fully qualified storage class, in other words the name of the disk
+  * instance and the name of the storage class.
+  */
+struct StorageClass {
+
+  /**
+    * The name of the storage class which is only guaranteed to be unique
+    */
+  std::string storageClassName;
+
+  /**
+    * Constructor.
+    *
+    * @param sN The name of the storage class which is only guaranteed to be
+    * unique within its disk instance.
+    */
+  explicit StorageClass(const std::string &s): storageClassName(s) {}
+
+  /**
+    * Less than operator.
+    *
+    * @param rhs The argument on the right hand side of the operator.
+    * @return True if this object is less than the argument on the right hand
+    * side of the operator.
+    */
+  bool operator<(const StorageClass &rhs) const {
+    return storageClassName < rhs.storageClassName;
+  }
+}; // struct StorageClass
+
+class RdbmsCatalogue;
+
+class RdbmsStorageClassCatalogue : public StorageClassCatalogue {
+public:
+  ~RdbmsStorageClassCatalogue() override = default;
+
+  void createStorageClass(
+    const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::StorageClass &storageClass) override;
+
+  void deleteStorageClass(const std::string &storageClassName) override;
+
+  std::list<common::dataStructures::StorageClass> getStorageClasses() const override;
+
+  common::dataStructures::StorageClass getStorageClass(const std::string &name) const override;
+
+  void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t nbCopies) override;
+
+  void modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) override;
+
+  void modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) override;
+
+protected:
+  RdbmsStorageClassCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+  /**
+   * Returns true if the specified storage class is currently being used by one
+   * or more archive routes.
+   *
+   * @param conn The database connection.
+   * @param storageClassName The name of the storage class.
+   */
+  bool storageClassIsUsedByArchiveRoutes(rdbms::Conn &conn, const std::string &storageClassName) const;
+
+  /**
+   * Returns true if the specified storage class is currently being used by one
+   * or more archive files.
+   *
+   * @param conn The database connection.
+   * @param storageClassName The name of the storage class.
+   */
+  bool storageClassIsUsedByArchiveFiles(rdbms::Conn &conn, const std::string &storageClassName) const;
+
+  /**
+   * Returns true if the specified storage class is currently being used by one
+   * or more files in the recycle log.
+   *
+   * @param conn The database connection.
+   * @param storageClassName The name of the storage class.
+   */
+  bool storageClassIsUsedByFileRecyleLogs(rdbms::Conn & conn, const std::string & storageClassName) const;
+
+  /**
+   * Returns a unique storage class ID that can be used by a new storage class
+   * within the catalogue.
+   *
+   * This method must be implemented by the sub-classes of RdbmsCatalogue
+   * because different database technologies propose different solution to the
+   * problem of generating ever increasing numeric identifiers.
+   *
+   * @param conn The database connection.
+   * @return a unique storage class ID that can be used by a new storage class
+   * within the catalogue.
+   */
+  virtual uint64_t getNextStorageClassId(rdbms::Conn &conn) = 0;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsTapeCatalogue.cpp b/catalogue/rdbms/RdbmsTapeCatalogue.cpp
new file mode 100644
index 0000000000..758e94b425
--- /dev/null
+++ b/catalogue/rdbms/RdbmsTapeCatalogue.cpp
@@ -0,0 +1,1879 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapePoolCatalogue.hpp"
+#include "catalogue/TapeForWriting.hpp"
+#include "catalogue/TapeSearchCriteria.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/Tape.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/log/Logger.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/threading/Mutex.hpp"
+#include "common/threading/MutexLocker.hpp"
+#include "common/Timer.hpp"
+#include "common/utils/utils.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsTapeCatalogue::RdbmsTapeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsTapeCatalogue::createTape(const common::dataStructures::SecurityIdentity &admin,
+  const CreateTapeAttributes & tape) {
+  // CTA hard code this field to FALSE
+  const bool isFromCastor = false;
+  try {
+    std::string vid = tape.vid;
+    std::string mediaTypeName = tape.mediaType;
+    std::string vendor = tape.vendor;
+    std::string logicalLibraryName = tape.logicalLibraryName;
+    std::string tapePoolName = tape.tapePoolName;
+    bool full = tape.full;
+    // Translate an empty comment string to a NULL database value
+    const std::optional<std::string> tapeComment = tape.comment && tape.comment->empty() ? std::nullopt : tape.comment;
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(tapeComment, &m_log);
+    const std::optional<std::string> stateReason = tape.stateReason
+      && cta::utils::trimString(tape.stateReason.value()).empty() ? std::nullopt : tape.stateReason;
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(stateReason, &m_log);
+    if(vid.empty()) {
+      throw UserSpecifiedAnEmptyStringVid("Cannot create tape because the VID is an empty string");
+    }
+
+    if(mediaTypeName.empty()) {
+      throw UserSpecifiedAnEmptyStringMediaType("Cannot create tape because the media type is an empty string");
+    }
+
+    if(vendor.empty()) {
+      throw UserSpecifiedAnEmptyStringVendor("Cannot create tape because the vendor is an empty string");
+    }
+
+    if(logicalLibraryName.empty()) {
+      throw UserSpecifiedAnEmptyStringLogicalLibraryName("Cannot create tape because the logical library name is an"
+        " empty string");
+    }
+
+    if(tapePoolName.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create tape because the tape pool name is an empty string");
+    }
+
+    std::string tapeState;
+    try {
+      tapeState = common::dataStructures::Tape::stateToString(tape.state);
+    } catch(cta::exception::Exception &ex) {
+      std::string errorMsg = "Cannot create tape because the state specified does not exist. Possible values for state are: "
+        + common::dataStructures::Tape::getAllPossibleStates();
+      throw UserSpecifiedANonExistentTapeState(errorMsg);
+    }
+
+    if(tape.state != common::dataStructures::Tape::ACTIVE){
+      if(!stateReason){
+        throw UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive("Cannot create tape because no reason has been "
+          "provided for the state " + tapeState);
+      }
+    }
+
+    auto conn = m_connPool->getConn();
+    if(RdbmsCatalogueUtils::tapeExists(conn, vid)) {
+      throw exception::UserError(std::string("Cannot create tape ") + vid +
+        " because a tape with the same volume identifier already exists");
+    }
+    const auto logicLibCatalogue = static_cast<RdbmsLogicalLibraryCatalogue*>(m_rdbmsCatalogue->LogicalLibrary().get());
+    const auto logicalLibraryId = logicLibCatalogue->getLogicalLibraryId(conn, logicalLibraryName);
+    if(!logicalLibraryId) {
+      throw exception::UserError(std::string("Cannot create tape ") + vid + " because logical library " +
+        logicalLibraryName + " does not exist");
+    }
+    const auto tapePoolCatalogue = static_cast<RdbmsTapePoolCatalogue*>(m_rdbmsCatalogue->TapePool().get());
+    const auto tapePoolId = tapePoolCatalogue->getTapePoolId(conn, tapePoolName);
+    if(!tapePoolId) {
+      throw exception::UserError(std::string("Cannot create tape ") + vid + " because tape pool " +
+      tapePoolName + " does not exist");
+    }
+    const auto mediaTypeCatalogue = static_cast<RdbmsMediaTypeCatalogue*>(m_rdbmsCatalogue->MediaType().get());
+    const auto mediaTypeId = mediaTypeCatalogue->getMediaTypeId(conn, mediaTypeName);
+    if(!mediaTypeId) {
+      throw exception::UserError(std::string("Cannot create tape ") + vid + " because media type " +
+        mediaTypeName + " does not exist");
+    }
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO TAPE("
+        "VID, "
+        "MEDIA_TYPE_ID, "
+        "VENDOR, "
+        "LOGICAL_LIBRARY_ID, "
+        "TAPE_POOL_ID, "
+        "DATA_IN_BYTES, "
+        "LAST_FSEQ, "
+        "IS_FULL, "
+        "IS_FROM_CASTOR, "
+
+        "USER_COMMENT, "
+
+        "TAPE_STATE, "
+        "STATE_REASON, "
+        "STATE_UPDATE_TIME, "
+        "STATE_MODIFIED_BY, "
+
+        "CREATION_LOG_USER_NAME, "
+        "CREATION_LOG_HOST_NAME, "
+        "CREATION_LOG_TIME, "
+
+        "LAST_UPDATE_USER_NAME, "
+        "LAST_UPDATE_HOST_NAME, "
+        "LAST_UPDATE_TIME) "
+      "VALUES("
+        ":VID, "
+        ":MEDIA_TYPE_ID, "
+        ":VENDOR, "
+        ":LOGICAL_LIBRARY_ID, "
+        ":TAPE_POOL_ID, "
+        ":DATA_IN_BYTES, "
+        ":LAST_FSEQ, "
+        ":IS_FULL, "
+        ":IS_FROM_CASTOR, "
+
+        ":USER_COMMENT, "
+
+        ":TAPE_STATE, "
+        ":STATE_REASON, "
+        ":STATE_UPDATE_TIME, "
+        ":STATE_MODIFIED_BY, "
+
+        ":CREATION_LOG_USER_NAME, "
+        ":CREATION_LOG_HOST_NAME, "
+        ":CREATION_LOG_TIME, "
+
+        ":LAST_UPDATE_USER_NAME, "
+        ":LAST_UPDATE_HOST_NAME, "
+        ":LAST_UPDATE_TIME"
+      ")";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":VID", vid);
+    stmt.bindUint64(":MEDIA_TYPE_ID", mediaTypeId.value());
+    stmt.bindString(":VENDOR", vendor);
+    stmt.bindUint64(":LOGICAL_LIBRARY_ID", logicalLibraryId.value());
+    stmt.bindUint64(":TAPE_POOL_ID", tapePoolId.value());
+    stmt.bindUint64(":DATA_IN_BYTES", 0);
+    stmt.bindUint64(":LAST_FSEQ", 0);
+    stmt.bindBool(":IS_FULL", full);
+    stmt.bindBool(":IS_FROM_CASTOR", isFromCastor);
+
+    stmt.bindString(":USER_COMMENT", tapeComment);
+
+    std::string stateModifiedBy = RdbmsCatalogueUtils::generateTapeStateModifiedBy(admin);
+    stmt.bindString(":TAPE_STATE",cta::common::dataStructures::Tape::stateToString(tape.state));
+    stmt.bindString(":STATE_REASON",stateReason);
+    stmt.bindUint64(":STATE_UPDATE_TIME",now);
+    stmt.bindString(":STATE_MODIFIED_BY", stateModifiedBy);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("mediaType", mediaTypeName)
+       .add("vendor", vendor)
+       .add("logicalLibraryName", logicalLibraryName)
+       .add("tapePoolName", tapePoolName)
+       .add("isFull", full ? 1 : 0)
+       .add("isFromCastor", isFromCastor ? 1 : 0)
+       .add("userComment", tape.comment ? tape.comment.value() : "")
+       .add("tapeState",cta::common::dataStructures::Tape::stateToString(tape.state))
+       .add("stateReason",stateReason ? stateReason.value() : "")
+       .add("stateUpdateTime",now)
+       .add("stateModifiedBy",stateModifiedBy)
+       .add("creationLogUserName", admin.username)
+       .add("creationLogHostName", admin.host)
+       .add("creationLogTime", now);
+    lc.log(log::INFO, "Catalogue - user created tape");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::deleteTape(const std::string &vid) {
+  try {
+    const char *const delete_sql =
+      "DELETE "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :DELETE_VID AND "
+        "NOT EXISTS (SELECT VID FROM TAPE_FILE WHERE VID = :SELECT_VID) AND "
+        "NOT EXISTS (SELECT VID FROM FILE_RECYCLE_LOG WHERE VID = :SELECT_VID2)";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(delete_sql);
+    stmt.bindString(":DELETE_VID", vid);
+    stmt.bindString(":SELECT_VID", vid);
+    stmt.bindString(":SELECT_VID2", vid);
+    stmt.executeNonQuery();
+
+    // The delete statement will effect no rows and will not raise an error if
+    // either the tape does not exist or if it still has tape files or files in the recycle log
+    if(0 == stmt.getNbAffectedRows()) {
+      if(RdbmsCatalogueUtils::tapeExists(conn, vid)) {
+        throw UserSpecifiedANonEmptyTape(std::string("Cannot delete tape ") + vid + " because either it contains one or more files or the files that were in it are in the file recycle log.");
+      } else {
+        throw UserSpecifiedANonExistentTape(std::string("Cannot delete tape ") + vid + " because it does not exist");
+      }
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::Tape> RdbmsTapeCatalogue::getTapes(const TapeSearchCriteria &searchCriteria) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return getTapes(conn, searchCriteria);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::VidToTapeMap RdbmsTapeCatalogue::getTapesByVid(const std::string& vid) const {
+  try {
+    const char* const sql =
+    "SELECT "
+      "TAPE.VID AS VID,"
+      "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
+      "TAPE.VENDOR AS VENDOR,"
+      "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
+      "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+      "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
+      "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME,"
+      "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
+      "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
+      "TAPE.LAST_FSEQ AS LAST_FSEQ,"
+      "TAPE.IS_FULL AS IS_FULL,"
+      "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR,"
+      "TAPE.LABEL_FORMAT AS LABEL_FORMAT,"
+      "TAPE.LABEL_DRIVE AS LABEL_DRIVE,"
+      "TAPE.LABEL_TIME AS LABEL_TIME,"
+      "TAPE.LAST_READ_DRIVE AS LAST_READ_DRIVE,"
+      "TAPE.LAST_READ_TIME AS LAST_READ_TIME,"
+      "TAPE.LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE,"
+      "TAPE.LAST_WRITE_TIME AS LAST_WRITE_TIME,"
+      "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
+      "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
+      "TAPE.USER_COMMENT AS USER_COMMENT,"
+      "TAPE.TAPE_STATE AS TAPE_STATE,"
+      "TAPE.STATE_REASON AS STATE_REASON,"
+      "TAPE.STATE_UPDATE_TIME AS STATE_UPDATE_TIME,"
+      "TAPE.STATE_MODIFIED_BY AS STATE_MODIFIED_BY,"
+      "TAPE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+      "TAPE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+      "TAPE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+      "TAPE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+      "TAPE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+      "TAPE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+    "FROM "
+      "TAPE "
+    "INNER JOIN TAPE_POOL ON "
+      "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+    "INNER JOIN LOGICAL_LIBRARY ON "
+      "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
+    "INNER JOIN MEDIA_TYPE ON "
+      "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
+    "INNER JOIN VIRTUAL_ORGANIZATION ON "
+      "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+    "WHERE "
+      "VID = :VID";
+
+    common::dataStructures::VidToTapeMap vidToTapeMap;
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    executeGetTapesByVidStmtAndCollectResults(stmt, vidToTapeMap);
+
+    if(vidToTapeMap.size() != 1) {
+      exception::Exception ex;
+      ex.getMessage() << "Not all tapes were found: expected=1 actual=" << vidToTapeMap.size();
+      throw ex;
+    }
+    return vidToTapeMap;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::VidToTapeMap RdbmsTapeCatalogue::getTapesByVid(const std::set<std::string> &vids) const {
+  try {
+    common::dataStructures::VidToTapeMap vidToTapeMap;
+
+    if(vids.empty()) return vidToTapeMap;
+
+    static const std::string selectTapesBy100VidsSql = getSelectTapesBy100VidsSql();
+
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(selectTapesBy100VidsSql);
+    uint64_t vidNb = 1;
+
+    for(const auto &vid: vids) {
+      // Bind the current tape VID
+      std::ostringstream paramName;
+      paramName << ":V" << vidNb;
+      stmt.bindString(paramName.str(), vid);
+
+      // If the 100th tape VID has not yet been reached
+      if(100 > vidNb) {
+        vidNb++;
+      } else { // The 100th VID has been reached
+        vidNb = 1;
+
+        // Execute the query and collect the results
+        executeGetTapesByVidStmtAndCollectResults(stmt, vidToTapeMap);
+
+        // Create a new statement
+        stmt = conn.createStmt(selectTapesBy100VidsSql);
+      }
+    }
+
+    // If there is a statement under construction
+    if(1 != vidNb) {
+      // Bind the remaining parameters with last tape VID.  This has no effect
+      // on the search results but makes the statement valid.
+      const std::string &lastVid = *vids.rbegin();
+      while(100 >= vidNb) {
+        std::ostringstream paramName;
+        paramName << ":V" << vidNb;
+        stmt.bindString(paramName.str(), lastVid);
+        vidNb++;
+      }
+
+      // Execute the query and collect the results
+      executeGetTapesByVidStmtAndCollectResults(stmt, vidToTapeMap);
+    }
+
+    if(vids.size() != vidToTapeMap.size()) {
+      exception::Exception ex;
+      ex.getMessage() << "Not all tapes were found: expected=" << vids.size() << " actual=" << vidToTapeMap.size();
+      throw ex;
+    }
+
+    return vidToTapeMap;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::map<std::string, std::string> RdbmsTapeCatalogue::getVidToLogicalLibrary(const std::set<std::string> &vids) const {
+  try {
+    std::map<std::string, std::string> vidToLogicalLibrary;
+
+    if(vids.empty()) return vidToLogicalLibrary;
+
+    static const std::string sql = getSelectVidToLogicalLibraryBy100Sql();
+
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(sql);
+    uint64_t vidNb = 1;
+
+    for(const auto &vid: vids) {
+      // Bind the current tape VID
+      std::ostringstream paramName;
+      paramName << ":V" << vidNb;
+      stmt.bindString(paramName.str(), vid);
+
+      // If the 100th tape VID has not yet been reached
+      if(100 > vidNb) {
+        vidNb++;
+      } else { // The 100th VID has been reached
+        vidNb = 1;
+
+        // Execute the query and collect the results
+        executeGetVidToLogicalLibraryBy100StmtAndCollectResults(stmt, vidToLogicalLibrary);
+
+        // Create a new statement
+        stmt = conn.createStmt(sql);
+      }
+    }
+
+    // If there is a statement under construction
+    if(1 != vidNb) {
+      // Bind the remaining parameters with last tape VID.  This has no effect
+      // on the search results but makes the statement valid.
+      const std::string &lastVid = *vids.rbegin();
+      while(100 >= vidNb) {
+        std::ostringstream paramName;
+        paramName << ":V" << vidNb;
+        stmt.bindString(paramName.str(), lastVid);
+        vidNb++;
+      }
+
+      // Execute the query and collect the results
+      executeGetVidToLogicalLibraryBy100StmtAndCollectResults(stmt, vidToLogicalLibrary);
+    }
+
+    if(vids.size() != vidToLogicalLibrary.size()) {
+      exception::Exception ex;
+      ex.getMessage() << "Not all tapes were found: expected=" << vids.size() << " actual=" <<
+        vidToLogicalLibrary.size();
+      throw ex;
+    }
+
+    return vidToLogicalLibrary;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  cta::log::LogContext & lc) {
+  try{
+    log::TimingList tl;
+    utils::Timer t;
+    auto conn = m_connPool->getConn();
+
+    TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = vid;
+    const auto tapes = getTapes(conn, searchCriteria);
+    tl.insertAndReset("getTapesTime", t);
+
+    if (tapes.empty()) {
+      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it does not exist");
+    } else if (tapes.front().state != common::dataStructures::Tape::State::ACTIVE
+      && tapes.front().state != common::dataStructures::Tape::State::DISABLED) {
+      throw exception::UserError(std::string("Cannot reclaim tape ") + vid
+        + " because it is not on ACTIVE or DISABLED state");
+    } else if (!tapes.front().full) {
+      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it is not FULL");
+    }
+
+    // The tape exists and is full, we can try to reclaim it
+    if (this->getNbFilesOnTape(conn, vid) == 0) {
+      tl.insertAndReset("getNbFilesOnTape", t);
+      // There is no files on the tape, we can reclaim it : delete the files and reset the counters
+      static_cast<RdbmsFileRecycleLogCatalogue*>(
+        m_rdbmsCatalogue->FileRecycleLog().get())->deleteFilesFromRecycleLog(conn, vid, lc);
+      tl.insertAndReset("deleteFileFromRecycleLogTime", t);
+      resetTapeCounters(conn, admin, vid);
+      tl.insertAndReset("resetTapeCountersTime", t);
+      log::ScopedParamContainer spc(lc);
+      spc.add("vid", vid);
+      spc.add("host", admin.host);
+      spc.add("username", admin.username);
+      tl.addToLog(spc);
+      lc.log(log::INFO, "In RdbmsCatalogue::reclaimTape(), tape reclaimed.");
+    } else {
+      throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because there is at least one tape"
+            " file in the catalogue that is on the tape");
+    }
+  } catch (exception::UserError& ue) {
+    throw;
+  }
+  catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::checkTapeForLabel(const std::string &vid) {
+  try{
+    auto conn = m_connPool->getConn();
+
+    TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = vid;
+    const auto tapes = getTapes(conn, searchCriteria);
+
+    if(tapes.empty()) {
+      throw exception::UserError(std::string("Cannot label tape ") + vid +
+                                             " because it does not exist");
+    }
+    //The tape exists checks any files on it
+    const uint64_t nbFilesOnTape = getNbFilesOnTape(conn, vid);
+    if( 0 != nbFilesOnTape) {
+      throw exception::UserError(std::string("Cannot label tape ") + vid +
+                                             " because it has " +
+                                             std::to_string(nbFilesOnTape) +
+                                             " file(s)");
+    }
+  } catch (exception::UserError& ue) {
+    throw;
+  }
+  catch (exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t RdbmsTapeCatalogue::getNbFilesOnTape(const std::string &vid) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return getNbFilesOnTape(conn, vid);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &mediaType) {
+  try {
+    auto conn = m_connPool->getConn();
+    if(!RdbmsCatalogueUtils::mediaTypeExists(conn, mediaType)){
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because the media type " + mediaType + " does not exist");
+    }
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "MEDIA_TYPE_ID = (SELECT MEDIA_TYPE_ID FROM MEDIA_TYPE WHERE MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE),"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":MEDIA_TYPE", mediaType);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("mediaType", mediaType)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - mediaType");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  const std::string &vendor) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "VENDOR = :VENDOR,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VENDOR", vendor);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("vendor", vendor)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - vendor");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &logicalLibraryName) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "LOGICAL_LIBRARY_ID = "
+          "(SELECT LOGICAL_LIBRARY_ID FROM LOGICAL_LIBRARY WHERE LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME),"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    if(!RdbmsCatalogueUtils::logicalLibraryExists(conn,logicalLibraryName)){
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because the logical library " + logicalLibraryName + " does not exist");
+    }
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because either it or logical library " +
+        logicalLibraryName + " does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("logicalLibraryName", logicalLibraryName)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - logicalLibraryName");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &tapePoolName) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "TAPE_POOL_ID = (SELECT TAPE_POOL_ID FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME),"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    if(!RdbmsCatalogueUtils::tapePoolExists(conn,tapePoolName)){
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because the tape pool " + tapePoolName + " does not exist");
+    }
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because either it or tape pool " +
+        tapePoolName + " does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("tapePoolName", tapePoolName)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - tapePoolName");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &encryptionKeyName) {
+  try {
+    std::optional<std::string> optionalEncryptionKeyName;
+    if(!encryptionKeyName.empty()) {
+      optionalEncryptionKeyName = encryptionKeyName;
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "ENCRYPTION_KEY_NAME = :ENCRYPTION_KEY_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":ENCRYPTION_KEY_NAME", optionalEncryptionKeyName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("encryptionKeyName", optionalEncryptionKeyName ? optionalEncryptionKeyName.value() : "NULL")
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - encryptionKeyName");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &verificationStatus) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "VERIFICATION_STATUS = :VERIFICATION_STATUS,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    if (verificationStatus.empty()) {
+      stmt.bindString(":VERIFICATION_STATUS", std::nullopt);
+    } else {
+      stmt.bindString(":VERIFICATION_STATUS", verificationStatus);
+    }
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("verificationStatus", verificationStatus)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - verificationStatus");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid,
+  const common::dataStructures::Tape::State & state,
+  const std::optional<common::dataStructures::Tape::State> & prev_state,
+  const std::optional<std::string> & stateReason) {
+  try {
+    using namespace common::dataStructures;
+    const time_t now = time(nullptr);
+
+    const std::optional<std::string> stateReasonCopy = stateReason && cta::utils::trimString(stateReason.value()).empty() ? std::nullopt : stateReason;
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(stateReasonCopy, &m_log);
+
+    std::string stateStr;
+    try {
+      stateStr = cta::common::dataStructures::Tape::stateToString(state);
+    } catch(cta::exception::Exception & ex){
+      std::string errorMsg = "The state provided in parameter (" + std::to_string(state) + ") is not known or has not been initialized existing states are:" + common::dataStructures::Tape::getAllPossibleStates();
+      throw UserSpecifiedANonExistentTapeState(errorMsg);
+    }
+
+    std::string prevStateStr;
+    if (prev_state.has_value()) {
+      try {
+        prevStateStr = cta::common::dataStructures::Tape::stateToString(prev_state.value());
+      } catch (cta::exception::Exception &ex) {
+        std::string errorMsg = "The previous state provided in parameter (" + std::to_string(prev_state.value()) + ") is not known or has not been initialized existing states are:" + common::dataStructures::Tape::getAllPossibleStates();
+        throw UserSpecifiedANonExistentTapeState(errorMsg);
+      }
+    }
+
+    //Check the reason is set for all the status except the ACTIVE one, this is the only state that allows the reason to be set to null.
+    if(state != Tape::State::ACTIVE){
+      if(!stateReasonCopy){
+        throw UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive(std::string("Cannot modify the state of the tape ") + vid + " to " + stateStr + " because the reason has not been provided.");
+      }
+    }
+
+    std::string sql =
+      "UPDATE TAPE SET "
+        "TAPE_STATE = :TAPE_STATE,"
+        "STATE_REASON = :STATE_REASON,"
+        "STATE_UPDATE_TIME = :STATE_UPDATE_TIME,"
+        "STATE_MODIFIED_BY = :STATE_MODIFIED_BY "
+      "WHERE "
+        "VID = :VID";
+
+    if (prev_state.has_value()) {
+      sql += " AND TAPE_STATE = :PREV_TAPE_STATE";
+    }
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":TAPE_STATE", stateStr);
+    stmt.bindString(":STATE_REASON", stateReasonCopy);
+    stmt.bindUint64(":STATE_UPDATE_TIME", now);
+    stmt.bindString(":STATE_MODIFIED_BY", RdbmsCatalogueUtils::generateTapeStateModifiedBy(admin));
+    stmt.bindString(":VID",vid);
+    if (prev_state.has_value()) {
+      stmt.bindString(":PREV_TAPE_STATE",prevStateStr);
+    }
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw UserSpecifiedANonExistentTape(std::string("Cannot modify the state of the tape ") + vid
+        + " because it does not exist or because a recent state change has been detected");
+    }
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsTapeCatalogue::tapeExists(const std::string &vid) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return RdbmsCatalogueUtils::tapeExists(conn, vid);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  const bool fullValue) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "IS_FULL = :IS_FULL,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindBool(":IS_FULL", fullValue);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("isFull", fullValue ? 1 : 0)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - isFull");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  const bool dirtyValue) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "DIRTY = :DIRTY,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindBool(":DIRTY", dirtyValue);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("dirty", dirtyValue ? 1 : 0)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - dirty");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::noSpaceLeftOnTape(const std::string &vid) {
+  try {
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "IS_FULL = '1' "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::Exception(std::string("Tape ") + vid + " does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("isFull", 1)
+       .add("method", "noSpaceLeftOnTape");
+    lc.log(log::INFO, "Catalogue - system modified tape - isFull");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<TapeForWriting> RdbmsTapeCatalogue::getTapesForWriting(const std::string &logicalLibraryName) const {
+  try {
+    std::list<TapeForWriting> tapes;
+    const char *const sql =
+      "SELECT "
+        "TAPE.VID AS VID,"
+        "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
+        "TAPE.VENDOR AS VENDOR,"
+        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
+        "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
+        "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
+        "TAPE.LAST_FSEQ AS LAST_FSEQ,"
+        "TAPE.LABEL_FORMAT AS LABEL_FORMAT "
+      "FROM "
+        "TAPE "
+      "INNER JOIN TAPE_POOL ON "
+        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "INNER JOIN LOGICAL_LIBRARY ON "
+        "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
+      "INNER JOIN MEDIA_TYPE ON "
+        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
+      "INNER JOIN VIRTUAL_ORGANIZATION ON "
+        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+//      "TAPE.LABEL_DRIVE IS NOT NULL AND " // Set when the tape has been labelled
+//      "TAPE.LABEL_TIME IS NOT NULL AND "  // Set when the tape has been labelled
+        "TAPE.TAPE_STATE = :TAPE_STATE AND "
+        "TAPE.IS_FULL = '0' AND "
+        "TAPE.IS_FROM_CASTOR = '0' AND "
+        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME "
+      "ORDER BY TAPE.DATA_IN_BYTES DESC";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName);
+    stmt.bindString(":TAPE_STATE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      TapeForWriting tape;
+      tape.vid = rset.columnString("VID");
+      tape.mediaType = rset.columnString("MEDIA_TYPE");
+      tape.vendor = rset.columnString("VENDOR");
+      tape.tapePool = rset.columnString("TAPE_POOL_NAME");
+      tape.vo = rset.columnString("VO");
+      tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+      tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES");
+      tape.lastFSeq = rset.columnUint64("LAST_FSEQ");
+      tape.labelFormat = common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"),
+        "[RdbmsCatalogue::getTapesForWriting()]");
+
+      tapes.push_back(tape);
+    }
+    return tapes;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::Label::Format RdbmsTapeCatalogue::getTapeLabelFormat(const std::string& vid) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE.LABEL_FORMAT AS LABEL_FORMAT "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if(rset.next()) {
+      return common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"),
+        "[RdbmsCatalogue::getTapeLabelFormat()]");
+    } else {
+      throw exception::Exception(std::string("No such tape with vid=") + vid);
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeIsFromCastorInUnitTests(const std::string &vid) {
+  try {
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "IS_FROM_CASTOR = '1' "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::Exception(std::string("Tape ") + vid + " does not exist");
+    }
+
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("isFromCastor", 1)
+       .add("method", "setTapeIsFromCastorInUnitTests");
+    lc.log(log::INFO, "Catalogue - system modified tape - isFromCastor");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  const std::string & reason) {
+  try {
+    modifyTapeState(admin,vid,common::dataStructures::Tape::DISABLED,std::nullopt,reason);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string & reason) {
+  try {
+    modifyTapeState(admin,vid,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt,reason);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeDirty(const std::string & vid) {
+  try {
+    auto conn = m_connPool->getConn();
+    RdbmsCatalogueUtils::setTapeDirty(conn,vid);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::modifyTapeComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::optional<std::string> &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("userComment", comment ? comment.value() : "")
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - userComment");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::tapeLabelled(const std::string &vid, const std::string &drive) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "LABEL_DRIVE = :LABEL_DRIVE,"
+        "LABEL_TIME = :LABEL_TIME "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LABEL_DRIVE", drive);
+    stmt.bindUint64(":LABEL_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::tapeMountedForArchive(const std::string &vid, const std::string &drive) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE,"
+        "LAST_WRITE_TIME = :LAST_WRITE_TIME, "
+        "WRITE_MOUNT_COUNT = WRITE_MOUNT_COUNT + 1 "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LAST_WRITE_DRIVE", drive);
+    stmt.bindUint64(":LAST_WRITE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if (0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("lastWriteDrive", drive)
+       .add("lastWriteTime", now);
+    lc.log(log::INFO, "Catalogue - system modified tape - mountedForArchive");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::tapeMountedForRetrieve(const std::string &vid, const std::string &drive) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "LAST_READ_DRIVE = :LAST_READ_DRIVE,"
+        "LAST_READ_TIME = :LAST_READ_TIME, "
+        "READ_MOUNT_COUNT = READ_MOUNT_COUNT + 1 "
+      "WHERE "
+        "VID = :VID";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":LAST_READ_DRIVE", drive);
+    stmt.bindUint64(":LAST_READ_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("lastReadDrive", drive)
+       .add("lastReadTime", now);
+    lc.log(log::INFO, "Catalogue - system modified tape - mountedForRetrieve");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::Tape> RdbmsTapeCatalogue::getTapes(rdbms::Conn &conn,
+  const TapeSearchCriteria &searchCriteria) const {
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.vid))
+    throw exception::UserError("VID cannot be an empty string");
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.mediaType))
+    throw exception::UserError("Media type cannot be an empty string");
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.vendor))
+    throw exception::UserError("Vendor cannot be an empty string");
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.logicalLibrary))
+    throw exception::UserError("Logical library cannot be an empty string");
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.tapePool))
+    throw exception::UserError("Tape pool cannot be an empty string");
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.vo))
+    throw exception::UserError("Virtual organisation cannot be an empty string");
+  if(RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.diskFileIds))
+    throw exception::UserError("Disk file ID list cannot be empty");
+
+  try {
+    if(searchCriteria.tapePool && !RdbmsCatalogueUtils::tapePoolExists(conn, searchCriteria.tapePool.value())) {
+      UserSpecifiedANonExistentTapePool ex;
+      ex.getMessage() << "Cannot list tapes because tape pool " + searchCriteria.tapePool.value() + " does not exist";
+      throw ex;
+    }
+
+    std::list<common::dataStructures::Tape> tapes;
+    std::string sql =
+      "SELECT "
+        "TAPE.VID AS VID,"
+        "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
+        "TAPE.VENDOR AS VENDOR,"
+        "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
+        "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME,"
+        "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
+        "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
+        "TAPE.NB_MASTER_FILES AS NB_MASTER_FILES,"
+        "TAPE.MASTER_DATA_IN_BYTES AS MASTER_DATA_IN_BYTES,"
+        "TAPE.LAST_FSEQ AS LAST_FSEQ,"
+        "TAPE.IS_FULL AS IS_FULL,"
+        "TAPE.DIRTY AS DIRTY,"
+
+        "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR,"
+
+        "TAPE.LABEL_FORMAT AS LABEL_FORMAT,"
+
+        "TAPE.LABEL_DRIVE AS LABEL_DRIVE,"
+        "TAPE.LABEL_TIME AS LABEL_TIME,"
+
+        "TAPE.LAST_READ_DRIVE AS LAST_READ_DRIVE,"
+        "TAPE.LAST_READ_TIME AS LAST_READ_TIME,"
+
+        "TAPE.LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE,"
+        "TAPE.LAST_WRITE_TIME AS LAST_WRITE_TIME,"
+
+        "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
+        "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
+
+        "TAPE.VERIFICATION_STATUS AS VERIFICATION_STATUS,"
+
+        "TAPE.USER_COMMENT AS USER_COMMENT,"
+
+        "TAPE.TAPE_STATE AS TAPE_STATE,"
+        "TAPE.STATE_REASON AS STATE_REASON,"
+        "TAPE.STATE_UPDATE_TIME AS STATE_UPDATE_TIME,"
+        "TAPE.STATE_MODIFIED_BY AS STATE_MODIFIED_BY,"
+
+        "TAPE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "TAPE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "TAPE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "TAPE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "TAPE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "TAPE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "TAPE "
+      "INNER JOIN TAPE_POOL ON "
+        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "INNER JOIN LOGICAL_LIBRARY ON "
+        "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
+      "INNER JOIN MEDIA_TYPE ON "
+        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
+      "INNER JOIN VIRTUAL_ORGANIZATION ON "
+        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID";
+
+    if(searchCriteria.vid ||
+       searchCriteria.mediaType ||
+       searchCriteria.vendor ||
+       searchCriteria.logicalLibrary ||
+       searchCriteria.tapePool ||
+       searchCriteria.vo ||
+       searchCriteria.capacityInBytes ||
+       searchCriteria.full ||
+       searchCriteria.diskFileIds ||
+       searchCriteria.state ||
+       searchCriteria.fromCastor) {
+      sql += " WHERE";
+    }
+
+    bool addedAWhereConstraint = false;
+
+    if(searchCriteria.vid) {
+      sql += " TAPE.VID = :VID";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.mediaType) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " MEDIA_TYPE.MEDIA_TYPE_NAME = :MEDIA_TYPE";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.vendor) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " TAPE.VENDOR = :VENDOR";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.logicalLibrary) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.tapePool) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.vo) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME = :VO";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.capacityInBytes) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " MEDIA_TYPE.CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.full) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " TAPE.IS_FULL = :IS_FULL";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.diskFileIds) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " VID IN ("
+         "SELECT DISTINCT A.VID "
+         "FROM "
+           "TAPE_FILE A, ARCHIVE_FILE B "
+         "WHERE "
+           "A.ARCHIVE_FILE_ID = B.ARCHIVE_FILE_ID AND "
+           "B.DISK_FILE_ID IN (:DISK_FID0)"
+           //"B.DISK_FILE_ID IN (:DISK_FID0, :DISK_FID1, :DISK_FID2, :DISK_FID3, :DISK_FID4, :DISK_FID5, :DISK_FID6, :DISK_FID7, :DISK_FID8, :DISK_FID9)"
+         ")";
+      addedAWhereConstraint = true;
+    }
+
+    if(searchCriteria.state) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " TAPE.TAPE_STATE = :TAPE_STATE";
+      addedAWhereConstraint = true;
+    }
+    if(searchCriteria.fromCastor) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " TAPE.IS_FROM_CASTOR = :FROM_CASTOR";
+      addedAWhereConstraint = true;
+    }
+
+    sql += " ORDER BY TAPE.VID";
+
+    auto stmt = conn.createStmt(sql);
+
+    if(searchCriteria.vid) stmt.bindString(":VID", searchCriteria.vid.value());
+    if(searchCriteria.mediaType) stmt.bindString(":MEDIA_TYPE", searchCriteria.mediaType.value());
+    if(searchCriteria.vendor) stmt.bindString(":VENDOR", searchCriteria.vendor.value());
+    if(searchCriteria.logicalLibrary) stmt.bindString(":LOGICAL_LIBRARY_NAME", searchCriteria.logicalLibrary.value());
+    if(searchCriteria.tapePool) stmt.bindString(":TAPE_POOL_NAME", searchCriteria.tapePool.value());
+    if(searchCriteria.vo) stmt.bindString(":VO", searchCriteria.vo.value());
+    if(searchCriteria.capacityInBytes) stmt.bindUint64(":CAPACITY_IN_BYTES", searchCriteria.capacityInBytes.value());
+    if(searchCriteria.full) stmt.bindBool(":IS_FULL", searchCriteria.full.value());
+    if(searchCriteria.fromCastor) stmt.bindBool(":FROM_CASTOR", searchCriteria.fromCastor.value());
+    try{
+      if(searchCriteria.state)
+        stmt.bindString(":TAPE_STATE", cta::common::dataStructures::Tape::stateToString(searchCriteria.state.value()));
+    } catch(cta::exception::Exception &ex){
+      throw cta::exception::UserError(std::string("The state provided does not exist. Possible values are: ")
+        + cta::common::dataStructures::Tape::getAllPossibleStates());
+    }
+
+    // Disk file ID lookup requires multiple queries
+    std::vector<std::string>::const_iterator diskFileId_it;
+    std::set<std::string> vidsInList;
+    if(searchCriteria.diskFileIds) diskFileId_it = searchCriteria.diskFileIds.value().begin();
+    int num_queries = searchCriteria.diskFileIds ? searchCriteria.diskFileIds.value().size() : 1;
+
+    for(int i = 0; i < num_queries; ++i) {
+      if(searchCriteria.diskFileIds) {
+        stmt.bindString(":DISK_FID0", *diskFileId_it++);
+      }
+
+      auto rset = stmt.executeQuery();
+      while (rset.next()) {
+        auto vid = rset.columnString("VID");
+        if(vidsInList.count(vid) == 1) continue;
+        vidsInList.insert(vid);
+
+        common::dataStructures::Tape tape;
+
+        tape.vid = vid;
+        tape.mediaType = rset.columnString("MEDIA_TYPE");
+        tape.vendor = rset.columnString("VENDOR");
+        tape.logicalLibraryName = rset.columnString("LOGICAL_LIBRARY_NAME");
+        tape.tapePoolName = rset.columnString("TAPE_POOL_NAME");
+        tape.vo = rset.columnString("VO");
+        tape.encryptionKeyName = rset.columnOptionalString("ENCRYPTION_KEY_NAME");
+        tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+        tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES");
+        tape.nbMasterFiles = rset.columnUint64("NB_MASTER_FILES");
+        tape.masterDataInBytes = rset.columnUint64("MASTER_DATA_IN_BYTES");
+        tape.lastFSeq = rset.columnUint64("LAST_FSEQ");
+        tape.full = rset.columnBool("IS_FULL");
+        tape.dirty = rset.columnBool("DIRTY");
+        tape.isFromCastor = rset.columnBool("IS_FROM_CASTOR");
+
+        tape.labelFormat = common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"),
+          "[RdbmsCatalogue::getTapes()]");
+
+        tape.labelLog = getTapeLogFromRset(rset, "LABEL_DRIVE", "LABEL_TIME");
+        tape.lastReadLog = getTapeLogFromRset(rset, "LAST_READ_DRIVE", "LAST_READ_TIME");
+        tape.lastWriteLog = getTapeLogFromRset(rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME");
+
+        tape.readMountCount = rset.columnUint64("READ_MOUNT_COUNT");
+        tape.writeMountCount = rset.columnUint64("WRITE_MOUNT_COUNT");
+
+        tape.verificationStatus =  rset.columnOptionalString("VERIFICATION_STATUS");
+
+        auto optionalComment = rset.columnOptionalString("USER_COMMENT");
+        tape.comment = optionalComment ? optionalComment.value() : "";
+
+        tape.setState(rset.columnString("TAPE_STATE"));
+        tape.stateReason = rset.columnOptionalString("STATE_REASON");
+        tape.stateUpdateTime = rset.columnUint64("STATE_UPDATE_TIME");
+        tape.stateModifiedBy = rset.columnString("STATE_MODIFIED_BY");
+
+        tape.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+        tape.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+        tape.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+        tape.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+        tape.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+        tape.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+        tapes.push_back(tape);
+      }
+    }
+    if(searchCriteria.diskFileIds) {
+      // When searching by diskFileId, results are not guaranteed to be in sorted order
+      tapes.sort([](const common::dataStructures::Tape &a, const common::dataStructures::Tape &b) { return a.vid < b.vid; });
+    }
+
+    return tapes;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::setTapeLastFSeq(rdbms::Conn &conn, const std::string &vid, const uint64_t lastFSeq) {
+  try {
+    threading::MutexLocker locker(m_rdbmsCatalogue->m_mutex);
+
+    const uint64_t currentValue = getTapeLastFSeq(conn, vid);
+    if(lastFSeq != currentValue + 1) {
+      exception::Exception ex;
+      ex.getMessage() << "The last FSeq MUST be incremented by exactly one: currentValue=" << currentValue <<
+        ",nextValue=" << lastFSeq;
+      throw ex;
+    }
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "LAST_FSEQ = :LAST_FSEQ "
+      "WHERE "
+        "VID=:VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.bindUint64(":LAST_FSEQ", lastFSeq);
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::deleteTapeFiles(rdbms::Conn& conn, const std::string& vid) const {
+  try {
+    const char * const sql =
+    "DELETE FROM TAPE_FILE WHERE VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+    RdbmsCatalogueUtils::setTapeDirty(conn,vid);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t RdbmsTapeCatalogue::getNbFilesOnTape(rdbms::Conn& conn, const std::string& vid) const {
+  try {
+    const char *const sql =
+    "SELECT COUNT(*) AS NB_FILES FROM TAPE_FILE "
+    "WHERE VID = :VID ";
+
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    rset.next();
+    return rset.columnUint64("NB_FILES");
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeCatalogue::resetTapeCounters(rdbms::Conn& conn, const common::dataStructures::SecurityIdentity& admin,
+  const std::string& vid) const {
+  try {
+    const time_t now = time(nullptr);
+    const char * const sql =
+    "UPDATE TAPE SET "
+        "DATA_IN_BYTES = 0,"
+        "MASTER_DATA_IN_BYTES = 0,"
+        "LAST_FSEQ = 0,"
+        "NB_MASTER_FILES = 0,"
+        "NB_COPY_NB_1 = 0,"
+        "COPY_NB_1_IN_BYTES = 0,"
+        "NB_COPY_NB_GT_1 = 0,"
+        "COPY_NB_GT_1_IN_BYTES = 0,"
+        "IS_FULL = '0',"
+        "IS_FROM_CASTOR = '0',"
+        "VERIFICATION_STATUS = :VERIFICATION_STATUS,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME,"
+        "DIRTY = '0' "
+      "WHERE "
+        "VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VERIFICATION_STATUS", std::nullopt);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VID", vid);
+    stmt.executeNonQuery();
+  } catch (exception::Exception &ex){
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<common::dataStructures::TapeLog> RdbmsTapeCatalogue::getTapeLogFromRset(const rdbms::Rset &rset,
+  const std::string &driveColName, const std::string &timeColName) const {
+  try {
+    const std::optional<std::string> drive = rset.columnOptionalString(driveColName);
+    const std::optional<uint64_t> time = rset.columnOptionalUint64(timeColName);
+
+    if(!drive && !time) {
+      return std::nullopt;
+    }
+
+    if(drive && !time) {
+      throw exception::Exception(std::string("Database column ") + driveColName + " contains " + drive.value() +
+        " but column " + timeColName + " is nullptr");
+    }
+
+    if(time && !drive) {
+      throw exception::Exception(std::string("Database column ") + timeColName + " contains " +
+        std::to_string(time.value()) + " but column " + driveColName + " is nullptr");
+    }
+
+    common::dataStructures::TapeLog tapeLog;
+    tapeLog.drive = drive.value();
+    tapeLog.time = time.value();
+
+    return tapeLog;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::string RdbmsTapeCatalogue::getSelectTapesBy100VidsSql() const {
+  std::stringstream sql;
+
+  sql <<
+    "SELECT "
+      "TAPE.VID AS VID,"
+      "MEDIA_TYPE.MEDIA_TYPE_NAME AS MEDIA_TYPE,"
+      "TAPE.VENDOR AS VENDOR,"
+      "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME,"
+      "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+      "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
+      "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME,"
+      "MEDIA_TYPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES,"
+      "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES,"
+      "TAPE.LAST_FSEQ AS LAST_FSEQ,"
+      "TAPE.IS_FULL AS IS_FULL,"
+      "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR,"
+
+      "TAPE.LABEL_FORMAT AS LABEL_FORMAT,"
+
+      "TAPE.LABEL_DRIVE AS LABEL_DRIVE,"
+      "TAPE.LABEL_TIME AS LABEL_TIME,"
+
+      "TAPE.LAST_READ_DRIVE AS LAST_READ_DRIVE,"
+      "TAPE.LAST_READ_TIME AS LAST_READ_TIME,"
+
+      "TAPE.LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE,"
+      "TAPE.LAST_WRITE_TIME AS LAST_WRITE_TIME,"
+
+      "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
+      "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
+
+      "TAPE.USER_COMMENT AS USER_COMMENT,"
+
+      "TAPE.TAPE_STATE AS TAPE_STATE,"
+      "TAPE.STATE_REASON AS STATE_REASON,"
+      "TAPE.STATE_UPDATE_TIME AS STATE_UPDATE_TIME,"
+      "TAPE.STATE_MODIFIED_BY AS STATE_MODIFIED_BY,"
+
+      "TAPE.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+      "TAPE.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+      "TAPE.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+      "TAPE.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+      "TAPE.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+      "TAPE.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+    "FROM "
+      "TAPE "
+    "INNER JOIN TAPE_POOL ON "
+      "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+    "INNER JOIN LOGICAL_LIBRARY ON "
+      "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
+    "INNER JOIN MEDIA_TYPE ON "
+      "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
+    "INNER JOIN VIRTUAL_ORGANIZATION ON "
+      "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+    "WHERE "
+      "VID IN (:V1";
+
+  for(uint32_t i=2; i<=100; i++) {
+    sql << ",:V" << i;
+  }
+
+  sql << ")";
+
+  return sql.str();
+}
+
+void RdbmsTapeCatalogue::executeGetTapesByVidStmtAndCollectResults(rdbms::Stmt &stmt,
+  common::dataStructures::VidToTapeMap &vidToTapeMap) const {
+  auto rset = stmt.executeQuery();
+  while (rset.next()) {
+    common::dataStructures::Tape tape;
+
+    tape.vid = rset.columnString("VID");
+    tape.mediaType = rset.columnString("MEDIA_TYPE");
+    tape.vendor = rset.columnString("VENDOR");
+    tape.logicalLibraryName = rset.columnString("LOGICAL_LIBRARY_NAME");
+    tape.tapePoolName = rset.columnString("TAPE_POOL_NAME");
+    tape.vo = rset.columnString("VO");
+    tape.encryptionKeyName = rset.columnOptionalString("ENCRYPTION_KEY_NAME");
+    tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+    tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES");
+    tape.lastFSeq = rset.columnUint64("LAST_FSEQ");
+    tape.full = rset.columnBool("IS_FULL");
+    tape.isFromCastor = rset.columnBool("IS_FROM_CASTOR");
+
+    tape.labelFormat = common::dataStructures::Label::validateFormat(rset.columnOptionalUint8("LABEL_FORMAT"),
+      "[RdbmsCatalogue::executeGetTapesByVidsStmtAndCollectResults()]");
+
+    tape.labelLog = getTapeLogFromRset(rset, "LABEL_DRIVE", "LABEL_TIME");
+    tape.lastReadLog = getTapeLogFromRset(rset, "LAST_READ_DRIVE", "LAST_READ_TIME");
+    tape.lastWriteLog = getTapeLogFromRset(rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME");
+    tape.readMountCount = rset.columnUint64("READ_MOUNT_COUNT");
+    tape.writeMountCount = rset.columnUint64("WRITE_MOUNT_COUNT");
+    auto optionalComment = rset.columnOptionalString("USER_COMMENT");
+    tape.comment = optionalComment ? optionalComment.value() : "";
+
+    tape.setState(rset.columnString("TAPE_STATE"));
+    tape.stateReason = rset.columnOptionalString("STATE_REASON");
+    tape.stateUpdateTime = rset.columnUint64("STATE_UPDATE_TIME");
+    tape.stateModifiedBy = rset.columnString("STATE_MODIFIED_BY");
+
+    tape.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+    tape.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+    tape.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+    tape.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+    tape.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+    tape.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+    vidToTapeMap[tape.vid] = tape;
+  }
+}
+
+std::string RdbmsTapeCatalogue::getSelectVidToLogicalLibraryBy100Sql() const {
+  std::stringstream sql;
+
+  sql <<
+    "SELECT "
+      "TAPE.VID AS VID, "
+      "LOGICAL_LIBRARY.LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME "
+    "FROM "
+      "TAPE "
+    "INNER JOIN LOGICAL_LIBRARY ON "
+      "TAPE.LOGICAL_LIBRARY_ID = LOGICAL_LIBRARY.LOGICAL_LIBRARY_ID "
+    "WHERE "
+      "VID IN (:V1";
+
+  for(uint32_t i=2; i<=100; i++) {
+    sql << ",:V" << i;
+  }
+
+  sql << ")";
+
+  return sql.str();
+}
+
+void RdbmsTapeCatalogue::executeGetVidToLogicalLibraryBy100StmtAndCollectResults(rdbms::Stmt &stmt,
+std::map<std::string, std::string> &vidToLogicalLibrary) const {
+  auto rset = stmt.executeQuery();
+  while (rset.next()) {
+    vidToLogicalLibrary[rset.columnString("VID")] = rset.columnString("LOGICAL_LIBRARY_NAME");
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsTapeCatalogue.hpp b/catalogue/rdbms/RdbmsTapeCatalogue.hpp
new file mode 100644
index 0000000000..134ebe0fb0
--- /dev/null
+++ b/catalogue/rdbms/RdbmsTapeCatalogue.hpp
@@ -0,0 +1,186 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/TapeCatalogue.hpp"
+#include "common/log/Logger.hpp"
+#include "common/threading/Mutex.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+class Rset;
+class Stmt;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+class TapeItemWritten;
+
+class RdbmsTapeCatalogue : public TapeCatalogue {
+public:
+  ~RdbmsTapeCatalogue() override = default;
+
+  void createTape(const common::dataStructures::SecurityIdentity &admin, const CreateTapeAttributes & tape) override;
+
+  void deleteTape(const std::string &vid) override;
+
+  std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria &searchCriteria) const override;
+
+  common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const override;
+
+  common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string> &vids) const override;
+
+  std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override;
+
+  void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    cta::log::LogContext & lc) override;
+
+  void checkTapeForLabel(const std::string &vid) override;
+
+  void tapeLabelled(const std::string &vid, const std::string &drive) override;
+
+  uint64_t getNbFilesOnTape(const std::string &vid) const override;
+
+  void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &mediaType) override;
+
+  void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &vendor) override;
+
+  void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &logicalLibraryName) override;
+
+  void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &tapePoolName) override;
+
+  void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &encryptionKeyName) override;
+
+  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &verificationStatus) override;
+
+  void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid,
+    const common::dataStructures::Tape::State & state,
+    const std::optional<common::dataStructures::Tape::State> & prev_state,
+    const std::optional<std::string> & stateReason) override;
+
+  bool tapeExists(const std::string &vid) const override;
+
+  void setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool fullValue) override;
+
+  void setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool dirtyValue) override;
+
+  void setTapeIsFromCastorInUnitTests(const std::string &vid) override;
+
+  void setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) override;
+
+  void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) override;
+
+  void setTapeDirty(const std::string & vid) override;
+
+  void modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::optional<std::string> &comment) override;
+
+  void tapeMountedForArchive(const std::string &vid, const std::string &drive) override;
+
+  void tapeMountedForRetrieve(const std::string &vid, const std::string &drive) override;
+
+  void noSpaceLeftOnTape(const std::string &vid) override;
+
+  std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const override;
+
+  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override;
+
+protected:
+  RdbmsTapeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue);
+
+  /**
+   * Gets the last FSeq of the specified tape.
+   *
+   * @param conn The database connection.
+   * @param vid The volume identifier of the tape.
+   * @return The last FSeq of the tape.
+   */
+  virtual uint64_t getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const = 0;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+  std::list<common::dataStructures::Tape> getTapes(rdbms::Conn &conn, const TapeSearchCriteria &searchCriteria) const;
+
+  std::string getSelectTapesBy100VidsSql() const;
+
+  void executeGetTapesByVidStmtAndCollectResults(rdbms::Stmt &stmt,
+    common::dataStructures::VidToTapeMap &vidToTapeMap) const;
+
+  std::string getSelectVidToLogicalLibraryBy100Sql() const;
+
+  void executeGetVidToLogicalLibraryBy100StmtAndCollectResults(rdbms::Stmt &stmt,
+    std::map<std::string, std::string> &vidToLogicalLibrary) const;
+
+  /**
+   * Returns the number of any files contained in the tape identified by its vid
+   * @param conn the database connection
+   * @param vid the vid in which we will count the number of files
+   * @return the number of files on the tape
+   */
+  uint64_t getNbFilesOnTape(rdbms::Conn &conn, const std::string &vid) const;
+
+  /**
+   * Reset the counters of a tape
+   * @param conn the database connection
+   * @param admin the administrator
+   * @param vid the vid to reset the counters
+   */
+  void resetTapeCounters(rdbms::Conn &conn, const common::dataStructures::SecurityIdentity &admin,
+    const std::string& vid) const;
+
+  /**
+   * Delete all the tape files of the VID passed in parameter
+   * @param conn the database connection
+   * @param vid the vid in which we want to remove all the tape files
+   */
+  void deleteTapeFiles(rdbms::Conn &conn, const std::string& vid) const;
+
+  std::optional<common::dataStructures::TapeLog> getTapeLogFromRset(const rdbms::Rset &rset,
+    const std::string &driveColName, const std::string &timeColName) const;
+
+  /**
+   * Sets the last FSeq of the specified tape to the specified value.
+   *
+   * @param conn The database connection.
+   * @param vid The volume identifier of the tape.
+   * @param lastFseq The new value of the last FSeq.
+   */
+  void setTapeLastFSeq(rdbms::Conn &conn, const std::string &vid, const uint64_t lastFSeq);
+};
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/RdbmsTapeFileCatalogue.cpp b/catalogue/rdbms/RdbmsTapeFileCatalogue.cpp
new file mode 100644
index 0000000000..9fdab495a0
--- /dev/null
+++ b/catalogue/rdbms/RdbmsTapeFileCatalogue.cpp
@@ -0,0 +1,343 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "catalogue/InsertFileRecycleLog.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsMountPolicyCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsTapeFileCatalogue::RdbmsTapeFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {
+}
+
+void RdbmsTapeFileCatalogue::deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) {
+  log::LogContext lc(m_log);
+  auto conn = m_connPool->getConn();
+  copyTapeFileToFileRecyleLogAndDelete(conn, file, reason, lc);
+}
+
+void RdbmsTapeFileCatalogue::copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+  const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, log::LogContext & lc) const {
+  try {
+    utils::Timer timer;
+    log::TimingList timingList;
+    copyTapeFileToFileRecyleLogAndDeleteTransaction(conn, file, reason, &timer, &timingList, lc);
+    timingList.insertAndReset("commitTime", timer);
+    log::ScopedParamContainer spc(lc);
+    spc.add("archiveFileId", file.archiveFileID);
+    spc.add("diskFileId", file.diskFileId);
+    spc.add("diskFilePath", file.diskFileInfo.path);
+    spc.add("diskInstance", file.diskInstance);
+    timingList.addToLog(spc);
+    lc.log(log::INFO, "In RdbmsFileRecycleLogCatalogue::copyArchiveFileToRecycleBinAndDelete: "
+      "ArchiveFile moved to the recycle-bin.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeFileCatalogue::checkTapeItemWrittenFieldsAreSet(const std::string& callingFunc,
+  const TapeItemWritten& event) const {
+  try {
+    if(event.vid.empty()) throw exception::Exception("vid is an empty string");
+    if(0 == event.fSeq) throw exception::Exception("fSeq is 0");
+    if(event.tapeDrive.empty()) throw exception::Exception("tapeDrive is an empty string");
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(callingFunc + " failed: TapeItemWrittenEvent is invalid: " + ex.getMessage().str());
+  }
+}
+
+void RdbmsTapeFileCatalogue::checkTapeFileWrittenFieldsAreSet(const std::string &callingFunc,
+  const TapeFileWritten &event)
+  const {
+  try {
+    if(event.diskInstance.empty()) throw exception::Exception("diskInstance is an empty string");
+    if(event.diskFileId.empty()) throw exception::Exception("diskFileId is an empty string");
+    if(0 == event.diskFileOwnerUid) throw exception::Exception("diskFileOwnerUid is 0");
+    if(0 == event.size) throw exception::Exception("size is 0");
+    if(event.checksumBlob.length() == 0) throw exception::Exception("checksumBlob is an empty string");
+    if(event.storageClassName.empty()) throw exception::Exception("storageClassName is an empty string");
+    if(event.vid.empty()) throw exception::Exception("vid is an empty string");
+    if(0 == event.fSeq) throw exception::Exception("fSeq is 0");
+    if(0 == event.blockId && event.fSeq != 1) throw exception::Exception("blockId is 0 and fSeq is not 1");
+    if(0 == event.copyNb) throw exception::Exception("copyNb is 0");
+    if(event.tapeDrive.empty()) throw exception::Exception("tapeDrive is an empty string");
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(callingFunc + " failed: TapeFileWrittenEvent is invalid: " + ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// insertTapeFile
+//------------------------------------------------------------------------------
+void RdbmsTapeFileCatalogue::insertTapeFile(rdbms::Conn &conn, const common::dataStructures::TapeFile &tapeFile,
+  const uint64_t archiveFileId) {
+  rdbms::AutoRollback autoRollback(conn);
+  try{
+    const auto fileRecycleLogCatalogue = static_cast<RdbmsFileRecycleLogCatalogue*>(
+      m_rdbmsCatalogue->FileRecycleLog().get());
+    std::list<InsertFileRecycleLog> insertedFilesRecycleLog
+      = fileRecycleLogCatalogue->insertOldCopiesOfFilesIfAnyOnFileRecycleLog(conn,tapeFile,archiveFileId);
+    {
+      const time_t now = time(nullptr);
+      const char *const sql =
+        "INSERT INTO TAPE_FILE("
+          "VID,"
+          "FSEQ,"
+          "BLOCK_ID,"
+          "LOGICAL_SIZE_IN_BYTES,"
+          "COPY_NB,"
+          "CREATION_TIME,"
+          "ARCHIVE_FILE_ID)"
+        "VALUES("
+          ":VID,"
+          ":FSEQ,"
+          ":BLOCK_ID,"
+          ":LOGICAL_SIZE_IN_BYTES,"
+          ":COPY_NB,"
+          ":CREATION_TIME,"
+          ":ARCHIVE_FILE_ID)";
+      auto stmt = conn.createStmt(sql);
+
+      stmt.bindString(":VID", tapeFile.vid);
+      stmt.bindUint64(":FSEQ", tapeFile.fSeq);
+      stmt.bindUint64(":BLOCK_ID", tapeFile.blockId);
+      stmt.bindUint64(":LOGICAL_SIZE_IN_BYTES", tapeFile.fileSize);
+      stmt.bindUint64(":COPY_NB", tapeFile.copyNb);
+      stmt.bindUint64(":CREATION_TIME", now);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+    {
+      for(auto& fileRecycleLog: insertedFilesRecycleLog){
+        const char *const sql =
+        "DELETE FROM "
+          "TAPE_FILE "
+        "WHERE "
+          "VID=:VID AND "
+          "FSEQ=:FSEQ";
+        auto stmt = conn.createStmt(sql);
+        stmt.bindString(":VID",fileRecycleLog.vid);
+        stmt.bindUint64(":FSEQ",fileRecycleLog.fSeq);
+        stmt.executeNonQuery();
+      }
+    }
+    conn.commit();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeFileCatalogue::deleteTapeFiles(rdbms::Conn & conn,
+  const common::dataStructures::DeleteArchiveRequest& request) const {
+  try {
+    //Delete the tape files after.
+    const char *const deleteTapeFilesSql =
+    "DELETE FROM "
+      "TAPE_FILE "
+    "WHERE TAPE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+
+    auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
+    deleteTapeFilesStmt.bindUint64(":ARCHIVE_FILE_ID",request.archiveFileID);
+    deleteTapeFilesStmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapeFileCatalogue::deleteTapeFiles(rdbms::Conn & conn,
+  const common::dataStructures::ArchiveFile& file) const {
+  try {
+    for(auto &tapeFile: file.tapeFiles) {
+
+      //Delete the tape file.
+      const char *const deleteTapeFilesSql =
+      "DELETE FROM "
+        "TAPE_FILE "
+      "WHERE "
+       "TAPE_FILE.VID = :VID AND "
+       "TAPE_FILE.FSEQ = :FSEQ";
+
+      auto deleteTapeFilesStmt = conn.createStmt(deleteTapeFilesSql);
+      deleteTapeFilesStmt.bindString(":VID", tapeFile.vid);
+      deleteTapeFilesStmt.bindUint64(":FSEQ", tapeFile.fSeq);
+      deleteTapeFilesStmt.executeNonQuery();
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::RetrieveFileQueueCriteria RdbmsTapeFileCatalogue::prepareToRetrieveFile(
+  const std::string &diskInstanceName, const uint64_t archiveFileId,
+  const common::dataStructures::RequesterIdentity &user, const std::optional<std::string>& activity,
+  log::LogContext &lc, const std::optional<std::string> &mountPolicyName) {
+  try {
+    cta::utils::Timer t;
+    common::dataStructures::RetrieveFileQueueCriteria criteria;
+    {
+      auto conn = m_connPool->getConn();
+      const auto getConnTime = t.secs(utils::Timer::resetCounter);
+      const auto archiveFileCatalogue = static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+      auto archiveFile = archiveFileCatalogue->getArchiveFileToRetrieveByArchiveFileId(conn, archiveFileId);
+      const auto getArchiveFileTime = t.secs(utils::Timer::resetCounter);
+      if(nullptr == archiveFile.get()) {
+        exception::UserError ex;
+        auto tapeFileStateList = archiveFileCatalogue->getTapeFileStateListForArchiveFileId(conn, archiveFileId);
+        if (tapeFileStateList.empty()) {
+          ex.getMessage() << "File with archive file ID " << archiveFileId << " does not exist in CTA namespace";
+          throw ex;
+        }
+        const auto nonBrokenState = std::find_if(std::begin(tapeFileStateList), std::end(tapeFileStateList),
+          [](std::pair<std::string, std::string> state) {
+            return (state.second != "BROKEN")
+                   && (state.second != "BROKEN_PENDING")
+                   && (state.second != "EXPORTED")
+                   && (state.second != "EXPORTED_PENDING");
+          });
+
+        if (nonBrokenState != std::end(tapeFileStateList)) {
+          ex.getMessage() << "WARNING: File with archive file ID " << archiveFileId
+            << " exits in CTA namespace but is temporarily unavailable on " << nonBrokenState->second << " tape "
+            << nonBrokenState->first;
+          throw ex;
+        }
+        const auto brokenState = tapeFileStateList.front();
+        //All tape files are on broken tapes, just generate an error about the first
+        ex.getMessage() << "ERROR: File with archive file ID " << archiveFileId
+          << " exits in CTA namespace but is permanently unavailable on " << brokenState.second << " tape "
+          << brokenState.first;
+        throw ex;
+      }
+      if (mountPolicyName) {
+          const auto mountPolicyCatalogue = static_cast<RdbmsMountPolicyCatalogue*>(m_rdbmsCatalogue->MountPolicy().get());
+          std::optional<common::dataStructures::MountPolicy> mountPolicy = mountPolicyCatalogue->getMountPolicy(conn, mountPolicyName.value());
+          if (mountPolicy) {
+            criteria.archiveFile = *archiveFile;
+            criteria.mountPolicy = mountPolicy.value();
+            return criteria;
+          } else {
+            log::ScopedParamContainer spc(lc);
+            spc.add("mountPolicyName", mountPolicyName.value())
+               .add("archiveFileId", archiveFileId);
+            lc.log(log::WARNING, "Catalogue::prepareToRetrieve Could not find specified mount policy, falling back to querying mount rules");
+          }
+      }
+
+      if(diskInstanceName != archiveFile->diskInstance) {
+        exception::UserError ue;
+        ue.getMessage() << "Cannot retrieve file because the disk instance of the request does not match that of the"
+          " archived file: archiveFileId=" << archiveFileId <<
+          " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" << archiveFile->diskInstance;
+        throw ue;
+      }
+
+      t.reset();
+      RequesterAndGroupMountPolicies mountPolicies;
+      const auto mountPolicyCatalogue = static_cast<RdbmsMountPolicyCatalogue*>(m_rdbmsCatalogue->MountPolicy().get());
+      if (activity) {
+        mountPolicies = mountPolicyCatalogue->getMountPolicies(conn, diskInstanceName, user.name, user.group, activity.value());
+      } else {
+        mountPolicies = mountPolicyCatalogue->getMountPolicies(conn, diskInstanceName, user.name, user.group);
+      }
+
+      const auto getMountPoliciesTime = t.secs(utils::Timer::resetCounter);
+
+      log::ScopedParamContainer spc(lc);
+      spc.add("getConnTime", getConnTime)
+         .add("getArchiveFileTime", getArchiveFileTime)
+         .add("getMountPoliciesTime", getMountPoliciesTime);
+      lc.log(log::INFO, "Catalogue::prepareToRetrieve internal timings");
+
+      // Requester activity mount policies overrule requester mount policies
+      // Requester mount policies overrule requester group mount policies
+      common::dataStructures::MountPolicy mountPolicy;
+      if (!mountPolicies.requesterActivityMountPolicies.empty()) {
+        //More than one may match the activity, so choose the one with highest retrieve priority
+        mountPolicy = *std::max_element(mountPolicies.requesterActivityMountPolicies.begin(),
+          mountPolicies.requesterActivityMountPolicies.end(),
+          [](const  common::dataStructures::MountPolicy &p1,  common::dataStructures::MountPolicy &p2) {
+            return p1.retrievePriority < p2.retrievePriority;
+          });
+      } else if(!mountPolicies.requesterMountPolicies.empty()) {
+        mountPolicy = mountPolicies.requesterMountPolicies.front();
+      } else if(!mountPolicies.requesterGroupMountPolicies.empty()) {
+        mountPolicy = mountPolicies.requesterGroupMountPolicies.front();
+      } else {
+        mountPolicies = mountPolicyCatalogue->getMountPolicies(conn, diskInstanceName, "default", user.group);
+
+        if(!mountPolicies.requesterMountPolicies.empty()) {
+          mountPolicy = mountPolicies.requesterMountPolicies.front();
+        } else {
+          exception::UserError ue;
+          ue.getMessage()
+            << "Cannot retrieve file because there are no mount rules for the requester, activity or their group:"
+            << " archiveFileId=" << archiveFileId << " requester=" << diskInstanceName << ":" << user.name << ":"
+            << user.group;
+          if (activity) {
+            ue.getMessage() << " activity=" << activity.value();
+          }
+          throw ue;
+        }
+      }
+      criteria.archiveFile = *archiveFile;
+      criteria.mountPolicy = mountPolicy;
+    }
+    return criteria;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsTapeFileCatalogue.hpp b/catalogue/rdbms/RdbmsTapeFileCatalogue.hpp
new file mode 100644
index 0000000000..bfd8b6a6ca
--- /dev/null
+++ b/catalogue/rdbms/RdbmsTapeFileCatalogue.hpp
@@ -0,0 +1,131 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/TapeFileCatalogue.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct ArchiveFile;
+struct DeleteArchiveRequest;
+struct TapeFile;
+}  // namespace dataStructures
+}  // namespace common
+
+namespace log {
+class TimingList;
+}
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace utils {
+class Timer;
+}
+
+namespace catalogue {
+
+struct TapeItemWritten;
+struct TapeFileWritten;
+class RdbmsCatalogue;
+
+class RdbmsTapeFileCatalogue : public TapeFileCatalogue {
+public:
+  ~RdbmsTapeFileCatalogue() override = default;
+
+  void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) override;
+
+  common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(const std::string &diskInstanceName,
+    const uint64_t archiveFileId, const common::dataStructures::RequesterIdentity &user,
+    const std::optional<std::string> & activity, log::LogContext &lc,
+    const std::optional<std::string> &mountPolicyName = std::nullopt) override;
+
+protected:
+  RdbmsTapeFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue);
+
+protected:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+protected:
+  void copyTapeFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+    const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, log::LogContext & lc) const;
+
+  virtual void copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+    const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+    log::TimingList *timingList, log::LogContext & lc) const = 0;
+
+  /**
+   * Throws an exception if one of the fields of the specified event have not
+   * been set.
+   *
+   * @param callingFunc The name of the calling function.
+   * @param event The evnt to be checked.
+   */
+  void checkTapeItemWrittenFieldsAreSet(const std::string &callingFunc, const TapeItemWritten &event) const;
+
+  /**
+   * Throws an exception if one of the fields of the specified event have not
+   * been set.
+   *
+   * @param callingFunc The name of the calling function.
+   * @param event The evnt to be checked.
+   */
+  void checkTapeFileWrittenFieldsAreSet(const std::string &callingFunc, const TapeFileWritten &event) const;
+
+  friend class RdbmsFileRecycleLogCatalogue;
+  /**
+   * Inserts the specified tape file into the Tape table.
+   *
+   * @param conn The database connection.
+   * @param tapeFile The tape file.
+   * @param archiveFileId The identifier of the archive file of which the tape
+   * file is a copy.
+   */
+  void insertTapeFile(rdbms::Conn &conn, const common::dataStructures::TapeFile &tapeFile,
+    const uint64_t archiveFileId);
+
+  friend class OracleArchiveFileCatalogue;
+  friend class PostgresArchiveFileCatalogue;
+  friend class SqliteArchiveFileCatalogue;
+  /**
+   * Delete the TapeFiles associated to an ArchiveFile from the TAPE_FILE table
+   * @param conn the database connection
+   * @param file the file that contains the tape files to delete
+   */
+  void deleteTapeFiles(rdbms::Conn & conn, const common::dataStructures::ArchiveFile &file) const;
+
+  /**
+   * Delete the TapeFile from the TAPE_FILE table
+   * @param conn the database connection
+   * @param request the DeleteArchiveRequest that contains the archiveFileId to delete the corresponding tape files
+   */
+  void deleteTapeFiles(rdbms::Conn & conn, const common::dataStructures::DeleteArchiveRequest & request) const;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/RdbmsTapePoolCatalogue.cpp b/catalogue/rdbms/RdbmsTapePoolCatalogue.cpp
new file mode 100644
index 0000000000..58f181cb51
--- /dev/null
+++ b/catalogue/rdbms/RdbmsTapePoolCatalogue.cpp
@@ -0,0 +1,762 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <optional>
+#include <string>
+
+#include "catalogue/interfaces/StorageClassCatalogue.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsTapePoolCatalogue.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/TapePoolSearchCriteria.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/log/Logger.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsTapePoolCatalogue::RdbmsTapePoolCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue *rdbmsCatalogue)
+  : m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsTapePoolCatalogue::createTapePool(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+  const std::optional<std::string> &supply, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot create tape pool because the tape pool name is an empty string");
+    }
+
+    if(vo.empty()) {
+      throw UserSpecifiedAnEmptyStringVo("Cannot create tape pool because the VO is an empty string");
+    }
+
+    if (comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create tape pool because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    auto conn = m_connPool->getConn();
+
+    if(RdbmsCatalogueUtils::tapePoolExists(conn, name)) {
+      throw exception::UserError(std::string("Cannot create tape pool ") + name +
+        " because a tape pool with the same name already exists");
+    }
+    if(!RdbmsCatalogueUtils::virtualOrganizationExists(conn,vo)){
+      throw exception::UserError(std::string("Cannot create tape pool ") + name + \
+        " because vo : "+vo+" does not exist.");
+    }
+    const uint64_t tapePoolId = getNextTapePoolId(conn);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "INSERT INTO TAPE_POOL("
+        "TAPE_POOL_ID,"
+        "TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION_ID,"
+        "NB_PARTIAL_TAPES,"
+        "IS_ENCRYPTED,"
+        "SUPPLY,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "SELECT "
+        ":TAPE_POOL_ID,"
+        ":TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION_ID,"
+        ":NB_PARTIAL_TAPES,"
+        ":IS_ENCRYPTED,"
+        ":SUPPLY,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VO";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindUint64(":TAPE_POOL_ID", tapePoolId);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    stmt.bindString(":VO", vo);
+    stmt.bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes);
+    stmt.bindBool(":IS_ENCRYPTED", encryptionValue);
+    stmt.bindString(":SUPPLY", supply);
+
+    stmt.bindString(":USER_COMMENT", comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::deleteTapePool(const std::string &name) {
+  try {
+    auto conn = m_connPool->getConn();
+
+    if(tapePoolUsedInAnArchiveRoute(conn, name)) {
+      UserSpecifiedTapePoolUsedInAnArchiveRoute ex;
+      ex.getMessage() << "Cannot delete tape-pool " << name << " because it is used in an archive route";
+      throw ex;
+    }
+
+    const uint64_t nbTapesInPool = getNbTapesInPool(conn, name);
+
+    if(0 == nbTapesInPool) {
+      const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindString(":TAPE_POOL_NAME", name);
+      stmt.executeNonQuery();
+
+      if(0 == stmt.getNbAffectedRows()) {
+        throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
+      }
+
+      m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+    } else {
+      throw UserSpecifiedAnEmptyTapePool(std::string("Cannot delete tape-pool ") + name + " because it is not empty");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<TapePool> RdbmsTapePoolCatalogue::getTapePools(const TapePoolSearchCriteria &searchCriteria) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return getTapePools(conn, searchCriteria);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<TapePool> RdbmsTapePoolCatalogue::getTapePools(rdbms::Conn &conn,
+  const TapePoolSearchCriteria &searchCriteria) const {
+  if (RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.name))
+    throw exception::UserError("Pool name cannot be an empty string");
+  if (RdbmsCatalogueUtils::isSetAndEmpty(searchCriteria.vo))
+    throw exception::UserError("Virtual organisation cannot be an empty string");
+  try {
+
+    if (searchCriteria.name && !RdbmsCatalogueUtils::tapePoolExists(conn, searchCriteria.name.value())) {
+      UserSpecifiedANonExistentTapePool ex;
+      ex.getMessage() << "Cannot list tape pools because tape pool " + searchCriteria.name.value() + " does not exist";
+      throw ex;
+    }
+
+    if (searchCriteria.vo
+      && !RdbmsCatalogueUtils::virtualOrganizationExists(conn, searchCriteria.vo.value())) {
+      UserSpecifiedANonExistentVirtualOrganization ex;
+      ex.getMessage() << "Cannot list tape pools because virtual organization " + searchCriteria.vo.value() + " does not exist";
+      throw ex;
+    }
+
+    std::list<TapePool> pools;
+    std::string sql =
+      "SELECT "
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
+        "TAPE_POOL.NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES,"
+        "TAPE_POOL.IS_ENCRYPTED AS IS_ENCRYPTED,"
+        "TAPE_POOL.SUPPLY AS SUPPLY,"
+
+        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.DATA_IN_BYTES = 0 THEN 1 ELSE 0 END), 0) AS NB_EMPTY_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_DISABLED THEN 1 ELSE 0 END), 0) AS NB_DISABLED_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.IS_FULL <> '0' THEN 1 ELSE 0 END), 0) AS NB_FULL_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_ACTIVE AND TAPE.IS_FULL = '0' THEN 1 ELSE 0 END), 0) AS NB_WRITABLE_TAPES,"
+        "COALESCE(SUM(MEDIA_TYPE.CAPACITY_IN_BYTES), 0) AS CAPACITY_IN_BYTES,"
+        "COALESCE(SUM(TAPE.DATA_IN_BYTES), 0) AS DATA_IN_BYTES,"
+        "COALESCE(SUM(TAPE.LAST_FSEQ), 0) AS NB_PHYSICAL_FILES,"
+
+        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
+
+        "TAPE_POOL.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "TAPE_POOL.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "TAPE_POOL.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "TAPE_POOL.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "TAPE_POOL.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "TAPE_POOL.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "TAPE_POOL "
+      "INNER JOIN VIRTUAL_ORGANIZATION ON "
+        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+      "LEFT OUTER JOIN TAPE ON "
+        "TAPE_POOL.TAPE_POOL_ID = TAPE.TAPE_POOL_ID "
+      "LEFT OUTER JOIN MEDIA_TYPE ON "
+        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID";
+
+    if (searchCriteria.name || searchCriteria.vo || searchCriteria.encrypted) {
+      sql += " WHERE ";
+    }
+    bool addedAWhereConstraint = false;
+    if (searchCriteria.name) {
+      sql += "TAPE_POOL.TAPE_POOL_NAME = :NAME";
+      addedAWhereConstraint = true;
+    }
+
+    if (searchCriteria.vo) {
+      if (addedAWhereConstraint) sql += " AND ";
+      sql += "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME = :VO";
+      addedAWhereConstraint = true;
+    }
+
+    if (searchCriteria.encrypted) {
+      if (addedAWhereConstraint) sql += " AND ";
+      sql += "TAPE_POOL.IS_ENCRYPTED = :ENCRYPTED";
+    }
+
+    sql +=
+        " GROUP BY "
+           "TAPE_POOL.TAPE_POOL_NAME,"
+           "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME,"
+           "TAPE_POOL.NB_PARTIAL_TAPES,"
+           "TAPE_POOL.IS_ENCRYPTED,"
+           "TAPE_POOL.SUPPLY,"
+           "TAPE_POOL.USER_COMMENT,"
+           "TAPE_POOL.CREATION_LOG_USER_NAME,"
+           "TAPE_POOL.CREATION_LOG_HOST_NAME,"
+           "TAPE_POOL.CREATION_LOG_TIME,"
+           "TAPE_POOL.LAST_UPDATE_USER_NAME,"
+           "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
+           "TAPE_POOL.LAST_UPDATE_TIME "
+         "ORDER BY "
+           "TAPE_POOL_NAME";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":STATE_DISABLED",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::DISABLED));
+    stmt.bindString(":STATE_ACTIVE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
+
+    if (searchCriteria.name) {
+      stmt.bindString(":NAME", searchCriteria.name.value());
+    }
+
+    if (searchCriteria.vo) {
+      stmt.bindString(":VO", searchCriteria.vo.value());
+    }
+
+    if(searchCriteria.encrypted) {
+      stmt.bindBool(":ENCRYPTED", searchCriteria.encrypted.value());
+    }
+
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      TapePool pool;
+
+      pool.name = rset.columnString("TAPE_POOL_NAME");
+      pool.vo.name = rset.columnString("VO");
+      pool.nbPartialTapes = rset.columnUint64("NB_PARTIAL_TAPES");
+      pool.encryption = rset.columnBool("IS_ENCRYPTED");
+      pool.supply = rset.columnOptionalString("SUPPLY");
+      pool.nbTapes = rset.columnUint64("NB_TAPES");
+      pool.nbEmptyTapes = rset.columnUint64("NB_EMPTY_TAPES");
+      pool.nbDisabledTapes = rset.columnUint64("NB_DISABLED_TAPES");
+      pool.nbFullTapes = rset.columnUint64("NB_FULL_TAPES");
+      pool.nbWritableTapes = rset.columnUint64("NB_WRITABLE_TAPES");
+      pool.capacityBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+      pool.dataBytes = rset.columnUint64("DATA_IN_BYTES");
+      pool.nbPhysicalFiles = rset.columnUint64("NB_PHYSICAL_FILES");
+      pool.comment = rset.columnString("USER_COMMENT");
+      pool.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      pool.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      pool.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      pool.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      pool.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      pool.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+      pools.push_back(pool);
+    }
+
+    return pools;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<TapePool> RdbmsTapePoolCatalogue::getTapePool(const std::string &tapePoolName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
+        "TAPE_POOL.NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES,"
+        "TAPE_POOL.IS_ENCRYPTED AS IS_ENCRYPTED,"
+        "TAPE_POOL.SUPPLY AS SUPPLY,"
+
+        "COALESCE(COUNT(TAPE.VID), 0) AS NB_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.DATA_IN_BYTES = 0 THEN 1 ELSE 0 END), 0) AS NB_EMPTY_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_DISABLED THEN 1 ELSE 0 END), 0) AS NB_DISABLED_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.IS_FULL <> '0' THEN 1 ELSE 0 END), 0) AS NB_FULL_TAPES,"
+        "COALESCE(SUM(CASE WHEN TAPE.TAPE_STATE = :STATE_ACTIVE AND TAPE.IS_FULL = '0' THEN 1 ELSE 0 END), 0) AS NB_WRITABLE_TAPES,"
+        "COALESCE(SUM(MEDIA_TYPE.CAPACITY_IN_BYTES), 0) AS CAPACITY_IN_BYTES,"
+        "COALESCE(SUM(TAPE.DATA_IN_BYTES), 0) AS DATA_IN_BYTES,"
+        "COALESCE(SUM(TAPE.LAST_FSEQ), 0) AS NB_PHYSICAL_FILES,"
+
+        "TAPE_POOL.USER_COMMENT AS USER_COMMENT,"
+
+        "TAPE_POOL.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "TAPE_POOL.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "TAPE_POOL.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "TAPE_POOL.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "TAPE_POOL.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "TAPE_POOL.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "TAPE_POOL "
+      "INNER JOIN VIRTUAL_ORGANIZATION ON "
+        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+      "LEFT OUTER JOIN TAPE ON "
+        "TAPE_POOL.TAPE_POOL_ID = TAPE.TAPE_POOL_ID "
+      "LEFT OUTER JOIN MEDIA_TYPE ON "
+        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
+      "GROUP BY "
+        "TAPE_POOL.TAPE_POOL_NAME,"
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME,"
+        "TAPE_POOL.NB_PARTIAL_TAPES,"
+        "TAPE_POOL.IS_ENCRYPTED,"
+        "TAPE_POOL.SUPPLY,"
+        "TAPE_POOL.USER_COMMENT,"
+        "TAPE_POOL.CREATION_LOG_USER_NAME,"
+        "TAPE_POOL.CREATION_LOG_HOST_NAME,"
+        "TAPE_POOL.CREATION_LOG_TIME,"
+        "TAPE_POOL.LAST_UPDATE_USER_NAME,"
+        "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
+        "TAPE_POOL.LAST_UPDATE_TIME "
+      "HAVING "
+        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME "
+      "ORDER BY "
+        "TAPE_POOL_NAME";
+
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+    stmt.bindString(":STATE_DISABLED",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::DISABLED));
+    stmt.bindString(":STATE_ACTIVE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
+
+    auto rset = stmt.executeQuery();
+
+    if (!rset.next()) {
+      return std::nullopt;
+    }
+
+    TapePool pool;
+    pool.name = rset.columnString("TAPE_POOL_NAME");
+    pool.vo.name = rset.columnString("VO");
+    pool.nbPartialTapes = rset.columnUint64("NB_PARTIAL_TAPES");
+    pool.encryption = rset.columnBool("IS_ENCRYPTED");
+    pool.supply = rset.columnOptionalString("SUPPLY");
+    pool.nbTapes = rset.columnUint64("NB_TAPES");
+    pool.nbEmptyTapes = rset.columnUint64("NB_EMPTY_TAPES");
+    pool.nbDisabledTapes = rset.columnUint64("NB_DISABLED_TAPES");
+    pool.nbFullTapes = rset.columnUint64("NB_FULL_TAPES");
+    pool.nbWritableTapes = rset.columnUint64("NB_WRITABLE_TAPES");
+    pool.capacityBytes = rset.columnUint64("CAPACITY_IN_BYTES");
+    pool.dataBytes = rset.columnUint64("DATA_IN_BYTES");
+    pool.nbPhysicalFiles = rset.columnUint64("NB_PHYSICAL_FILES");
+    pool.comment = rset.columnString("USER_COMMENT");
+    pool.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+    pool.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+    pool.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+    pool.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+    pool.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+    pool.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+
+    return pool;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
+        " string");
+    }
+
+    if(vo.empty()) {
+      throw UserSpecifiedAnEmptyStringVo("Cannot modify tape pool because the new VO is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE_POOL SET "
+        "VIRTUAL_ORGANIZATION_ID = (SELECT VIRTUAL_ORGANIZATION_ID FROM VIRTUAL_ORGANIZATION WHERE VIRTUAL_ORGANIZATION_NAME=:VO),"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto conn = m_connPool->getConn();
+
+    if(!RdbmsCatalogueUtils::virtualOrganizationExists(conn,vo)){
+      throw exception::UserError(std::string("Cannot modify tape pool ") + name +" because the vo " + vo + " does not exist");
+    }
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VO", vo);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
+    }
+    //The VO of this tapepool has changed, invalidate the tapepool-VO cache
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t nbPartialTapes) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
+        " string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE_POOL SET "
+        "NB_PARTIAL_TAPES = :NB_PARTIAL_TAPES,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
+        " string");
+    }
+
+    if(comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot modify tape pool because the new comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE_POOL SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool encryptionValue) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE_POOL SET "
+        "IS_ENCRYPTED = :IS_ENCRYPTED,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindBool(":IS_ENCRYPTED", encryptionValue);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &supply) {
+  try {
+    if(name.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
+        " string");
+    }
+
+    std::optional<std::string> optionalSupply;
+    if(!supply.empty()) {
+      optionalSupply = supply;
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE_POOL SET "
+        "SUPPLY = :SUPPLY,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":SUPPLY", optionalSupply);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsTapePoolCatalogue::modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  try {
+    if(currentName.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty"
+        " string");
+    }
+
+    if(newName.empty()) {
+      throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the new name is an empty string");
+    }
+
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE_POOL SET "
+        "TAPE_POOL_NAME = :NEW_TAPE_POOL_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "TAPE_POOL_NAME = :CURRENT_TAPE_POOL_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":NEW_TAPE_POOL_NAME", newName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":CURRENT_TAPE_POOL_NAME", currentName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify tape pool ") + currentName + " because it does not exist");
+    }
+
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsTapePoolCatalogue::tapePoolUsedInAnArchiveRoute(rdbms::Conn &conn, const std::string &tapePoolName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE_POOL_NAME AS TAPE_POOL_NAME "
+      "FROM "
+        "TAPE_POOL "
+      "INNER JOIN ARCHIVE_ROUTE ON "
+        "TAPE_POOL.TAPE_POOL_ID = ARCHIVE_ROUTE.TAPE_POOL_ID "
+      "WHERE "
+        "TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", tapePoolName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t RdbmsTapePoolCatalogue::getNbTapesInPool(rdbms::Conn &conn, const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "COUNT(*) AS NB_TAPES "
+      "FROM "
+        "TAPE "
+      "INNER JOIN TAPE_POOL ON "
+        "TAPE.TAPE_POOL_ID = TAPE_POOL.TAPE_POOL_ID "
+      "WHERE "
+        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set of SELECT COUNT(*) is empty");
+    }
+    return rset.columnUint64("NB_TAPES");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::optional<uint64_t> RdbmsTapePoolCatalogue::getTapePoolId(rdbms::Conn &conn, const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE_POOL_ID AS TAPE_POOL_ID "
+      "FROM "
+        "TAPE_POOL "
+      "WHERE "
+        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      return std::nullopt;
+    }
+    return rset.columnUint64("TAPE_POOL_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsTapePoolCatalogue::tapePoolExists(const std::string &tapePoolName) const {
+  try {
+    auto conn = m_connPool->getConn();
+    return RdbmsCatalogueUtils::tapePoolExists(conn, tapePoolName);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsTapePoolCatalogue.hpp b/catalogue/rdbms/RdbmsTapePoolCatalogue.hpp
new file mode 100644
index 0000000000..c155f51016
--- /dev/null
+++ b/catalogue/rdbms/RdbmsTapePoolCatalogue.hpp
@@ -0,0 +1,122 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "catalogue/interfaces/TapePoolCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsTapePoolCatalogue : public TapePoolCatalogue {
+public:
+  ~RdbmsTapePoolCatalogue() override = default;
+
+  void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+    const std::optional<std::string> &supply, const std::string &comment) override;
+
+  void deleteTapePool(const std::string &name) override;
+
+  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const override;
+
+  std::optional<TapePool> getTapePool(const std::string &tapePoolName) const override;
+
+  void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) override;
+
+  void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const uint64_t nbPartialTapes) override;
+
+  void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+  void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool encryptionValue) override;
+
+  void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &supply) override;
+
+  void modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) override;
+
+  bool tapePoolExists(const std::string &tapePoolName) const override;
+
+protected:
+  RdbmsTapePoolCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue);
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+  /**
+   * Returns a unique tape pool ID that can be used by a new tape pool within
+   * the catalogue.
+   *
+   * This method must be implemented by the sub-classes of RdbmsCatalogue
+   * because different database technologies propose different solution to the
+   * problem of generating ever increasing numeric identifiers.
+   *
+   * @param conn The database connection.
+   * @return a unique tape pool ID that can be used by a new tape pool within
+   * the catalogue.
+   */
+  virtual uint64_t getNextTapePoolId(rdbms::Conn &conn) const = 0;
+
+  std::list<TapePool> getTapePools(rdbms::Conn &conn, const TapePoolSearchCriteria &searchCriteria) const;
+
+  /**
+   * Returns true if the specified tape pool is used in an archive route.
+   *
+   * @param conn The database connection.
+   * @param tapePoolName The name of the tape pool.
+   * @return True if the tape pool is used in an archive route.
+   */
+  bool tapePoolUsedInAnArchiveRoute(rdbms::Conn &conn, const std::string &tapePoolName) const;
+
+  /**
+   * Returns the number of tapes in the specified tape pool.
+   *
+   * If the tape pool does not exist then this method returns 0.
+   *
+   * @param conn The database connection.
+   * @param name The name of the tape pool.
+   * @return The number of tapes in the specified tape pool.
+   */
+  uint64_t getNbTapesInPool(rdbms::Conn &conn, const std::string &name) const;
+
+  friend class RdbmsTapeCatalogue;
+  std::optional<uint64_t> getTapePoolId(rdbms::Conn &conn, const std::string &name) const;
+};
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.cpp b/catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.cpp
new file mode 100644
index 0000000000..b9d5da8194
--- /dev/null
+++ b/catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.cpp
@@ -0,0 +1,591 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RdbmsVirtualOrganizationCatalogue::RdbmsVirtualOrganizationCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue):
+  m_log(log), m_connPool(connPool), m_rdbmsCatalogue(rdbmsCatalogue) {}
+
+void RdbmsVirtualOrganizationCatalogue::createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin,
+  const common::dataStructures::VirtualOrganization &vo) {
+  try {
+    if (vo.name.empty()){
+      throw UserSpecifiedAnEmptyStringVo("Cannot create virtual organization because the name is an empty string");
+    }
+    if (vo.comment.empty()) {
+      throw UserSpecifiedAnEmptyStringComment("Cannot create virtual organization because the comment is an empty string");
+    }
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(vo.comment, &m_log);
+    if (vo.diskInstanceName.empty()) {
+      throw UserSpecifiedAnEmptyStringDiskInstanceName("Cannot create virtual organization because the disk instance is an empty string");
+    }
+
+    auto conn = m_connPool->getConn();
+    if (RdbmsCatalogueUtils::virtualOrganizationExists(conn, vo.name)) {
+      throw exception::UserError(std::string("Cannot create vo : ") +
+        vo.name + " because it already exists");
+    }
+    const uint64_t virtualOrganizationId = getNextVirtualOrganizationId(conn);
+    const time_t now = time(nullptr);
+    const char *const sql =
+    "INSERT INTO VIRTUAL_ORGANIZATION("
+        "VIRTUAL_ORGANIZATION_ID,"
+        "VIRTUAL_ORGANIZATION_NAME,"
+
+        "READ_MAX_DRIVES,"
+        "WRITE_MAX_DRIVES,"
+        "MAX_FILE_SIZE,"
+
+        "DISK_INSTANCE_NAME,"
+
+        "USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME,"
+
+        "LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME)"
+      "VALUES("
+        ":VIRTUAL_ORGANIZATION_ID,"
+        ":VIRTUAL_ORGANIZATION_NAME,"
+        ":READ_MAX_DRIVES,"
+        ":WRITE_MAX_DRIVES,"
+        ":MAX_FILE_SIZE,"
+
+        ":DISK_INSTANCE_NAME,"
+
+        ":USER_COMMENT,"
+
+        ":CREATION_LOG_USER_NAME,"
+        ":CREATION_LOG_HOST_NAME,"
+        ":CREATION_LOG_TIME,"
+
+        ":LAST_UPDATE_USER_NAME,"
+        ":LAST_UPDATE_HOST_NAME,"
+        ":LAST_UPDATE_TIME)";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindUint64(":VIRTUAL_ORGANIZATION_ID", virtualOrganizationId);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", vo.name);
+
+    stmt.bindUint64(":READ_MAX_DRIVES",vo.readMaxDrives);
+    stmt.bindUint64(":WRITE_MAX_DRIVES",vo.writeMaxDrives);
+    stmt.bindUint64(":MAX_FILE_SIZE", vo.maxFileSize);
+
+    stmt.bindString(":DISK_INSTANCE_NAME", vo.diskInstanceName);
+
+    stmt.bindString(":USER_COMMENT", vo.comment);
+
+    stmt.bindString(":CREATION_LOG_USER_NAME", admin.username);
+    stmt.bindString(":CREATION_LOG_HOST_NAME", admin.host);
+    stmt.bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt.executeNonQuery();
+
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::deleteVirtualOrganization(const std::string &voName) {
+  try {
+    auto conn = m_connPool->getConn();
+
+    if(virtualOrganizationIsUsedByStorageClasses(conn, voName)) {
+      throw UserSpecifiedStorageClassUsedByArchiveRoutes(std::string("The Virtual Organization ") + voName +
+        " is being used by one or more storage classes");
+    }
+
+    if(virtualOrganizationIsUsedByTapepools(conn, voName)) {
+      throw UserSpecifiedStorageClassUsedByArchiveFiles(std::string("The Virtual Organization ") + voName +
+        " is being used by one or more Tapepools");
+    }
+
+    const char *const sql =
+      "DELETE FROM "
+        "VIRTUAL_ORGANIZATION "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+
+    stmt.executeNonQuery();
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete Virtual Organization : ") +
+        voName + " because it does not exist");
+    }
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<common::dataStructures::VirtualOrganization> RdbmsVirtualOrganizationCatalogue::getVirtualOrganizations() const {
+  try {
+    std::list<common::dataStructures::VirtualOrganization> virtualOrganizations;
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
+
+        "READ_MAX_DRIVES AS READ_MAX_DRIVES,"
+        "WRITE_MAX_DRIVES AS WRITE_MAX_DRIVES,"
+        "MAX_FILE_SIZE AS MAX_FILE_SIZE,"
+
+        "USER_COMMENT AS USER_COMMENT,"
+
+        "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+
+        "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "ORDER BY "
+        "VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    while (rset.next()) {
+      common::dataStructures::VirtualOrganization virtualOrganization;
+
+      virtualOrganization.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
+
+      virtualOrganization.readMaxDrives = rset.columnUint64("READ_MAX_DRIVES");
+      virtualOrganization.writeMaxDrives = rset.columnUint64("WRITE_MAX_DRIVES");
+      virtualOrganization.maxFileSize = rset.columnUint64("MAX_FILE_SIZE");
+      virtualOrganization.comment = rset.columnString("USER_COMMENT");
+      virtualOrganization.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+      virtualOrganization.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+      virtualOrganization.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+      virtualOrganization.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+      virtualOrganization.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+      virtualOrganization.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+      virtualOrganization.diskInstanceName = rset.columnString("DISK_INSTANCE_NAME");
+
+      virtualOrganizations.push_back(virtualOrganization);
+    }
+
+    return virtualOrganizations;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::VirtualOrganization RdbmsVirtualOrganizationCatalogue::getVirtualOrganizationOfTapepool(
+  const std::string & tapepoolName) const {
+   try {
+    auto conn = m_connPool->getConn();
+    return getVirtualOrganizationOfTapepool(conn,tapepoolName);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::VirtualOrganization RdbmsVirtualOrganizationCatalogue::getVirtualOrganizationOfTapepool(
+  rdbms::Conn & conn, const std::string & tapepoolName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME,"
+
+        "VIRTUAL_ORGANIZATION.READ_MAX_DRIVES AS READ_MAX_DRIVES,"
+        "VIRTUAL_ORGANIZATION.WRITE_MAX_DRIVES AS WRITE_MAX_DRIVES,"
+        "VIRTUAL_ORGANIZATION.MAX_FILE_SIZE AS MAX_FILE_SIZE,"
+
+        "VIRTUAL_ORGANIZATION.USER_COMMENT AS USER_COMMENT,"
+
+        "VIRTUAL_ORGANIZATION.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME,"
+        "VIRTUAL_ORGANIZATION.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME,"
+        "VIRTUAL_ORGANIZATION.CREATION_LOG_TIME AS CREATION_LOG_TIME,"
+
+        "VIRTUAL_ORGANIZATION.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+
+        "VIRTUAL_ORGANIZATION.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME,"
+        "VIRTUAL_ORGANIZATION.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME,"
+        "VIRTUAL_ORGANIZATION.LAST_UPDATE_TIME AS LAST_UPDATE_TIME "
+      "FROM "
+        "TAPE_POOL "
+      "INNER JOIN "
+        "VIRTUAL_ORGANIZATION "
+      "ON "
+        "TAPE_POOL.VIRTUAL_ORGANIZATION_ID = VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+        "TAPE_POOL.TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME",tapepoolName);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()){
+      throw exception::UserError(std::string("In RdbmsCatalogue::getVirtualOrganizationsOfTapepool() unable to find the Virtual Organization of the tapepool ") + tapepoolName + ".");
+    }
+    common::dataStructures::VirtualOrganization virtualOrganization;
+
+    virtualOrganization.name = rset.columnString("VIRTUAL_ORGANIZATION_NAME");
+    virtualOrganization.readMaxDrives = rset.columnUint64("READ_MAX_DRIVES");
+    virtualOrganization.writeMaxDrives = rset.columnUint64("WRITE_MAX_DRIVES");
+    virtualOrganization.maxFileSize = rset.columnUint64("MAX_FILE_SIZE");
+    virtualOrganization.comment = rset.columnString("USER_COMMENT");
+    virtualOrganization.creationLog.username = rset.columnString("CREATION_LOG_USER_NAME");
+    virtualOrganization.creationLog.host = rset.columnString("CREATION_LOG_HOST_NAME");
+    virtualOrganization.creationLog.time = rset.columnUint64("CREATION_LOG_TIME");
+    virtualOrganization.lastModificationLog.username = rset.columnString("LAST_UPDATE_USER_NAME");
+    virtualOrganization.lastModificationLog.host = rset.columnString("LAST_UPDATE_HOST_NAME");
+    virtualOrganization.lastModificationLog.time = rset.columnUint64("LAST_UPDATE_TIME");
+    virtualOrganization.diskInstanceName = rset.columnString("DISK_INSTANCE_NAME");
+    return virtualOrganization;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+common::dataStructures::VirtualOrganization RdbmsVirtualOrganizationCatalogue::getCachedVirtualOrganizationOfTapepool(
+  const std::string & tapepoolName) const {
+  try {
+    auto getNonCachedValue = [&] {
+      auto conn = m_connPool->getConn();
+      return getVirtualOrganizationOfTapepool(conn,tapepoolName);
+    };
+    return m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.getCachedValue(tapepoolName,getNonCachedValue).value;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::modifyVirtualOrganizationName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+  const std::string &newVoName) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE VIRTUAL_ORGANIZATION SET "
+        "VIRTUAL_ORGANIZATION_NAME = :NEW_VIRTUAL_ORGANIZATION_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :CUR_VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+    if(newVoName != currentVoName){
+      if(RdbmsCatalogueUtils::virtualOrganizationExists(conn,newVoName)){
+        throw exception::UserError(std::string("Cannot modify the virtual organization name ") + currentVoName +". The new name : " + newVoName+" already exists in the database.");
+      }
+    }
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":NEW_VIRTUAL_ORGANIZATION_NAME", newVoName);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":CUR_VIRTUAL_ORGANIZATION_NAME", currentVoName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify virtual organization : ") + currentVoName +
+        " because it does not exist");
+    }
+
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::modifyVirtualOrganizationReadMaxDrives(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE VIRTUAL_ORGANIZATION SET "
+        "READ_MAX_DRIVES = :READ_MAX_DRIVES,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":READ_MAX_DRIVES", readMaxDrives);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
+        " because it does not exist");
+    }
+
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::modifyVirtualOrganizationWriteMaxDrives(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE VIRTUAL_ORGANIZATION SET "
+        "WRITE_MAX_DRIVES = :WRITE_MAX_DRIVES,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":WRITE_MAX_DRIVES", writeMaxDrives);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
+        " because it does not exist");
+    }
+
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::modifyVirtualOrganizationMaxFileSize(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE VIRTUAL_ORGANIZATION SET "
+        "MAX_FILE_SIZE = :MAX_FILE_SIZE,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindUint64(":MAX_FILE_SIZE", maxFileSize);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
+        " because it does not exist");
+    }
+
+    m_rdbmsCatalogue->m_tapepoolVirtualOrganizationCache.invalidate();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::modifyVirtualOrganizationComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) {
+  try {
+    RdbmsCatalogueUtils::checkCommentOrReasonMaxLength(comment, &m_log);
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE VIRTUAL_ORGANIZATION SET "
+        "USER_COMMENT = :USER_COMMENT,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":USER_COMMENT", comment);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
+        " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void RdbmsVirtualOrganizationCatalogue::modifyVirtualOrganizationDiskInstanceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE VIRTUAL_ORGANIZATION SET "
+        "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME,"
+        "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME,"
+        "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME,"
+        "LAST_UPDATE_TIME = :LAST_UPDATE_TIME "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto conn = m_connPool->getConn();
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":DISK_INSTANCE_NAME", diskInstance);
+    stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username);
+    stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host);
+    stmt.bindUint64(":LAST_UPDATE_TIME", now);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    stmt.executeNonQuery();
+
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot modify virtual organization : ") + voName +
+        " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsVirtualOrganizationCatalogue::virtualOrganizationIsUsedByStorageClasses(rdbms::Conn &conn,
+  const std::string &voName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "INNER JOIN "
+        "STORAGE_CLASS "
+      "ON "
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID = STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+bool RdbmsVirtualOrganizationCatalogue::virtualOrganizationIsUsedByTapepools(rdbms::Conn &conn,
+  const std::string &voName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "INNER JOIN "
+        "TAPE_POOL "
+      "ON "
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID = TAPE_POOL.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp b/catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp
new file mode 100644
index 0000000000..4682cc4e52
--- /dev/null
+++ b/catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp
@@ -0,0 +1,110 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/VirtualOrganizationCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+
+namespace rdbms {
+class Conn;
+class ConnPool;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class RdbmsVirtualOrganizationCatalogue : public VirtualOrganizationCatalogue {
+public:
+  ~RdbmsVirtualOrganizationCatalogue() override = default;
+
+  void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::VirtualOrganization &vo) override;
+
+  void deleteVirtualOrganization(const std::string &voName) override;
+  std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const override;
+
+  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const override;
+
+  common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const override;
+
+  void modifyVirtualOrganizationName(
+    const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+    const std::string &newVoName) override;
+
+  void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t readMaxDrives) override;
+
+  void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t writeMaxDrives) override;
+
+  void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t maxFileSize) override;
+
+  void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &comment) override;
+
+  void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &diskInstance) override;
+
+protected:
+  RdbmsVirtualOrganizationCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+
+  /**
+   * Returns a unique virtual organization ID that can be used by a new Virtual Organization
+   * within the catalogue.
+   *
+   * This method must be implemented by the sub-classes of RdbmsCatalogue
+   * because different database technologies propose different solution to the
+   * problem of generating ever increasing numeric identifiers.
+   *
+   * @param conn The database connection
+   * @return a unique virtual organization ID that can be used by a new Virtual Organization
+   * within the catalogue.
+   */
+  virtual uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) = 0;
+
+private:
+  log::Logger &m_log;
+  std::shared_ptr<rdbms::ConnPool> m_connPool;
+  RdbmsCatalogue* m_rdbmsCatalogue;
+
+  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(rdbms::Conn & conn,
+    const std::string & tapepoolName) const;
+
+  bool virtualOrganizationIsUsedByStorageClasses(rdbms::Conn &conn, const std::string &voName) const;
+
+  /**
+   * Returns true if the specified Virtual Organization is currently being used by one
+   * or more Tapepools
+   *
+   * @param conn The database connection.
+   * @param voName The name of the Virtual Organization.
+   */
+  bool virtualOrganizationIsUsedByTapepools(rdbms::Conn &conn, const std::string &voName) const;
+};
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleArchiveFileCatalogue.cpp b/catalogue/rdbms/oracle/OracleArchiveFileCatalogue.cpp
new file mode 100644
index 0000000000..52cfbb3c20
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleArchiveFileCatalogue.cpp
@@ -0,0 +1,344 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+#include "rdbms/wrapper/OcciColumn.hpp"
+#include "rdbms/wrapper/OcciStmt.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleArchiveFileCatalogue::OracleArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsArchiveFileCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void OracleArchiveFileCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName,
+  const uint64_t archiveFileId, log::LogContext &lc) {
+  try {
+    const char *selectSql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
+        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
+        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
+        "TAPE_FILE.VID AS VID,"
+        "TAPE_FILE.FSEQ AS FSEQ,"
+        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID "
+      "FOR UPDATE";
+    utils::Timer t;
+
+    auto conn = m_connPool->getConn();
+    rdbms::AutoRollback autoRollback(conn);
+
+    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
+
+    const auto getConnTime = t.secs(utils::Timer::resetCounter);
+    auto selectStmt = conn.createStmt(selectSql);
+    const auto createStmtTime = t.secs();
+    selectStmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    t.reset();
+    rdbms::Rset selectRset = selectStmt.executeQuery();
+    const auto selectFromArchiveFileTime = t.secs();
+    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
+    std::set<std::string> vidsToSetDirty;
+    while(selectRset.next()) {
+      if(nullptr == archiveFile.get()) {
+        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
+
+        archiveFile->archiveFileID = selectRset.columnUint64("ARCHIVE_FILE_ID");
+        archiveFile->diskInstance = selectRset.columnString("DISK_INSTANCE_NAME");
+        archiveFile->diskFileId = selectRset.columnString("DISK_FILE_ID");
+        archiveFile->diskFileInfo.owner_uid = selectRset.columnUint64("DISK_FILE_UID");
+        archiveFile->diskFileInfo.gid = selectRset.columnUint64("DISK_FILE_GID");
+        archiveFile->fileSize = selectRset.columnUint64("SIZE_IN_BYTES");
+        archiveFile->checksumBlob.deserializeOrSetAdler32(selectRset.columnBlob("CHECKSUM_BLOB"), selectRset.columnUint64("CHECKSUM_ADLER32"));
+        archiveFile->storageClass = selectRset.columnString("STORAGE_CLASS_NAME");
+        archiveFile->creationTime = selectRset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
+        archiveFile->reconciliationTime = selectRset.columnUint64("RECONCILIATION_TIME");
+      }
+
+      // If there is a tape file
+      if(!selectRset.columnIsNull("VID")) {
+        // Add the tape file to the archive file's in-memory structure
+        common::dataStructures::TapeFile tapeFile;
+        tapeFile.vid = selectRset.columnString("VID");
+        vidsToSetDirty.insert(tapeFile.vid);
+        tapeFile.fSeq = selectRset.columnUint64("FSEQ");
+        tapeFile.blockId = selectRset.columnUint64("BLOCK_ID");
+        tapeFile.fileSize = selectRset.columnUint64("LOGICAL_SIZE_IN_BYTES");
+        tapeFile.copyNb = selectRset.columnUint8("COPY_NB");
+        tapeFile.creationTime = selectRset.columnUint64("TAPE_FILE_CREATION_TIME");
+        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
+        archiveFile->tapeFiles.push_back(tapeFile);
+      }
+    }
+
+    if(nullptr == archiveFile.get()) {
+      log::ScopedParamContainer spc(lc);
+      spc.add("fileId", archiveFileId);
+      lc.log(log::WARNING, "Ignoring request to delete archive file because it does not exist in the catalogue");
+      return;
+    }
+
+    if(diskInstanceName != archiveFile->diskInstance) {
+      log::ScopedParamContainer spc(lc);
+      spc.add("fileId", std::to_string(archiveFile->archiveFileID))
+         .add("diskInstance", archiveFile->diskInstance)
+         .add("requestDiskInstance", diskInstanceName)
+         .add("diskFileId", archiveFile->diskFileId)
+         .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
+         .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
+         .add("fileSize", std::to_string(archiveFile->fileSize))
+         .add("creationTime", std::to_string(archiveFile->creationTime))
+         .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
+         .add("storageClass", archiveFile->storageClass)
+         .add("getConnTime", getConnTime)
+         .add("createStmtTime", createStmtTime)
+         .add("selectFromArchiveFileTime", selectFromArchiveFileTime);
+      archiveFile->checksumBlob.addFirstChecksumToLog(spc);
+      for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
+        std::stringstream tapeCopyLogStream;
+        tapeCopyLogStream << "copy number: " << static_cast<int>(it->copyNb)
+          << " vid: " << it->vid
+          << " fSeq: " << it->fSeq
+          << " blockId: " << it->blockId
+          << " creationTime: " << it->creationTime
+          << " fileSize: " << it->fileSize
+          << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
+          << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
+        spc.add("TAPE FILE", tapeCopyLogStream.str());
+      }
+      lc.log(log::WARNING, "Failed to delete archive file because the disk instance of the request does not match that "
+        "of the archived file");
+
+      exception::UserError ue;
+      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because the disk instance of "
+        "the request does not match that of the archived file: archiveFileId=" << archiveFileId << " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" <<
+        archiveFile->diskInstance;
+      throw ue;
+    }
+
+    t.reset();
+    {
+      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+
+    const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter);
+
+    //Set the tapes where the files have been deleted to dirty
+    for(auto &vidToSetDirty: vidsToSetDirty){
+      RdbmsCatalogueUtils::setTapeDirty(conn,vidToSetDirty);
+    }
+
+    const auto setTapeDirtyTime = t.secs(utils::Timer::resetCounter);
+
+    {
+      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+    const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter);
+
+    conn.commit();
+    const auto commitTime = t.secs();
+
+    log::ScopedParamContainer spc(lc);
+    spc.add("fileId", std::to_string(archiveFile->archiveFileID))
+       .add("diskInstance", archiveFile->diskInstance)
+       .add("diskFileId", archiveFile->diskFileId)
+       .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
+       .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
+       .add("fileSize", std::to_string(archiveFile->fileSize))
+       .add("creationTime", std::to_string(archiveFile->creationTime))
+       .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
+       .add("storageClass", archiveFile->storageClass)
+       .add("getConnTime", getConnTime)
+       .add("createStmtTime", createStmtTime)
+       .add("selectFromArchiveFileTime", selectFromArchiveFileTime)
+       .add("deleteFromTapeFileTime", deleteFromTapeFileTime)
+       .add("setTapeDirtyTime",setTapeDirtyTime)
+       .add("deleteFromArchiveFileTime", deleteFromArchiveFileTime)
+       .add("commitTime", commitTime);
+    archiveFile->checksumBlob.addFirstChecksumToLog(spc);
+    for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
+      std::stringstream tapeCopyLogStream;
+      tapeCopyLogStream << "copy number: " << it->copyNb
+        << " vid: " << it->vid
+        << " fSeq: " << it->fSeq
+        << " blockId: " << it->blockId
+        << " creationTime: " << it->creationTime
+        << " fileSize: " << it->fileSize
+        << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
+        << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
+      spc.add("TAPE FILE", tapeCopyLogStream.str());
+    }
+    lc.log(log::INFO, "Archive file deleted from CTA catalogue");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    std::ostringstream msg;
+    msg << __FUNCTION__ << ": diskInstanceName=" << diskInstanceName <<",archiveFileId=" <<
+      archiveFileId << ": " << ex.getMessage().str();
+    ex.getMessage().str(msg.str());
+    throw;
+  }
+}
+
+uint64_t OracleArchiveFileCatalogue::getNextArchiveFileId(rdbms::Conn &conn) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE_ID_SEQ.NEXTVAL AS ARCHIVE_FILE_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("ARCHIVE_FILE_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void OracleArchiveFileCatalogue::copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+  const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc){
+  try {
+    utils::Timer t;
+    log::TimingList tl;
+    //We currently do an INSERT INTO, update and two DELETE FROM
+    //in a single transaction
+    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
+    const auto fileRecycleLog = static_cast<RdbmsFileRecycleLogCatalogue*>(m_rdbmsCatalogue->FileRecycleLog().get());
+    fileRecycleLog->copyArchiveFileToFileRecycleLog(conn,request);
+    tl.insertAndReset("insertToRecycleBinTime",t);
+    RdbmsCatalogueUtils::setTapeDirty(conn,request.archiveFileID);
+    tl.insertAndReset("setTapeDirtyTime",t);
+    const auto tapeFileCatalogue = static_cast<RdbmsTapeFileCatalogue*>(m_rdbmsCatalogue->TapeFile().get());
+    tapeFileCatalogue->deleteTapeFiles(conn,request);
+    tl.insertAndReset("deleteTapeFilesTime",t);
+    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
+    deleteArchiveFile(conn,request);
+    tl.insertAndReset("deleteArchiveFileTime",t);
+    log::ScopedParamContainer spc(lc);
+    spc.add("archiveFileId",request.archiveFileID);
+    spc.add("diskFileId",request.diskFileId);
+    spc.add("diskFilePath",request.diskFilePath);
+    spc.add("diskInstance",request.diskInstance);
+    tl.addToLog(spc);
+    lc.log(log::INFO,"In OracleCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// selectArchiveFileSizeAndChecksum
+//------------------------------------------------------------------------------
+std::map<uint64_t, OracleArchiveFileCatalogue::FileSizeAndChecksum>
+  OracleArchiveFileCatalogue::selectArchiveFileSizesAndChecksums(rdbms::Conn &conn,
+  const std::set<TapeFileWritten> &events) {
+  try {
+    std::vector<oracle::occi::Number> archiveFileIdList(events.size());
+    for (const auto &event: events) {
+      archiveFileIdList.push_back(oracle::occi::Number(event.archiveFileId));
+    }
+
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32 "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN TEMP_TAPE_FILE_INSERTION_BATCH ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TEMP_TAPE_FILE_INSERTION_BATCH.ARCHIVE_FILE_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+
+    std::map<uint64_t, FileSizeAndChecksum> fileSizesAndChecksums;
+    while (rset.next()) {
+      const uint64_t archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
+
+      if (fileSizesAndChecksums.end() != fileSizesAndChecksums.find(archiveFileId)) {
+        exception::Exception ex;
+        ex.getMessage() << __FUNCTION__ << " failed: "
+          "Found duplicate archive file identifier in batch of files written to tape: archiveFileId=" << archiveFileId;
+        throw ex;
+      }
+      FileSizeAndChecksum fileSizeAndChecksum;
+      fileSizeAndChecksum.fileSize = rset.columnUint64("SIZE_IN_BYTES");
+      fileSizeAndChecksum.checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
+      fileSizesAndChecksums[archiveFileId] = fileSizeAndChecksum;
+    }
+
+    return fileSizesAndChecksums;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp b/catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp
new file mode 100644
index 0000000000..188246d7bf
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp
@@ -0,0 +1,71 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "common/checksum/ChecksumBlob.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class TapeFileWritten;
+class RdbmsCatalogue;
+
+class OracleArchiveFileCatalogue : public RdbmsArchiveFileCatalogue {
+public:
+  OracleArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleArchiveFileCatalogue() override = default;
+
+  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
+    log::LogContext &lc) override;
+
+private:
+  uint64_t getNextArchiveFileId(rdbms::Conn &conn) override;
+
+  void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+    const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
+
+  /**
+   * The size and checksum of a file.
+   */
+  struct FileSizeAndChecksum {
+    uint64_t fileSize;
+    checksum::ChecksumBlob checksumBlob;
+  };
+
+  friend class OracleTapeFileCatalogue;
+  /**
+   * Returns the sizes and checksums of the specified archive files.
+   *
+   * @param conn The database connection.
+   * @param events The tape file written events that identify the archive files.
+   * @return A map from the identifier of each archive file to its size and checksum.
+   */
+  std::map<uint64_t, FileSizeAndChecksum> selectArchiveFileSizesAndChecksums(rdbms::Conn &conn,
+    const std::set<TapeFileWritten> &events);
+};  // class OracleArchiveFileCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleCatalogue.cpp b/catalogue/rdbms/oracle/OracleCatalogue.cpp
new file mode 100644
index 0000000000..8e79aacb56
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleCatalogue.cpp
@@ -0,0 +1,85 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleMediaTypeCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleStorageClassCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleTapeCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleTapePoolCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.hpp"
+#include "rdbms/Login.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleCatalogue::OracleCatalogue(
+  log::Logger &log,
+  const std::string &username,
+  const std::string &password,
+  const std::string &database,
+  const uint64_t nbConns,
+  const uint64_t nbArchiveFileListingConns):
+  RdbmsCatalogue(
+    log,
+    rdbms::Login(rdbms::Login::DBTYPE_ORACLE, username, password, database, "", 0),
+    nbConns,
+    nbArchiveFileListingConns) {
+  RdbmsCatalogue::m_fileRecycleLog = std::make_unique<OracleFileRecycleLogCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_storageClass = std::make_unique<OracleStorageClassCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_vo = std::make_unique<OracleVirtualOrganizationCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tapePool = std::make_unique<OracleTapePoolCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_mediaType = std::make_unique<OracleMediaTypeCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_logicalLibrary = std::make_unique<OracleLogicalLibraryCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tape = std::make_unique<OracleTapeCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_archiveFile = std::make_unique<OracleArchiveFileCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tapeFile = std::make_unique<OracleTapeFileCatalogue>(m_log, m_connPool, this);
+}
+
+std::string OracleCatalogue::createAndPopulateTempTableFxid(rdbms::Conn &conn,
+  const std::optional<std::vector<std::string>> &diskFileIds) const {
+  const std::string tempTableName = "ORA$PTT_DISK_FXIDS";
+
+  try {
+    if(diskFileIds) {
+      conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
+      std::string sql = "CREATE PRIVATE TEMPORARY TABLE " + tempTableName +
+        "(DISK_FILE_ID VARCHAR2(100))";
+      conn.executeNonQuery(sql);
+
+      sql = "INSERT INTO " + tempTableName + " VALUES(:DISK_FILE_ID)";
+      auto stmt = conn.createStmt(sql);
+      for(auto &diskFileId : diskFileIds.value()) {
+        stmt.bindString(":DISK_FILE_ID", diskFileId);
+        stmt.executeNonQuery();
+      }
+    }
+
+    return tempTableName;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleCatalogue.hpp b/catalogue/rdbms/oracle/OracleCatalogue.hpp
new file mode 100644
index 0000000000..17192f581f
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleCatalogue.hpp
@@ -0,0 +1,73 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * An Oracle based implementation of the CTA catalogue.
+ */
+class OracleCatalogue: public RdbmsCatalogue {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param log Object representing the API to the CTA logging system.
+   * @param username The database username.
+   * @param password The database password.
+   * @param database The database name.
+   * @param nbConns The maximum number of concurrent connections to the
+   * underlying relational database for all operations accept listing archive
+   * files which can be relatively long operations.
+   * @param nbArchiveFileListingConns The maximum number of concurrent
+   * connections to the underlying relational database for the sole purpose of
+   * listing archive files.
+   */
+  OracleCatalogue(
+    log::Logger       &log,
+    const std::string &username,
+    const std::string &password,
+    const std::string &database,
+    const uint64_t nbConns,
+    const uint64_t nbArchiveFileListingConns);
+
+  /**
+   * Destructor.
+   */
+  ~OracleCatalogue() override = default;
+
+  /**
+   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
+   *
+   * @param conn The database connection.
+   * @param diskFileIds List of disk file IDs (fxid).
+   * @return Name of the temporary table
+   */
+  std::string createAndPopulateTempTableFxid(rdbms::Conn &conn,
+    const std::optional<std::vector<std::string>> &diskFileIds) const override;
+}; // class OracleCatalogue
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.cpp b/catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.cpp
new file mode 100644
index 0000000000..99b1af1ecb
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.cpp
@@ -0,0 +1,117 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleFileRecycleLogCatalogue::OracleFileRecycleLogCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsFileRecycleLogCatalogue(log, connPool, rdbmsCatalogue) {}
+
+//------------------------------------------------------------------------------
+// restoreEntryInRecycleLog
+//------------------------------------------------------------------------------
+void OracleFileRecycleLogCatalogue::restoreEntryInRecycleLog(rdbms::Conn & conn,
+  FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) {
+  try {
+    utils::Timer timer;
+    log::TimingList timingList;
+
+    if (!fileRecycleLogItor.hasMore()) {
+      throw cta::exception::UserError("No file in the recycle bin matches the parameters passed");
+    }
+    auto fileRecycleLog = fileRecycleLogItor.next();
+    if (fileRecycleLogItor.hasMore()) {
+      // stop restoring more than one file at once
+      throw cta::exception::UserError("More than one recycle bin file matches the parameters passed");
+    }
+    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
+
+    const auto archiveFileCatalogue = static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+    if (auto archiveFilePtr = archiveFileCatalogue->getArchiveFileById(conn, fileRecycleLog.archiveFileId);
+      !archiveFilePtr) {
+      RdbmsFileRecycleLogCatalogue::restoreArchiveFileInRecycleLog(conn, fileRecycleLog, newFid, lc);
+    } else {
+      if (archiveFilePtr->tapeFiles.find(fileRecycleLog.copyNb) != archiveFilePtr->tapeFiles.end()) {
+        // copy with same copy_nb exists, cannot restore
+        UserSpecifiedExistingDeletedFileCopy ex;
+        ex.getMessage() << "Cannot restore file copy with archiveFileId "
+          << std::to_string(fileRecycleLog.archiveFileId)
+          << " and copy_nb " << std::to_string(fileRecycleLog.copyNb)
+          << " because a tapefile with same archiveFileId and copy_nb already exists";
+        throw ex;
+      }
+    }
+
+    RdbmsFileRecycleLogCatalogue::restoreFileCopyInRecycleLog(conn, fileRecycleLog, lc);
+
+    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
+    conn.commit();
+
+    log::ScopedParamContainer spc(lc);
+    timingList.insertAndReset("commitTime", timer);
+    timingList.addToLog(spc);
+    lc.log(log::INFO, "In OracleFileRecycleLogCatalogue::restoreEntryInRecycleLog: "
+      "all file copies successfully restored.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t OracleFileRecycleLogCatalogue::getNextFileRecyleLogId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "FILE_RECYCLE_LOG_ID_SEQ.NEXTVAL AS FILE_RECYCLE_LOG_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("FILE_RECYCLE_LOG_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.hpp b/catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.hpp
new file mode 100644
index 0000000000..51d6c4466b
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleFileRecycleLogCatalogue.hpp
@@ -0,0 +1,61 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+
+namespace cta {
+
+namespace utils {
+class Timer;
+}
+
+namespace log {
+class TimingList;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleFileRecycleLogCatalogue : public RdbmsFileRecycleLogCatalogue {
+public:
+  OracleFileRecycleLogCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleFileRecycleLogCatalogue() override = default;
+
+private:
+  void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid,
+    log::LogContext & lc) override;
+
+  /**
+   * Copy the fileRecycleLog to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entry
+   * @param conn the database connection
+   * @param fileRecycleLog the fileRecycleLog we want to restore
+   * @param lc the log context
+   */
+  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLog,
+    log::LogContext & lc);
+
+  uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) const override;
+};  // class SqliteFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.cpp b/catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.cpp
new file mode 100644
index 0000000000..af74b94a1c
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.cpp
@@ -0,0 +1,54 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleLogicalLibraryCatalogue::OracleLogicalLibraryCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsLogicalLibraryCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t OracleLogicalLibraryCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LOGICAL_LIBRARY_ID_SEQ.NEXTVAL AS LOGICAL_LIBRARY_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("LOGICAL_LIBRARY_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.hpp b/catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.hpp
new file mode 100644
index 0000000000..9b9396e33c
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleLogicalLibraryCatalogue.hpp
@@ -0,0 +1,40 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleLogicalLibraryCatalogue : public RdbmsLogicalLibraryCatalogue {
+public:
+  OracleLogicalLibraryCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleLogicalLibraryCatalogue() override = default;
+
+private:
+  uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) const override;
+};  // class SqliteFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleMediaTypeCatalogue.cpp b/catalogue/rdbms/oracle/OracleMediaTypeCatalogue.cpp
new file mode 100644
index 0000000000..1f62d03395
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleMediaTypeCatalogue.cpp
@@ -0,0 +1,54 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/oracle/OracleMediaTypeCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleMediaTypeCatalogue::OracleMediaTypeCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsMediaTypeCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t OracleMediaTypeCatalogue::getNextMediaTypeId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "MEDIA_TYPE_ID_SEQ.NEXTVAL AS MEDIA_TYPE_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("MEDIA_TYPE_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleMediaTypeCatalogue.hpp b/catalogue/rdbms/oracle/OracleMediaTypeCatalogue.hpp
new file mode 100644
index 0000000000..3dbdf2f3c8
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleMediaTypeCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleMediaTypeCatalogue : public RdbmsMediaTypeCatalogue {
+public:
+  OracleMediaTypeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleMediaTypeCatalogue() override = default;
+
+private:
+  uint64_t getNextMediaTypeId(rdbms::Conn &conn) const override;
+};  // class PostgresMediaTypeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleStorageClassCatalogue.cpp b/catalogue/rdbms/oracle/OracleStorageClassCatalogue.cpp
new file mode 100644
index 0000000000..f11220edde
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleStorageClassCatalogue.cpp
@@ -0,0 +1,54 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/oracle/OracleStorageClassCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleStorageClassCatalogue::OracleStorageClassCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsStorageClassCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t OracleStorageClassCatalogue::getNextStorageClassId(rdbms::Conn &conn) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "STORAGE_CLASS_ID_SEQ.NEXTVAL AS STORAGE_CLASS_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("STORAGE_CLASS_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleStorageClassCatalogue.hpp b/catalogue/rdbms/oracle/OracleStorageClassCatalogue.hpp
new file mode 100644
index 0000000000..cbf05b895c
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleStorageClassCatalogue.hpp
@@ -0,0 +1,42 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleStorageClassCatalogue : public RdbmsStorageClassCatalogue {
+public:
+  OracleStorageClassCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleStorageClassCatalogue() override = default;
+
+private:
+  uint64_t getNextStorageClassId(rdbms::Conn &conn) override;
+};  // class PostgresStorageClassCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleTapeCatalogue.cpp b/catalogue/rdbms/oracle/OracleTapeCatalogue.cpp
new file mode 100644
index 0000000000..3750928aeb
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleTapeCatalogue.cpp
@@ -0,0 +1,58 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/oracle/OracleTapeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleTapeCatalogue::OracleTapeCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsTapeCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t OracleTapeCatalogue::getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LAST_FSEQ AS LAST_FSEQ "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if(rset.next()) {
+      return rset.columnUint64("LAST_FSEQ");
+    } else {
+      throw exception::Exception(std::string("No such tape with vid=") + vid);
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleTapeCatalogue.hpp b/catalogue/rdbms/oracle/OracleTapeCatalogue.hpp
new file mode 100644
index 0000000000..fc0f460e54
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleTapeCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapeCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleTapeCatalogue : public RdbmsTapeCatalogue {
+public:
+  OracleTapeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleTapeCatalogue() override = default;
+
+private:
+  uint64_t getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const override;
+};  // class OracleTapeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleTapeFileCatalogue.cpp b/catalogue/rdbms/oracle/OracleTapeFileCatalogue.cpp
new file mode 100644
index 0000000000..6ca4f0cd79
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleTapeFileCatalogue.cpp
@@ -0,0 +1,619 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <string>
+
+#include "catalogue/InsertFileRecycleLog.hpp"
+#include "catalogue/rdbms/oracle/OracleArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/TapeItemWritten.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "common/checksum/ChecksumBlob.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/FileSizeMismatch.hpp"
+#include "common/exception/LostDatabaseConnection.hpp"
+#include "common/exception/TapeFseqMismatch.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+#include "rdbms/rdbms.hpp"
+#include "rdbms/wrapper/OcciColumn.hpp"
+#include "rdbms/wrapper/OcciStmt.hpp"
+
+namespace cta {
+namespace catalogue {
+
+namespace {
+/**
+ * Structure used to assemble a batch of rows to insert into the TAPE_FILE
+ * table.
+ */
+struct TapeFileBatch {
+  size_t nbRows;
+  rdbms::wrapper::OcciColumn vid;
+  rdbms::wrapper::OcciColumn fSeq;
+  rdbms::wrapper::OcciColumn blockId;
+  rdbms::wrapper::OcciColumn fileSize;
+  rdbms::wrapper::OcciColumn copyNb;
+  rdbms::wrapper::OcciColumn creationTime;
+  rdbms::wrapper::OcciColumn archiveFileId;
+
+  /**
+   * Constructor.
+   *
+   * @param nbRowsValue  The Number of rows to be inserted.
+   */
+  TapeFileBatch(const size_t nbRowsValue):
+    nbRows(nbRowsValue),
+    vid("VID", nbRows),
+    fSeq("FSEQ", nbRows),
+    blockId("BLOCK_ID", nbRows),
+    fileSize("LOGICAL_SIZE_IN_BYTES", nbRows),
+    copyNb("COPY_NB", nbRows),
+    creationTime("CREATION_TIME", nbRows),
+    archiveFileId("ARCHIVE_FILE_ID", nbRows) {
+  }
+}; // struct TapeFileBatch
+
+/**
+ * Structure used to assemble a batch of rows to insert into the ARCHIVE_FILE
+ * table.
+ */
+struct ArchiveFileBatch {
+  size_t nbRows;
+  rdbms::wrapper::OcciColumn archiveFileId;
+  rdbms::wrapper::OcciColumn diskInstance;
+  rdbms::wrapper::OcciColumn diskFileId;
+  rdbms::wrapper::OcciColumn diskFileUser;
+  rdbms::wrapper::OcciColumn diskFileGroup;
+  rdbms::wrapper::OcciColumn size;
+  rdbms::wrapper::OcciColumn checksumBlob;
+  rdbms::wrapper::OcciColumn checksumAdler32;
+  rdbms::wrapper::OcciColumn storageClassName;
+  rdbms::wrapper::OcciColumn creationTime;
+  rdbms::wrapper::OcciColumn reconciliationTime;
+
+  /**
+   * Constructor.
+   *
+   * @param nbRowsValue  The Number of rows to be inserted.
+   */
+  ArchiveFileBatch(const size_t nbRowsValue):
+    nbRows(nbRowsValue),
+    archiveFileId("ARCHIVE_FILE_ID", nbRows),
+    diskInstance("DISK_INSTANCE_NAME", nbRows),
+    diskFileId("DISK_FILE_ID", nbRows),
+    diskFileUser("DISK_FILE_UID", nbRows),
+    diskFileGroup("DISK_FILE_GID", nbRows),
+    size("SIZE_IN_BYTES", nbRows),
+    checksumBlob("CHECKSUM_BLOB", nbRows),
+    checksumAdler32("CHECKSUM_ADLER32", nbRows),
+    storageClassName("STORAGE_CLASS_NAME", nbRows),
+    creationTime("CREATION_TIME", nbRows),
+    reconciliationTime("RECONCILIATION_TIME", nbRows) {
+  }
+}; // struct ArchiveFileBatch
+
+/**
+ * Structure used to assemble a batch of rows to insert into the
+ * TAPE_FILE_BATCH temporary table.
+ */
+struct TempTapeFileBatch {
+  size_t nbRows;
+  rdbms::wrapper::OcciColumn archiveFileId;
+
+  /**
+   * Constructor.
+   *
+   * @param nbRowsValue  The Number of rows to be inserted.
+   */
+  TempTapeFileBatch(const size_t nbRowsValue):
+    nbRows(nbRowsValue),
+    archiveFileId("ARCHIVE_FILE_ID", nbRows) {
+  }
+}; // struct TempTapeFileBatch
+
+} // anonymous namespace
+
+OracleTapeFileCatalogue::OracleTapeFileCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue)
+  : RdbmsTapeFileCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void OracleTapeFileCatalogue::copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+  const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+  log::TimingList *timingList, log::LogContext & lc) const {
+  conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
+  const auto fileRecycleLogCatalogue = static_cast<RdbmsFileRecycleLogCatalogue*>(
+    RdbmsTapeFileCatalogue::m_rdbmsCatalogue->FileRecycleLog().get());
+  fileRecycleLogCatalogue->copyTapeFilesToFileRecycleLog(conn, file, reason);
+  timingList->insertAndReset("insertToRecycleBinTime", *timer);
+  RdbmsCatalogueUtils::setTapeDirty(conn, file.archiveFileID);
+  timingList->insertAndReset("setTapeDirtyTime", *timer);
+  deleteTapeFiles(conn, file);
+  timingList->insertAndReset("deleteTapeFilesTime", *timer);
+  conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
+  conn.commit();
+}
+
+void OracleTapeFileCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) {
+  try {
+    if (events.empty()) {
+      return;
+    }
+
+    auto firstEventItor = events.begin();
+    const auto &firstEvent = *(*firstEventItor);
+    checkTapeItemWrittenFieldsAreSet(__FUNCTION__, firstEvent);
+    const time_t now = time(nullptr);
+    threading::MutexLocker locker(m_rdbmsCatalogue->m_mutex);
+    auto conn = m_connPool->getConn();
+    rdbms::AutoRollback autoRollback(conn);
+
+    conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_OFF);
+
+    const uint64_t lastFSeq = selectTapeForUpdateAndGetLastFSeq(conn, firstEvent.vid);
+    uint64_t expectedFSeq = lastFSeq + 1;
+    uint64_t totalLogicalBytesWritten = 0;
+
+    uint32_t i = 0;
+    // We have a mix of files and items. Only files will be recorded, but items
+    // allow checking fSeq coherency.
+    // determine the number of files
+    size_t filesCount=std::count_if(events.cbegin(), events.cend(),
+        [](const TapeItemWrittenPointer &e) -> bool {return typeid(*e)==typeid(TapeFileWritten);});
+    TapeFileBatch tapeFileBatch(filesCount);
+
+    std::set<TapeFileWritten> fileEvents;
+
+    for (const auto &eventP: events) {
+      // Check for all item types.
+      const auto &event = *eventP;
+      checkTapeItemWrittenFieldsAreSet(__FUNCTION__, event);
+
+      if (event.vid != firstEvent.vid) {
+        throw exception::Exception(std::string("VID mismatch: expected=") + firstEvent.vid + " actual=" + event.vid);
+      }
+
+      if (expectedFSeq != event.fSeq) {
+        exception::TapeFseqMismatch ex;
+        ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
+          event.fSeq;
+        throw ex;
+      }
+      expectedFSeq++;
+
+      try {
+        // If this is a file (as opposed to a placeholder), do the full processing.
+        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(event);
+
+        checkTapeFileWrittenFieldsAreSet(__FUNCTION__, fileEvent);
+
+        totalLogicalBytesWritten += fileEvent.size;
+
+        // Store the length of each field and implicitly calculate the maximum field
+        // length of each column
+        tapeFileBatch.vid.setFieldLenToValueLen(i, fileEvent.vid);
+        tapeFileBatch.fSeq.setFieldLenToValueLen(i, fileEvent.fSeq);
+        tapeFileBatch.blockId.setFieldLenToValueLen(i, fileEvent.blockId);
+        tapeFileBatch.fileSize.setFieldLenToValueLen(i, fileEvent.size);
+        tapeFileBatch.copyNb.setFieldLenToValueLen(i, fileEvent.copyNb);
+        tapeFileBatch.creationTime.setFieldLenToValueLen(i, now);
+        tapeFileBatch.archiveFileId.setFieldLenToValueLen(i, fileEvent.archiveFileId);
+
+        fileEvents.insert(fileEvent);
+        i++;
+      } catch (std::bad_cast&) {}
+    }
+
+    // Store the value of each field
+    i = 0;
+    for (const auto &event: fileEvents) {
+      tapeFileBatch.vid.setFieldValue(i, event.vid);
+      tapeFileBatch.fSeq.setFieldValue(i, event.fSeq);
+      tapeFileBatch.blockId.setFieldValue(i, event.blockId);
+      tapeFileBatch.fileSize.setFieldValue(i, event.size);
+      tapeFileBatch.copyNb.setFieldValue(i, event.copyNb);
+      tapeFileBatch.creationTime.setFieldValue(i, now);
+      tapeFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
+      i++;
+    }
+
+    // Update the tape because all the necessary information is now available
+    auto lastEventItor = events.cend();
+    lastEventItor--;
+    const TapeItemWritten &lastEvent = **lastEventItor;
+    RdbmsCatalogueUtils::updateTape(conn, lastEvent.vid, lastEvent.fSeq, totalLogicalBytesWritten, filesCount,
+      lastEvent.tapeDrive);
+
+    // If we had only placeholders and no file recorded, we are done (but we still commit the update of the tape's fSeq).
+    if (fileEvents.empty()) {
+      conn.commit();
+      return;
+    }
+
+    // Create the archive file entries, skipping those that already exist
+    idempotentBatchInsertArchiveFiles(conn, fileEvents);
+
+    {
+      const char *const sql =
+        "INSERT INTO TEMP_TAPE_FILE_INSERTION_BATCH(" "\n"
+          "VID,"                                      "\n"
+          "FSEQ,"                                     "\n"
+          "BLOCK_ID,"                                 "\n"
+          "LOGICAL_SIZE_IN_BYTES,"                    "\n"
+          "COPY_NB,"                                  "\n"
+          "CREATION_TIME,"                            "\n"
+          "ARCHIVE_FILE_ID)"                          "\n"
+        "VALUES("                                     "\n"
+          ":VID,"                                     "\n"
+          ":FSEQ,"                                    "\n"
+          ":BLOCK_ID,"                                "\n"
+          ":LOGICAL_SIZE_IN_BYTES,"                   "\n"
+          ":COPY_NB,"                                 "\n"
+          ":CREATION_TIME,"                           "\n"
+          ":ARCHIVE_FILE_ID)"                         "\n";
+      auto stmt = conn.createStmt(sql);
+      rdbms::wrapper::OcciStmt &occiStmt = dynamic_cast<rdbms::wrapper::OcciStmt &>(stmt.getStmt());
+      occiStmt.setColumn(tapeFileBatch.vid);
+      occiStmt.setColumn(tapeFileBatch.fSeq);
+      occiStmt.setColumn(tapeFileBatch.blockId);
+      occiStmt.setColumn(tapeFileBatch.fileSize);
+      occiStmt.setColumn(tapeFileBatch.copyNb);
+      occiStmt.setColumn(tapeFileBatch.creationTime);
+      occiStmt.setColumn(tapeFileBatch.archiveFileId);
+      try {
+        occiStmt->executeArrayUpdate(tapeFileBatch.nbRows);
+      } catch(oracle::occi::SQLException &ex) {
+        std::ostringstream msg;
+        msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
+          ex.what();
+
+        if(rdbms::wrapper::OcciStmt::connShouldBeClosed(ex)) {
+          // Close the statement first and then the connection
+          try {
+            occiStmt.close();
+          } catch(...) {
+          }
+
+          try {
+            conn.closeUnderlyingStmtsAndConn();
+          } catch(...) {
+          }
+          throw exception::LostDatabaseConnection(msg.str());
+        }
+        throw exception::Exception(msg.str());
+      } catch(std::exception &se) {
+        std::ostringstream msg;
+        msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
+          se.what();
+
+        throw exception::Exception(msg.str());
+      }
+    }
+
+    // Verify that the archive file entries in the catalogue database agree with
+    // the tape file written events
+    const auto archiveFileCatalogue = static_cast<OracleArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+    const auto fileSizesAndChecksums = archiveFileCatalogue->selectArchiveFileSizesAndChecksums(conn, fileEvents);
+    for (const auto &event: fileEvents) {
+      const auto fileSizeAndChecksumItor = fileSizesAndChecksums.find(event.archiveFileId);
+
+      std::ostringstream fileContext;
+      fileContext << "archiveFileId=" << event.archiveFileId << ", diskInstanceName=" << event.diskInstance <<
+        ", diskFileId=" << event.diskFileId;
+
+      // This should never happen
+      if(fileSizesAndChecksums.end() == fileSizeAndChecksumItor) {
+        exception::Exception ex;
+        ex.getMessage() << __FUNCTION__ << ": Failed to find archive file entry in the catalogue: " << fileContext.str();
+        throw ex;
+      }
+
+      const auto &fileSizeAndChecksum = fileSizeAndChecksumItor->second;
+
+      if(fileSizeAndChecksum.fileSize != event.size) {
+        catalogue::FileSizeMismatch ex;
+        ex.getMessage() << __FUNCTION__ << ": File size mismatch: expected=" << fileSizeAndChecksum.fileSize <<
+          ", actual=" << event.size << ": " << fileContext.str();
+        throw ex;
+      }
+
+      fileSizeAndChecksum.checksumBlob.validate(event.checksumBlob);
+    }
+
+    std::list<InsertFileRecycleLog> recycledFiles = insertOldCopiesOfFilesIfAnyOnFileRecycleLog(conn);
+
+    {
+      const char *const sql =
+        "INSERT INTO TAPE_FILE (VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES, "
+           "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID) "
+        "SELECT VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES, "
+           "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID FROM TEMP_TAPE_FILE_INSERTION_BATCH";
+      auto stmt = conn.createStmt(sql);
+      stmt.executeNonQuery();
+    }
+
+    for(auto & recycledFile: recycledFiles){
+      const char *const sql =
+        "DELETE FROM "
+          "TAPE_FILE "
+        "WHERE "
+          "TAPE_FILE.VID = :VID AND TAPE_FILE.FSEQ = :FSEQ";
+
+      auto stmt = conn.createStmt(sql);
+      stmt.bindString(":VID",recycledFile.vid);
+      stmt.bindUint64(":FSEQ",recycledFile.fSeq);
+      stmt.executeNonQuery();
+    }
+
+    {
+      conn.setAutocommitMode(rdbms::AutocommitMode::AUTOCOMMIT_ON);
+      conn.commit();
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t OracleTapeFileCatalogue::selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn,
+  const std::string &vid) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LAST_FSEQ AS LAST_FSEQ "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID "
+      "FOR UPDATE";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("The tape with VID " + vid + " does not exist"));
+    }
+
+    return rset.columnUint64("LAST_FSEQ");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void OracleTapeFileCatalogue::idempotentBatchInsertArchiveFiles(rdbms::Conn &conn,
+  const std::set<TapeFileWritten> &events) {
+  try {
+    ArchiveFileBatch archiveFileBatch(events.size());
+    const time_t now = time(nullptr);
+    std::vector<uint32_t> adler32(events.size());
+
+    // Store the length of each field and implicitly calculate the maximum field length of each column
+    uint32_t i = 0;
+    for (const auto &event: events) {
+      // Keep transition ADLER32 checksum column up-to-date with the ChecksumBlob
+      try {
+        std::string adler32hex = checksum::ChecksumBlob::ByteArrayToHex(event.checksumBlob.at(checksum::ADLER32));
+        adler32[i] = strtoul(adler32hex.c_str(), 0, 16);
+      } catch(exception::ChecksumTypeMismatch &ex) {
+        // No ADLER32 checksum exists in the checksumBlob
+        adler32[i] = 0;
+      }
+
+      archiveFileBatch.archiveFileId.setFieldLenToValueLen(i, event.archiveFileId);
+      archiveFileBatch.diskInstance.setFieldLenToValueLen(i, event.diskInstance);
+      archiveFileBatch.diskFileId.setFieldLenToValueLen(i, event.diskFileId);
+      archiveFileBatch.diskFileUser.setFieldLenToValueLen(i, event.diskFileOwnerUid);
+      archiveFileBatch.diskFileGroup.setFieldLenToValueLen(i, event.diskFileGid);
+      archiveFileBatch.size.setFieldLenToValueLen(i, event.size);
+      archiveFileBatch.checksumBlob.setFieldLen(i, 2 + event.checksumBlob.length());
+      archiveFileBatch.checksumAdler32.setFieldLenToValueLen(i, adler32[i]);
+      archiveFileBatch.storageClassName.setFieldLenToValueLen(i, event.storageClassName);
+      archiveFileBatch.creationTime.setFieldLenToValueLen(i, now);
+      archiveFileBatch.reconciliationTime.setFieldLenToValueLen(i, now);
+      i++;
+    }
+
+    // Store the value of each field
+    i = 0;
+    for (const auto &event: events) {
+      archiveFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
+      archiveFileBatch.diskInstance.setFieldValue(i, event.diskInstance);
+      archiveFileBatch.diskFileId.setFieldValue(i, event.diskFileId);
+      archiveFileBatch.diskFileUser.setFieldValue(i, event.diskFileOwnerUid);
+      archiveFileBatch.diskFileGroup.setFieldValue(i, event.diskFileGid);
+      archiveFileBatch.size.setFieldValue(i, event.size);
+      archiveFileBatch.checksumBlob.setFieldValueToRaw(i, event.checksumBlob.serialize());
+      archiveFileBatch.checksumAdler32.setFieldValue(i, adler32[i]);
+      archiveFileBatch.storageClassName.setFieldValue(i, event.storageClassName);
+      archiveFileBatch.creationTime.setFieldValue(i, now);
+      archiveFileBatch.reconciliationTime.setFieldValue(i, now);
+      i++;
+    }
+
+    const char *const sql =
+      "INSERT INTO ARCHIVE_FILE("
+        "ARCHIVE_FILE_ID,"
+        "DISK_INSTANCE_NAME,"
+        "DISK_FILE_ID,"
+        "DISK_FILE_UID,"
+        "DISK_FILE_GID,"
+        "SIZE_IN_BYTES,"
+        "CHECKSUM_BLOB,"
+        "CHECKSUM_ADLER32,"
+        "STORAGE_CLASS_ID,"
+        "CREATION_TIME,"
+        "RECONCILIATION_TIME)"
+      "SELECT "
+        ":ARCHIVE_FILE_ID,"
+        ":DISK_INSTANCE_NAME,"
+        ":DISK_FILE_ID,"
+        ":DISK_FILE_UID,"
+        ":DISK_FILE_GID,"
+        ":SIZE_IN_BYTES,"
+        ":CHECKSUM_BLOB,"
+        ":CHECKSUM_ADLER32,"
+        "STORAGE_CLASS_ID,"
+        ":CREATION_TIME,"
+        ":RECONCILIATION_TIME "
+      "FROM "
+        "STORAGE_CLASS "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+    auto stmt = conn.createStmt(sql);
+    rdbms::wrapper::OcciStmt &occiStmt = dynamic_cast<rdbms::wrapper::OcciStmt &>(stmt.getStmt());
+    occiStmt->setBatchErrorMode(true);
+
+    occiStmt.setColumn(archiveFileBatch.archiveFileId);
+    occiStmt.setColumn(archiveFileBatch.diskInstance);
+    occiStmt.setColumn(archiveFileBatch.diskFileId);
+    occiStmt.setColumn(archiveFileBatch.diskFileUser);
+    occiStmt.setColumn(archiveFileBatch.diskFileGroup);
+    occiStmt.setColumn(archiveFileBatch.size);
+    occiStmt.setColumn(archiveFileBatch.checksumBlob, oracle::occi::OCCI_SQLT_VBI);
+    occiStmt.setColumn(archiveFileBatch.checksumAdler32);
+    occiStmt.setColumn(archiveFileBatch.storageClassName);
+    occiStmt.setColumn(archiveFileBatch.creationTime);
+    occiStmt.setColumn(archiveFileBatch.reconciliationTime);
+
+    try {
+      occiStmt->executeArrayUpdate(archiveFileBatch.nbRows);
+    } catch(oracle::occi::BatchSQLException &be) {
+      const unsigned int nbFailedRows = be.getFailedRowCount();
+      exception::Exception ex;
+      ex.getMessage() << "Caught a BatchSQLException" << nbFailedRows;
+      bool foundErrorOtherThanUniqueConstraint = false;
+      for (unsigned int row = 0; row < nbFailedRows; row++ ) {
+        oracle::occi::SQLException err = be.getException(row);
+        const unsigned int rowIndex = be.getRowNum(row);
+        const int errorCode = err.getErrorCode();
+
+        // If the error is anything other than a unique constraint error
+        if(1 != errorCode) {
+          foundErrorOtherThanUniqueConstraint = true;
+          ex.getMessage() << ": Row " << rowIndex << " generated ORA error " << errorCode;
+        }
+      }
+      if (foundErrorOtherThanUniqueConstraint) {
+        throw ex;
+      }
+    } catch(oracle::occi::SQLException &ex) {
+      std::ostringstream msg;
+      msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
+        ex.what();
+
+      if(rdbms::wrapper::OcciStmt::connShouldBeClosed(ex)) {
+        // Close the statement first and then the connection
+        try {
+          occiStmt.close();
+        } catch(...) {
+        }
+
+        try {
+          conn.closeUnderlyingStmtsAndConn();
+        } catch(...) {
+        }
+        throw exception::LostDatabaseConnection(msg.str());
+      }
+      throw exception::Exception(msg.str());
+    } catch(std::exception &se) {
+      std::ostringstream msg;
+      msg << std::string(__FUNCTION__) << " failed for SQL statement " << rdbms::getSqlForException(sql) << ": " <<
+        se.what();
+
+      throw exception::Exception(msg.str());
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+
+std::list<cta::catalogue::InsertFileRecycleLog> OracleTapeFileCatalogue::insertOldCopiesOfFilesIfAnyOnFileRecycleLog(
+  rdbms::Conn& conn) {
+  std::list<cta::catalogue::InsertFileRecycleLog> fileRecycleLogsToInsert;
+  try {
+    //Get the TAPE_FILE entry to put on the file recycle log
+    {
+      const char *const sql =
+        "SELECT "
+          "TAPE_FILE.VID AS VID,"
+          "TAPE_FILE.FSEQ AS FSEQ,"
+          "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+          "TAPE_FILE.COPY_NB AS COPY_NB,"
+          "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
+          "TAPE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
+        "FROM "
+          "TAPE_FILE "
+        "JOIN "
+          "TEMP_TAPE_FILE_INSERTION_BATCH "
+        "ON "
+          "TEMP_TAPE_FILE_INSERTION_BATCH.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID AND TEMP_TAPE_FILE_INSERTION_BATCH.COPY_NB = TAPE_FILE.COPY_NB "
+        "WHERE "
+          "TAPE_FILE.VID != TEMP_TAPE_FILE_INSERTION_BATCH.VID OR TAPE_FILE.FSEQ != TEMP_TAPE_FILE_INSERTION_BATCH.FSEQ";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      while(rset.next()){
+        cta::catalogue::InsertFileRecycleLog fileRecycleLog;
+        fileRecycleLog.vid = rset.columnString("VID");
+        fileRecycleLog.fSeq = rset.columnUint64("FSEQ");
+        fileRecycleLog.blockId = rset.columnUint64("BLOCK_ID");
+        fileRecycleLog.copyNb = rset.columnUint8("COPY_NB");
+        fileRecycleLog.tapeFileCreationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
+        fileRecycleLog.archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
+        fileRecycleLog.reasonLog = InsertFileRecycleLog::getRepackReasonLog();
+        fileRecycleLog.recycleLogTime = time(nullptr);
+        fileRecycleLogsToInsert.push_back(fileRecycleLog);
+      }
+    }
+    {
+      for(auto & fileRecycleLog: fileRecycleLogsToInsert){
+        const auto fileRecycleLogCatalogue
+          = static_cast<RdbmsFileRecycleLogCatalogue*>(m_rdbmsCatalogue->FileRecycleLog().get());
+        fileRecycleLogCatalogue->insertFileInFileRecycleLog(conn, fileRecycleLog);
+      }
+    }
+    return fileRecycleLogsToInsert;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleTapeFileCatalogue.hpp b/catalogue/rdbms/oracle/OracleTapeFileCatalogue.hpp
new file mode 100644
index 0000000000..e60ec5d889
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleTapeFileCatalogue.hpp
@@ -0,0 +1,88 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "common/checksum/ChecksumBlob.hpp"
+
+namespace cta {
+
+namespace utils {
+class Timer;
+}
+
+namespace log {
+class TimingList;
+}
+
+namespace catalogue {
+
+class InsertFileRecycleLog;
+class RdbmsCatalogue;
+
+class OracleTapeFileCatalogue : public RdbmsTapeFileCatalogue {
+public:
+  OracleTapeFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+  ~OracleTapeFileCatalogue() override = default;
+
+  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override;
+
+private:
+  void  copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+    const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+    log::TimingList *timingList, log::LogContext & lc) const override;
+
+  uint64_t selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn, const std::string &vid);
+
+  /**
+   * Batch inserts rows into the ARCHIVE_FILE table that correspond to the
+   * specified TapeFileWritten events.
+   *
+   * This method has idempotent behaviour in the case where an ARCHIVE_FILE
+   * already exists.  Such a situation will occur when a file has more than one
+   * copy on tape.  The first tape copy will cause two successful inserts, one
+   * into the ARCHIVE_FILE table and one into the  TAPE_FILE table.  The second
+   * tape copy will try to do the same, but the insert into the ARCHIVE_FILE
+   * table will fail or simply bounce as the row will already exists.  The
+   * insert into the TABLE_FILE table will succeed because the two TAPE_FILE
+   * rows will be unique.
+   *
+   * @param conn The database connection.
+   * @param events The tape file written events.
+   */
+  void idempotentBatchInsertArchiveFiles(rdbms::Conn &conn, const std::set<TapeFileWritten> &events);
+
+  /**
+   * In the case we insert a TAPE_FILE that already has a copy on the catalogue (same copyNb),
+   * this TAPE_FILE will go to the FILE_RECYCLE_LOG table.
+   *
+   * This case happens always during the repacking of a tape: the new TAPE_FILE created
+   * will replace the old one, the old one will then be moved to the FILE_RECYCLE_LOG table
+   *
+   * @param conn The database connection.
+   * @returns the list of inserted fileRecycleLog
+   */
+  std::list<cta::catalogue::InsertFileRecycleLog> insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn & conn);
+
+};  // class OracleTapeFileCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleTapePoolCatalogue.cpp b/catalogue/rdbms/oracle/OracleTapePoolCatalogue.cpp
new file mode 100644
index 0000000000..4dde1f6b93
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleTapePoolCatalogue.cpp
@@ -0,0 +1,55 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/oracle/OracleTapePoolCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleTapePoolCatalogue::OracleTapePoolCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsTapePoolCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t OracleTapePoolCatalogue::getNextTapePoolId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "TAPE_POOL_ID_SEQ.NEXTVAL AS TAPE_POOL_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("TAPE_POOL_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleTapePoolCatalogue.hpp b/catalogue/rdbms/oracle/OracleTapePoolCatalogue.hpp
new file mode 100644
index 0000000000..857291cd8d
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleTapePoolCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapePoolCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleTapePoolCatalogue : public RdbmsTapePoolCatalogue {
+public:
+  OracleTapePoolCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleTapePoolCatalogue() override = default;
+
+private:
+  uint64_t getNextTapePoolId(rdbms::Conn &conn) const override;
+};  // class PostgresMediaTypeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.cpp b/catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.cpp
new file mode 100644
index 0000000000..3c1ca532b4
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.cpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+OracleVirtualOrganizationCatalogue::OracleVirtualOrganizationCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsVirtualOrganizationCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t OracleVirtualOrganizationCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_ID_SEQ.NEXTVAL AS VIRTUAL_ORGANIZATION_ID "
+      "FROM "
+        "DUAL";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("Result set is unexpectedly empty"));
+    }
+
+    return rset.columnUint64("VIRTUAL_ORGANIZATION_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.hpp b/catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.hpp
new file mode 100644
index 0000000000..c8e46e9b12
--- /dev/null
+++ b/catalogue/rdbms/oracle/OracleVirtualOrganizationCatalogue.hpp
@@ -0,0 +1,39 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class OracleVirtualOrganizationCatalogue : public RdbmsVirtualOrganizationCatalogue {
+public:
+  OracleVirtualOrganizationCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~OracleVirtualOrganizationCatalogue() override = default;
+
+private:
+  uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
+};  // class OracleFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.cpp b/catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.cpp
new file mode 100644
index 0000000000..9e00c39e50
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.cpp
@@ -0,0 +1,338 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresArchiveFileCatalogue::PostgresArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsArchiveFileCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void PostgresArchiveFileCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName,
+  const uint64_t archiveFileId, log::LogContext &lc) {
+  try {
+    const char *selectSql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME,"
+        "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+        "ARCHIVE_FILE.DISK_FILE_UID AS DISK_FILE_UID,"
+        "ARCHIVE_FILE.DISK_FILE_GID AS DISK_FILE_GID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32,"
+        "STORAGE_CLASS.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME,"
+        "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME,"
+        "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME,"
+        "TAPE_FILE.VID AS VID,"
+        "TAPE_FILE.FSEQ AS FSEQ,"
+        "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+        "TAPE_FILE.LOGICAL_SIZE_IN_BYTES AS LOGICAL_SIZE_IN_BYTES,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN STORAGE_CLASS ON "
+        "ARCHIVE_FILE.STORAGE_CLASS_ID = STORAGE_CLASS.STORAGE_CLASS_ID "
+      "INNER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID "
+      "FOR UPDATE OF ARCHIVE_FILE";
+    utils::Timer t;
+    auto conn = m_connPool->getConn();
+    rdbms::AutoRollback autoRollback(conn);
+    conn.executeNonQuery("BEGIN");
+
+    const auto getConnTime = t.secs(utils::Timer::resetCounter);
+    auto selectStmt = conn.createStmt(selectSql);
+    const auto createStmtTime = t.secs();
+    selectStmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    t.reset();
+    rdbms::Rset selectRset = selectStmt.executeQuery();
+    const auto selectFromArchiveFileTime = t.secs();
+    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
+    std::set<std::string> vidsToSetDirty;
+    while(selectRset.next()) {
+      if(nullptr == archiveFile.get()) {
+        archiveFile = std::make_unique<common::dataStructures::ArchiveFile>();
+
+        archiveFile->archiveFileID = selectRset.columnUint64("ARCHIVE_FILE_ID");
+        archiveFile->diskInstance = selectRset.columnString("DISK_INSTANCE_NAME");
+        archiveFile->diskFileId = selectRset.columnString("DISK_FILE_ID");
+        archiveFile->diskFileInfo.owner_uid = selectRset.columnUint64("DISK_FILE_UID");
+        archiveFile->diskFileInfo.gid = selectRset.columnUint64("DISK_FILE_GID");
+        archiveFile->fileSize = selectRset.columnUint64("SIZE_IN_BYTES");
+        archiveFile->checksumBlob.deserializeOrSetAdler32(selectRset.columnBlob("CHECKSUM_BLOB"), selectRset.columnUint64("CHECKSUM_ADLER32"));
+        archiveFile->storageClass = selectRset.columnString("STORAGE_CLASS_NAME");
+        archiveFile->creationTime = selectRset.columnUint64("ARCHIVE_FILE_CREATION_TIME");
+        archiveFile->reconciliationTime = selectRset.columnUint64("RECONCILIATION_TIME");
+      }
+
+      // If there is a tape file
+      if(!selectRset.columnIsNull("VID")) {
+        // Add the tape file to the archive file's in-memory structure
+        common::dataStructures::TapeFile tapeFile;
+        tapeFile.vid = selectRset.columnString("VID");
+        vidsToSetDirty.insert(tapeFile.vid);
+        tapeFile.fSeq = selectRset.columnUint64("FSEQ");
+        tapeFile.blockId = selectRset.columnUint64("BLOCK_ID");
+        tapeFile.fileSize = selectRset.columnUint64("LOGICAL_SIZE_IN_BYTES");
+        tapeFile.copyNb = selectRset.columnUint64("COPY_NB");
+        tapeFile.creationTime = selectRset.columnUint64("TAPE_FILE_CREATION_TIME");
+        tapeFile.checksumBlob = archiveFile->checksumBlob; // Duplicated for convenience
+
+        archiveFile->tapeFiles.push_back(tapeFile);
+      }
+    }
+
+    if(nullptr == archiveFile.get()) {
+      log::ScopedParamContainer spc(lc);
+      spc.add("fileId", archiveFileId);
+      lc.log(log::WARNING, "Ignoring request to delete archive file because it does not exist in the catalogue");
+      return;
+    }
+
+    if(diskInstanceName != archiveFile->diskInstance) {
+      log::ScopedParamContainer spc(lc);
+      spc.add("fileId", std::to_string(archiveFile->archiveFileID))
+         .add("diskInstance", archiveFile->diskInstance)
+         .add("requestDiskInstance", diskInstanceName)
+         .add("diskFileId", archiveFile->diskFileId)
+         .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
+         .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
+         .add("fileSize", std::to_string(archiveFile->fileSize))
+         .add("creationTime", std::to_string(archiveFile->creationTime))
+         .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
+         .add("storageClass", archiveFile->storageClass)
+         .add("getConnTime", getConnTime)
+         .add("createStmtTime", createStmtTime)
+         .add("selectFromArchiveFileTime", selectFromArchiveFileTime);
+      archiveFile->checksumBlob.addFirstChecksumToLog(spc);
+      for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
+        std::stringstream tapeCopyLogStream;
+        tapeCopyLogStream << "copy number: " << it->copyNb
+          << " vid: " << it->vid
+          << " fSeq: " << it->fSeq
+          << " blockId: " << it->blockId
+          << " creationTime: " << it->creationTime
+          << " fileSize: " << it->fileSize
+          << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
+          << " copyNb: " << it->copyNb;
+        spc.add("TAPE FILE", tapeCopyLogStream.str());
+      }
+      lc.log(log::WARNING, "Failed to delete archive file because the disk instance of the request does not match that "
+        "of the archived file");
+
+      exception::UserError ue;
+      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because the disk instance of "
+        "the request does not match that of the archived file: archiveFileId=" << archiveFileId << " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" <<
+        archiveFile->diskInstance;
+      throw ue;
+    }
+
+    t.reset();
+    {
+      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+
+    const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter);
+
+    for(auto &vidToSetDirty: vidsToSetDirty){
+      //We deleted the TAPE_FILE so the tapes containing them should be set as dirty
+      RdbmsCatalogueUtils::setTapeDirty(conn,vidToSetDirty);
+    }
+
+    const auto setTapeDirtyTime = t.secs(utils::Timer::resetCounter);
+
+    {
+      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+    const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter);
+
+    conn.commit();
+    autoRollback.cancel();
+    const auto commitTime = t.secs();
+
+    log::ScopedParamContainer spc(lc);
+    spc.add("fileId", std::to_string(archiveFile->archiveFileID))
+       .add("diskInstance", archiveFile->diskInstance)
+       .add("diskFileId", archiveFile->diskFileId)
+       .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
+       .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
+       .add("fileSize", std::to_string(archiveFile->fileSize))
+       .add("creationTime", std::to_string(archiveFile->creationTime))
+       .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
+       .add("storageClass", archiveFile->storageClass)
+       .add("getConnTime", getConnTime)
+       .add("createStmtTime", createStmtTime)
+       .add("selectFromArchiveFileTime", selectFromArchiveFileTime)
+       .add("deleteFromTapeFileTime", deleteFromTapeFileTime)
+       .add("deleteFromArchiveFileTime", deleteFromArchiveFileTime)
+       .add("setTapeDirtyTime",setTapeDirtyTime)
+       .add("commitTime", commitTime);
+    archiveFile->checksumBlob.addFirstChecksumToLog(spc);
+    for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
+      std::stringstream tapeCopyLogStream;
+      tapeCopyLogStream << "copy number: " << it->copyNb
+        << " vid: " << it->vid
+        << " fSeq: " << it->fSeq
+        << " blockId: " << it->blockId
+        << " creationTime: " << it->creationTime
+        << " fileSize: " << it->fileSize
+        << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
+        << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
+      spc.add("TAPE FILE", tapeCopyLogStream.str());
+    }
+    lc.log(log::INFO, "Archive file deleted from CTA catalogue");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t PostgresArchiveFileCatalogue::getNextArchiveFileId(rdbms::Conn &conn) {
+  try {
+    const char *const sql =
+      "select NEXTVAL('ARCHIVE_FILE_ID_SEQ') AS ARCHIVE_FILE_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("ARCHIVE_FILE_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void PostgresArchiveFileCatalogue::copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+  const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) {
+  try {
+    utils::Timer t;
+    log::TimingList tl;
+    //We currently do an INSERT INTO and a DELETE FROM
+    //in a single transaction
+    conn.executeNonQuery("BEGIN");
+    const auto fileRecycleLog = static_cast<RdbmsFileRecycleLogCatalogue*>(m_rdbmsCatalogue->FileRecycleLog().get());
+    fileRecycleLog->copyArchiveFileToFileRecycleLog(conn,request);
+    tl.insertAndReset("insertToRecycleBinTime",t);
+    RdbmsCatalogueUtils::setTapeDirty(conn,request.archiveFileID);
+    tl.insertAndReset("setTapeDirtyTime",t);
+    const auto tapeFileCatalogue = static_cast<RdbmsTapeFileCatalogue*>(m_rdbmsCatalogue->TapeFile().get());
+    tapeFileCatalogue->deleteTapeFiles(conn,request);
+    tl.insertAndReset("deleteTapeFilesTime",t);
+    deleteArchiveFile(conn,request);
+    tl.insertAndReset("deleteArchiveFileTime",t);
+    conn.commit();
+    tl.insertAndReset("commitTime",t);
+    log::ScopedParamContainer spc(lc);
+    spc.add("archiveFileId",request.archiveFileID);
+    spc.add("diskFileId",request.diskFileId);
+    spc.add("diskFilePath",request.diskFilePath);
+    spc.add("diskInstance",request.diskInstance);
+    tl.addToLog(spc);
+    lc.log(log::INFO,"In PostgresCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// selectArchiveFileSizeAndChecksum
+//------------------------------------------------------------------------------
+std::map<uint64_t, PostgresArchiveFileCatalogue::FileSizeAndChecksum>
+  PostgresArchiveFileCatalogue::selectArchiveFileSizesAndChecksums(
+  rdbms::Conn &conn, const std::set<TapeFileWritten> &events) const {
+  try {
+    std::vector<uint64_t> archiveFileIdList(events.size());
+    for (const auto &event: events) {
+      archiveFileIdList.push_back(event.archiveFileId);
+    }
+
+    const char *const sql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.SIZE_IN_BYTES AS SIZE_IN_BYTES,"
+        "ARCHIVE_FILE.CHECKSUM_BLOB AS CHECKSUM_BLOB,"
+        "ARCHIVE_FILE.CHECKSUM_ADLER32 AS CHECKSUM_ADLER32 "
+      "FROM "
+        "ARCHIVE_FILE "
+      "INNER JOIN TEMP_TAPE_FILE_BATCH ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TEMP_TAPE_FILE_BATCH.ARCHIVE_FILE_ID";
+    auto stmt = conn.createStmt(sql);
+
+    auto rset = stmt.executeQuery();
+
+    std::map<uint64_t, FileSizeAndChecksum> fileSizesAndChecksums;
+    while (rset.next()) {
+      const uint64_t archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
+
+      if (fileSizesAndChecksums.end() != fileSizesAndChecksums.find(archiveFileId)) {
+        exception::Exception ex;
+        ex.getMessage() << __FUNCTION__ << " failed: "
+          "Found duplicate archive file identifier in batch of files written to tape: archiveFileId=" << archiveFileId;
+        throw ex;
+      }
+
+      FileSizeAndChecksum fileSizeAndChecksum;
+      fileSizeAndChecksum.fileSize = rset.columnUint64("SIZE_IN_BYTES");
+      fileSizeAndChecksum.checksumBlob.deserializeOrSetAdler32(rset.columnBlob("CHECKSUM_BLOB"), rset.columnUint64("CHECKSUM_ADLER32"));
+      fileSizesAndChecksums[archiveFileId] = fileSizeAndChecksum;
+    }
+
+    return fileSizesAndChecksums;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp b/catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp
new file mode 100644
index 0000000000..97f40414dc
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp
@@ -0,0 +1,72 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "common/checksum/ChecksumBlob.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class TapeFileWritten;
+class RdbmsCatalogue;
+
+class PostgresArchiveFileCatalogue : public RdbmsArchiveFileCatalogue {
+public:
+  PostgresArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresArchiveFileCatalogue() override = default;
+
+  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
+    log::LogContext &lc) override;
+
+private:
+  uint64_t getNextArchiveFileId(rdbms::Conn &conn) override;
+
+  void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+    const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
+
+  /**
+   * The size and checksum of a file.
+   */
+  struct FileSizeAndChecksum {
+    uint64_t fileSize;
+    checksum::ChecksumBlob checksumBlob;
+  };
+
+  friend class PostgresTapeFileCatalogue;
+
+  /**
+   * Returns the sizes and checksums of the specified archive files.
+   *
+   * @param conn The database connection.
+   * @param events The tape file written events that identify the archive files.
+   * @return A map from the identifier of each archive file to its size and checksum.
+   */
+  std::map<uint64_t, FileSizeAndChecksum> selectArchiveFileSizesAndChecksums(rdbms::Conn &conn,
+    const std::set<TapeFileWritten> &events) const;
+};  // class PostgresArchiveFileCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresCatalogue.cpp b/catalogue/rdbms/postgres/PostgresCatalogue.cpp
new file mode 100644
index 0000000000..ab3d09952c
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresCatalogue.cpp
@@ -0,0 +1,91 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresStorageClassCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresTapeCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresTapePoolCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "rdbms/Login.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresCatalogue::PostgresCatalogue(
+  log::Logger &log,
+  const rdbms::Login &login,
+  const uint64_t nbConns,
+  const uint64_t nbArchiveFileListingConns)
+  : RdbmsCatalogue(
+      log,
+      rdbms::Login(rdbms::Login::DBTYPE_POSTGRESQL,
+                  login.username, login.password, login.database,
+                  login.hostname, login.port),
+      nbConns,
+      nbArchiveFileListingConns) {
+  RdbmsCatalogue::m_vo = std::make_unique<PostgresVirtualOrganizationCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_mediaType = std::make_unique<PostgresMediaTypeCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_storageClass = std::make_unique<PostgresStorageClassCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tapePool = std::make_unique<PostgresTapePoolCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tapeFile = std::make_unique<PostgresTapeFileCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_fileRecycleLog = std::make_unique<PostgresFileRecycleLogCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_logicalLibrary = std::make_unique<PostgresLogicalLibraryCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_archiveFile = std::make_unique<PostgresArchiveFileCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tape = std::make_unique<PostgresTapeCatalogue>(m_log, m_connPool, this);
+}
+
+std::string PostgresCatalogue::createAndPopulateTempTableFxid(rdbms::Conn &conn,
+  const std::optional<std::vector<std::string>> &diskFileIds) const {
+  const std::string tempTableName = "TEMP_DISK_FXIDS";
+
+  if(diskFileIds) {
+    try {
+      std::string sql = "CREATE TEMPORARY TABLE " + tempTableName + "(DISK_FILE_ID VARCHAR(100))";
+      try {
+        conn.executeNonQuery(sql);
+      } catch(exception::Exception &ex) {
+        // Postgres does not drop temporary tables until the end of the session; trying to create another
+        // temporary table in the same unit test will fail. If this happens, truncate the table and carry on.
+        sql = "TRUNCATE TABLE " + tempTableName;
+        conn.executeNonQuery(sql);
+      }
+
+      sql = "INSERT INTO " + tempTableName + " VALUES(:DISK_FILE_ID)";
+      auto stmt = conn.createStmt(sql);
+      for(auto &diskFileId : diskFileIds.value()) {
+        stmt.bindString(":DISK_FILE_ID", diskFileId);
+        stmt.executeNonQuery();
+      }
+    } catch(exception::Exception &ex) {
+      ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+      throw;
+    }
+  }
+  return tempTableName;
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresCatalogue.hpp b/catalogue/rdbms/postgres/PostgresCatalogue.hpp
new file mode 100644
index 0000000000..f5afb9e043
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresCatalogue.hpp
@@ -0,0 +1,69 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * An Postgres based implementation of the CTA catalogue.
+ */
+class PostgresCatalogue: public RdbmsCatalogue {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param log Object representing the API to the CTA logging system.
+   * @param username The database username.
+   * @param password The database password.
+   * @param database The database name.
+   * @param nbConns The maximum number of concurrent connections to the
+   * underlying relational database for all operations accept listing archive
+   * files which can be relatively long operations.
+   * @param nbArchiveFileListingConns The maximum number of concurrent
+   * connections to the underlying relational database for the sole purpose of
+   * listing archive files.
+   */
+  PostgresCatalogue(
+    log::Logger &log,
+    const rdbms::Login &login,
+    const uint64_t nbConns,
+    const uint64_t nbArchiveFileListingConns);
+
+  /**
+   * Destructor.
+   */
+  ~PostgresCatalogue() override = default;
+
+  /**
+   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
+   *
+   * @param conn The database connection.
+   * @param diskFileIds List of disk file IDs (fxid).
+   * @return Name of the temporary table
+   */
+  std::string createAndPopulateTempTableFxid(rdbms::Conn &conn,
+    const std::optional<std::vector<std::string>> &diskFileIds) const override;
+}; // class PostgresCatalogue
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.cpp b/catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.cpp
new file mode 100644
index 0000000000..f0688bf7f4
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.cpp
@@ -0,0 +1,110 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresFileRecycleLogCatalogue::PostgresFileRecycleLogCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsFileRecycleLogCatalogue(log, connPool, rdbmsCatalogue) {}
+
+
+void PostgresFileRecycleLogCatalogue::restoreEntryInRecycleLog(rdbms::Conn & conn,
+  FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) {
+  try {
+    utils::Timer timer;
+    log::TimingList timingList;
+
+    if (!fileRecycleLogItor.hasMore()) {
+      throw cta::exception::UserError("No file in the recycle bin matches the parameters passed");
+    }
+  auto fileRecycleLog = fileRecycleLogItor.next();
+    if (fileRecycleLogItor.hasMore()) {
+      // stop restoring more than one file at once
+      throw cta::exception::UserError("More than one recycle bin file matches the parameters passed");
+    }
+
+    // We currently do all file copies restoring in a single transaction
+    conn.executeNonQuery("BEGIN");
+    const auto archiveFileCatalogue = static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+    if (auto archiveFilePtr = archiveFileCatalogue->getArchiveFileById(conn, fileRecycleLog.archiveFileId);
+      !archiveFilePtr) {
+      RdbmsFileRecycleLogCatalogue::restoreArchiveFileInRecycleLog(conn, fileRecycleLog, newFid, lc);
+    } else {
+      if (archiveFilePtr->tapeFiles.find(fileRecycleLog.copyNb) != archiveFilePtr->tapeFiles.end()) {
+        // copy with same copy_nb exists, cannot restore
+        UserSpecifiedExistingDeletedFileCopy ex;
+        ex.getMessage() << "Cannot restore file copy with archiveFileId "
+          << std::to_string(fileRecycleLog.archiveFileId)
+          << " and copy_nb " << std::to_string(fileRecycleLog.copyNb)
+          << " because a tapefile with same archiveFileId and copy_nb already exists";
+        throw ex;
+      }
+    }
+
+    RdbmsFileRecycleLogCatalogue::restoreFileCopyInRecycleLog(conn, fileRecycleLog, lc);
+    conn.commit();
+
+    log::ScopedParamContainer spc(lc);
+    timingList.insertAndReset("commitTime", timer);
+    timingList.addToLog(spc);
+    lc.log(log::INFO, "In PostgresFileRecycleLogCatalogue::restoreEntryInRecycleLog: "
+      "all file copies successfully restored.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t PostgresFileRecycleLogCatalogue::getNextFileRecyleLogId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "select NEXTVAL('FILE_RECYCLE_LOG_ID_SEQ') AS FILE_RECYCLE_LOG_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("FILE_RECYCLE_LOG_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.hpp b/catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.hpp
new file mode 100644
index 0000000000..3383c4214a
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresFileRecycleLogCatalogue.hpp
@@ -0,0 +1,62 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+
+namespace cta {
+
+namespace utils {
+class Timer;
+}
+
+namespace log {
+class TimingList;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresFileRecycleLogCatalogue : public RdbmsFileRecycleLogCatalogue {
+public:
+  PostgresFileRecycleLogCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresFileRecycleLogCatalogue() override = default;
+
+private:
+
+  void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid,
+    log::LogContext & lc) override;
+
+  /**
+   * Copy the fileRecycleLog to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entry
+   * @param conn the database connection
+   * @param fileRecycleLog the fileRecycleLog we want to restore
+   * @param lc the log context
+   */
+  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLog,
+    log::LogContext & lc);
+
+  uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) const override;
+};  // class PostgresFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.cpp b/catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.cpp
new file mode 100644
index 0000000000..aae87be493
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.cpp
@@ -0,0 +1,50 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresLogicalLibraryCatalogue::PostgresLogicalLibraryCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsLogicalLibraryCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t PostgresLogicalLibraryCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "select NEXTVAL('LOGICAL_LIBRARY_ID_SEQ') AS LOGICAL_LIBRARY_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("LOGICAL_LIBRARY_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.hpp b/catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.hpp
new file mode 100644
index 0000000000..80c7d76b8b
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresLogicalLibraryCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresLogicalLibraryCatalogue : public RdbmsLogicalLibraryCatalogue {
+public:
+  PostgresLogicalLibraryCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresLogicalLibraryCatalogue() override = default;
+
+private:
+  uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) const override;
+};  // class SqliteFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.cpp b/catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.cpp
new file mode 100644
index 0000000000..d44d38f74e
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.cpp
@@ -0,0 +1,49 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresMediaTypeCatalogue::PostgresMediaTypeCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsMediaTypeCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t PostgresMediaTypeCatalogue::getNextMediaTypeId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql = "select NEXTVAL('MEDIA_TYPE_ID_SEQ') AS MEDIA_TYPE_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("MEDIA_TYPE_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.hpp b/catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.hpp
new file mode 100644
index 0000000000..90187242a3
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresMediaTypeCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresMediaTypeCatalogue : public RdbmsMediaTypeCatalogue {
+public:
+  PostgresMediaTypeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresMediaTypeCatalogue() override = default;
+
+private:
+  uint64_t getNextMediaTypeId(rdbms::Conn &conn) const override;
+};  // class PostgresMediaTypeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresStorageClassCatalogue.cpp b/catalogue/rdbms/postgres/PostgresStorageClassCatalogue.cpp
new file mode 100644
index 0000000000..eaa1ec954c
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresStorageClassCatalogue.cpp
@@ -0,0 +1,51 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresStorageClassCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresStorageClassCatalogue::PostgresStorageClassCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsStorageClassCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t PostgresStorageClassCatalogue::getNextStorageClassId(rdbms::Conn &conn) {
+  try {
+    const char *const sql =
+      "select NEXTVAL('STORAGE_CLASS_ID_SEQ') AS STORAGE_CLASS_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("STORAGE_CLASS_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresStorageClassCatalogue.hpp b/catalogue/rdbms/postgres/PostgresStorageClassCatalogue.hpp
new file mode 100644
index 0000000000..4c42d2af1d
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresStorageClassCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresStorageClassCatalogue : public RdbmsStorageClassCatalogue {
+public:
+  PostgresStorageClassCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresStorageClassCatalogue() override = default;
+
+private:
+  uint64_t getNextStorageClassId(rdbms::Conn &conn) override;
+};  // class PostgresStorageClassCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresTapeCatalogue.cpp b/catalogue/rdbms/postgres/PostgresTapeCatalogue.cpp
new file mode 100644
index 0000000000..2d2d28f2a0
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresTapeCatalogue.cpp
@@ -0,0 +1,58 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresTapeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresTapeCatalogue::PostgresTapeCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsTapeCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t PostgresTapeCatalogue::getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LAST_FSEQ AS LAST_FSEQ "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if(rset.next()) {
+      return rset.columnUint64("LAST_FSEQ");
+    } else {
+      throw exception::Exception(std::string("No such tape with vid=") + vid);
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresTapeCatalogue.hpp b/catalogue/rdbms/postgres/PostgresTapeCatalogue.hpp
new file mode 100644
index 0000000000..882d727337
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresTapeCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapeCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresTapeCatalogue : public RdbmsTapeCatalogue {
+public:
+  PostgresTapeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresTapeCatalogue() override = default;
+
+private:
+  uint64_t getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const override;
+};  // class PostgresTapeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresTapeFileCatalogue.cpp b/catalogue/rdbms/postgres/PostgresTapeFileCatalogue.cpp
new file mode 100644
index 0000000000..b98c44d352
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresTapeFileCatalogue.cpp
@@ -0,0 +1,589 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <string>
+
+#include "catalogue/InsertFileRecycleLog.hpp"
+#include "catalogue/rdbms/postgres/PostgresArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/postgres/PostgresTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "catalogue/TapeItemWritten.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/FileSizeMismatch.hpp"
+#include "common/exception/TapeFseqMismatch.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+#include "rdbms/wrapper/PostgresColumn.hpp"
+#include "rdbms/wrapper/PostgresStmt.hpp"
+
+namespace cta {
+namespace catalogue {
+
+namespace {
+/**
+ * Structure used to assemble a batch of rows to insert into the TAPE_FILE
+ * table.
+ */
+struct TapeFileBatch {
+  size_t nbRows;
+  rdbms::wrapper::PostgresColumn vid;
+  rdbms::wrapper::PostgresColumn fSeq;
+  rdbms::wrapper::PostgresColumn blockId;
+  rdbms::wrapper::PostgresColumn fileSize;
+  rdbms::wrapper::PostgresColumn copyNb;
+  rdbms::wrapper::PostgresColumn creationTime;
+  rdbms::wrapper::PostgresColumn archiveFileId;
+
+  /**
+   * Constructor.
+   *
+   * @param nbRowsValue  The Number of rows to be inserted.
+   */
+  TapeFileBatch(const size_t nbRowsValue):
+    nbRows(nbRowsValue),
+    vid("VID", nbRows),
+    fSeq("FSEQ", nbRows),
+    blockId("BLOCK_ID", nbRows),
+    fileSize("LOGICAL_SIZE_IN_BYTES", nbRows),
+    copyNb("COPY_NB", nbRows),
+    creationTime("CREATION_TIME", nbRows),
+    archiveFileId("ARCHIVE_FILE_ID", nbRows) {
+  }
+}; // struct TapeFileBatch
+
+/**
+ * Structure used to assemble a batch of rows to insert into the TEMP_ARCHIVE_FILE_BATCH
+ * table.
+ */
+struct ArchiveFileBatch {
+  size_t nbRows;
+  rdbms::wrapper::PostgresColumn archiveFileId;
+  rdbms::wrapper::PostgresColumn diskInstance;
+  rdbms::wrapper::PostgresColumn diskFileId;
+  rdbms::wrapper::PostgresColumn diskFileUser;
+  rdbms::wrapper::PostgresColumn diskFileGroup;
+  rdbms::wrapper::PostgresColumn size;
+  rdbms::wrapper::PostgresColumn checksumBlob;
+  rdbms::wrapper::PostgresColumn checksumAdler32;
+  rdbms::wrapper::PostgresColumn storageClassName;
+  rdbms::wrapper::PostgresColumn creationTime;
+  rdbms::wrapper::PostgresColumn reconciliationTime;
+
+  /**
+   * Constructor.
+   *
+   * @param nbRowsValue  The Number of rows to be inserted.
+   */
+  ArchiveFileBatch(const size_t nbRowsValue):
+    nbRows(nbRowsValue),
+    archiveFileId("ARCHIVE_FILE_ID", nbRows),
+    diskInstance("DISK_INSTANCE_NAME", nbRows),
+    diskFileId("DISK_FILE_ID", nbRows),
+    diskFileUser("DISK_FILE_UID", nbRows),
+    diskFileGroup("DISK_FILE_GID", nbRows),
+    size("SIZE_IN_BYTES", nbRows),
+    checksumBlob("CHECKSUM_BLOB", nbRows),
+    checksumAdler32("CHECKSUM_ADLER32", nbRows),
+    storageClassName("STORAGE_CLASS_NAME", nbRows),
+    creationTime("CREATION_TIME", nbRows),
+    reconciliationTime("RECONCILIATION_TIME", nbRows) {
+  }
+}; // struct ArchiveFileBatch
+
+/**
+ * Structure used to assemble a batch of rows to insert into the
+ * TAPE_FILE_BATCH temporary table.
+ */
+struct TempTapeFileBatch {
+  size_t nbRows;
+  rdbms::wrapper::PostgresColumn archiveFileId;
+
+  /**
+   * Constructor.
+   *
+   * @param nbRowsValue  The Number of rows to be inserted.
+   */
+  TempTapeFileBatch(const size_t nbRowsValue):
+    nbRows(nbRowsValue),
+    archiveFileId("ARCHIVE_FILE_ID", nbRows) {
+  }
+}; // struct TempTapeFileBatch
+
+} // anonymous namespace
+
+PostgresTapeFileCatalogue::PostgresTapeFileCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue)
+  : RdbmsTapeFileCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void PostgresTapeFileCatalogue::copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+  const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+  log::TimingList *timingList, log::LogContext & lc) const {
+  conn.executeNonQuery("BEGIN");
+  const auto fileRecycleLogCatalogue = static_cast<RdbmsFileRecycleLogCatalogue*>(
+    RdbmsTapeFileCatalogue::m_rdbmsCatalogue->FileRecycleLog().get());
+  fileRecycleLogCatalogue->copyTapeFilesToFileRecycleLog(conn, file, reason);
+  timingList->insertAndReset("insertToRecycleBinTime", *timer);
+  RdbmsCatalogueUtils::setTapeDirty(conn, file.archiveFileID);
+  timingList->insertAndReset("setTapeDirtyTime", *timer);
+  deleteTapeFiles(conn, file);
+  timingList->insertAndReset("deleteTapeFilesTime", *timer);
+  conn.commit();
+}
+
+void PostgresTapeFileCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) {
+  try {
+    if (events.empty()) {
+      return;
+    }
+
+    auto firstEventItor = events.begin();
+    const auto &firstEvent = **firstEventItor;
+    checkTapeItemWrittenFieldsAreSet(__FUNCTION__, firstEvent);
+    const time_t now = time(nullptr);
+    auto conn = m_connPool->getConn();
+    rdbms::AutoRollback autoRollback(conn);
+
+    // Start DB transaction and create temporary tables TEMP_ARCHIVE_FILE_BATCH and TEMP_TAPE_FILE_BATCH.
+    // These two tables will exist only for the duration of the transaction.
+    // Set deferrable for second (disk instance, disk file id) constraint of the ARCHIVE_FILE table
+    // to avoid violation in the case of concurrent inserts of a previously not existing archive file.
+    beginCreateTemporarySetDeferred(conn);
+
+    const uint64_t lastFSeq = selectTapeForUpdateAndGetLastFSeq(conn, firstEvent.vid);
+    uint64_t expectedFSeq = lastFSeq + 1;
+    uint64_t totalLogicalBytesWritten = 0;
+
+    // We have a mix of files and items. Only files will be recorded, but items
+    // allow checking fSeq coherency.
+    // determine the number of files
+    size_t filesCount=std::count_if(events.cbegin(), events.cend(),
+        [](const TapeItemWrittenPointer &e) -> bool {return typeid(*e)==typeid(TapeFileWritten);});
+    TapeFileBatch tapeFileBatch(filesCount);
+
+    std::set<TapeFileWritten> fileEvents;
+
+    for (const auto &eventP: events) {
+      // Check for all item types.
+      const auto &event = *eventP;
+      checkTapeItemWrittenFieldsAreSet(__FUNCTION__, event);
+
+      if (event.vid != firstEvent.vid) {
+        throw exception::Exception(std::string("VID mismatch: expected=") + firstEvent.vid + " actual=" + event.vid);
+      }
+
+      if (expectedFSeq != event.fSeq) {
+        exception::TapeFseqMismatch ex;
+        ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
+          event.fSeq;
+        throw ex;
+      }
+      expectedFSeq++;
+
+      try {
+        // If this is a file (as opposed to a placeholder), do the full processing.
+        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(event);
+
+        checkTapeFileWrittenFieldsAreSet(__FUNCTION__, fileEvent);
+
+        totalLogicalBytesWritten += fileEvent.size;
+
+        fileEvents.insert(fileEvent);
+      } catch (std::bad_cast&) {}
+    }
+
+    // Update the tape because all the necessary information is now available
+    auto lastEventItor = events.cend();
+    lastEventItor--;
+    const TapeItemWritten &lastEvent = **lastEventItor;
+    RdbmsCatalogueUtils::updateTape(conn, lastEvent.vid, lastEvent.fSeq, totalLogicalBytesWritten, filesCount,
+      lastEvent.tapeDrive);
+
+    // If we had only placeholders and no file recorded, we are done (but we still commit the update of the tape's fSeq).
+    if (fileEvents.empty()) {
+      conn.commit();
+      return;
+    }
+
+    // Create the archive file entries, skipping those that already exist
+    // However we don't currently lock existing rows, so this transaction may
+    // still fail later, in the face of certain concurrent modifications such
+    // as the deletion of one of the existing archive files for which we are
+    // inserting another tape file.
+    idempotentBatchInsertArchiveFiles(conn, fileEvents);
+
+    insertTapeFileBatchIntoTempTable(conn, fileEvents);
+
+    // Verify that the archive file entries in the catalogue database agree with
+    // the tape file written events
+    const auto archiveFileCatalogue = static_cast<PostgresArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+    const auto fileSizesAndChecksums = archiveFileCatalogue->selectArchiveFileSizesAndChecksums(conn, fileEvents);
+    for (const auto &event: fileEvents) {
+      const auto fileSizeAndChecksumItor = fileSizesAndChecksums.find(event.archiveFileId);
+
+      std::ostringstream fileContext;
+      fileContext << "archiveFileId=" << event.archiveFileId << ", diskInstanceName=" << event.diskInstance <<
+        ", diskFileId=" << event.diskFileId;
+
+      // This should never happen
+      if(fileSizesAndChecksums.end() == fileSizeAndChecksumItor) {
+        exception::Exception ex;
+        ex.getMessage() << __FUNCTION__ << ": Failed to find archive file entry in the catalogue: " << fileContext.str();
+        throw ex;
+      }
+
+      const auto &fileSizeAndChecksum = fileSizeAndChecksumItor->second;
+
+      if(fileSizeAndChecksum.fileSize != event.size) {
+        catalogue::FileSizeMismatch ex;
+        ex.getMessage() << __FUNCTION__ << ": File size mismatch: expected=" << fileSizeAndChecksum.fileSize <<
+          ", actual=" << event.size << ": " << fileContext.str();
+        throw ex;
+      }
+
+      fileSizeAndChecksum.checksumBlob.validate(event.checksumBlob);
+    }
+
+    // Store the value of each field
+    uint32_t i = 0;
+    for (const auto &event: fileEvents) {
+      tapeFileBatch.vid.setFieldValue(i, event.vid);
+      tapeFileBatch.fSeq.setFieldValue(i, event.fSeq);
+      tapeFileBatch.blockId.setFieldValue(i, event.blockId);
+      tapeFileBatch.fileSize.setFieldValue(i, event.size);
+      tapeFileBatch.copyNb.setFieldValue(i, event.copyNb);
+      tapeFileBatch.creationTime.setFieldValue(i, now);
+      tapeFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
+      i++;
+    }
+
+    const char *const sql =
+    "CREATE TEMPORARY TABLE TEMP_TAPE_FILE_INSERTION_BATCH ("                        "\n"
+      "LIKE TAPE_FILE) "                                                             "\n"
+      "ON COMMIT DROP;"                                                              "\n"
+    "COPY TEMP_TAPE_FILE_INSERTION_BATCH("                                           "\n"
+      "VID,"                                                                         "\n"
+      "FSEQ,"                                                                        "\n"
+      "BLOCK_ID,"                                                                    "\n"
+      "LOGICAL_SIZE_IN_BYTES,"                                                       "\n"
+      "COPY_NB,"                                                                     "\n"
+      "CREATION_TIME,"                                                               "\n"
+      "ARCHIVE_FILE_ID) "                                                            "\n"
+    "FROM STDIN; --"                                                                 "\n"
+      "-- :VID,"                                                                     "\n"
+      "-- :FSEQ,"                                                                    "\n"
+      "-- :BLOCK_ID,"                                                                "\n"
+      "-- :LOGICAL_SIZE_IN_BYTES,"                                                   "\n"
+      "-- :COPY_NB,"                                                                 "\n"
+      "-- :CREATION_TIME,"                                                           "\n"
+      "-- :ARCHIVE_FILE_ID;"                                                         "\n";
+
+    auto stmt = conn.createStmt(sql);
+    rdbms::wrapper::PostgresStmt &postgresStmt = dynamic_cast<rdbms::wrapper::PostgresStmt &>(stmt.getStmt());
+    postgresStmt.setColumn(tapeFileBatch.vid);
+    postgresStmt.setColumn(tapeFileBatch.fSeq);
+    postgresStmt.setColumn(tapeFileBatch.blockId);
+    postgresStmt.setColumn(tapeFileBatch.fileSize);
+    postgresStmt.setColumn(tapeFileBatch.copyNb);
+    postgresStmt.setColumn(tapeFileBatch.creationTime);
+    postgresStmt.setColumn(tapeFileBatch.archiveFileId);
+
+    postgresStmt.executeCopyInsert(tapeFileBatch.nbRows);
+
+    auto recycledFiles = insertOldCopiesOfFilesIfAnyOnFileRecycleLog(conn);
+
+    {
+      //Insert the tapefiles from the TEMP_TAPE_FILE_INSERTION_BATCH
+      const char * const insertTapeFileSql =
+        "INSERT INTO TAPE_FILE (VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES, "
+        "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID) "
+        "SELECT VID, FSEQ, BLOCK_ID, LOGICAL_SIZE_IN_BYTES, "
+        "COPY_NB, CREATION_TIME, ARCHIVE_FILE_ID FROM TEMP_TAPE_FILE_INSERTION_BATCH;";
+      conn.executeNonQuery(insertTapeFileSql);
+    }
+
+    for(auto & recycledFile: recycledFiles){
+      const char * const deleteTapeFileSql =
+      "DELETE FROM TAPE_FILE WHERE TAPE_FILE.VID = :VID AND TAPE_FILE.FSEQ = :FSEQ";
+      auto deleteTapeFileStmt = conn.createStmt(deleteTapeFileSql);
+      deleteTapeFileStmt.bindString(":VID",recycledFile.vid);
+      deleteTapeFileStmt.bindUint64(":FSEQ",recycledFile.fSeq);
+      deleteTapeFileStmt.executeNonQuery();
+    }
+
+    autoRollback.cancel();
+    conn.commit();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+std::list<cta::catalogue::InsertFileRecycleLog> PostgresTapeFileCatalogue::insertOldCopiesOfFilesIfAnyOnFileRecycleLog(
+  rdbms::Conn& conn){
+  std::list<cta::catalogue::InsertFileRecycleLog> fileRecycleLogsToInsert;
+  try {
+    //Get the TAPE_FILE entry to put on the file recycle log
+    {
+      const char *const sql =
+        "SELECT "
+          "TAPE_FILE.VID AS VID,"
+          "TAPE_FILE.FSEQ AS FSEQ,"
+          "TAPE_FILE.BLOCK_ID AS BLOCK_ID,"
+          "TAPE_FILE.COPY_NB AS COPY_NB,"
+          "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME,"
+          "TAPE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID "
+        "FROM "
+          "TAPE_FILE "
+        "JOIN "
+          "TEMP_TAPE_FILE_INSERTION_BATCH "
+        "ON "
+          "TEMP_TAPE_FILE_INSERTION_BATCH.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID AND TEMP_TAPE_FILE_INSERTION_BATCH.COPY_NB = TAPE_FILE.COPY_NB "
+        "WHERE "
+          "TAPE_FILE.VID != TEMP_TAPE_FILE_INSERTION_BATCH.VID OR TAPE_FILE.FSEQ != TEMP_TAPE_FILE_INSERTION_BATCH.FSEQ";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      while(rset.next()){
+        cta::catalogue::InsertFileRecycleLog fileRecycleLog;
+        fileRecycleLog.vid = rset.columnString("VID");
+        fileRecycleLog.fSeq = rset.columnUint64("FSEQ");
+        fileRecycleLog.blockId = rset.columnUint64("BLOCK_ID");
+        fileRecycleLog.copyNb = rset.columnUint8("COPY_NB");
+        fileRecycleLog.tapeFileCreationTime = rset.columnUint64("TAPE_FILE_CREATION_TIME");
+        fileRecycleLog.archiveFileId = rset.columnUint64("ARCHIVE_FILE_ID");
+        fileRecycleLog.reasonLog = InsertFileRecycleLog::getRepackReasonLog();
+        fileRecycleLog.recycleLogTime = time(nullptr);
+        fileRecycleLogsToInsert.push_back(fileRecycleLog);
+      }
+    }
+    {
+      for(auto & fileRecycleLog: fileRecycleLogsToInsert){
+        const auto fileRecycleLogCatalogue
+          = static_cast<RdbmsFileRecycleLogCatalogue*>(m_rdbmsCatalogue->FileRecycleLog().get());
+        fileRecycleLogCatalogue->insertFileInFileRecycleLog(conn,fileRecycleLog);
+      }
+      return fileRecycleLogsToInsert;
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t PostgresTapeFileCatalogue::selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn, const std::string &vid) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LAST_FSEQ AS LAST_FSEQ "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID "
+      "FOR UPDATE";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("The tape with VID " + vid + " does not exist"));
+    }
+
+    return rset.columnUint64("LAST_FSEQ");
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void PostgresTapeFileCatalogue::beginCreateTemporarySetDeferred(rdbms::Conn &conn) const {
+  conn.executeNonQuery("BEGIN");
+  conn.executeNonQuery("CREATE TEMPORARY TABLE TEMP_ARCHIVE_FILE_BATCH (LIKE ARCHIVE_FILE) ON COMMIT DROP");
+  conn.executeNonQuery("ALTER TABLE TEMP_ARCHIVE_FILE_BATCH ADD COLUMN STORAGE_CLASS_NAME VARCHAR(100)");
+  conn.executeNonQuery("ALTER TABLE TEMP_ARCHIVE_FILE_BATCH ALTER COLUMN STORAGE_CLASS_ID DROP NOT NULL");
+  conn.executeNonQuery("ALTER TABLE TEMP_ARCHIVE_FILE_BATCH ALTER COLUMN IS_DELETED DROP NOT NULL");
+  conn.executeNonQuery("CREATE INDEX TEMP_A_F_B_ARCHIVE_FILE_ID_I ON TEMP_ARCHIVE_FILE_BATCH(ARCHIVE_FILE_ID)");
+  conn.executeNonQuery("CREATE INDEX TEMP_A_F_B_DIN_SCN_I ON TEMP_ARCHIVE_FILE_BATCH(DISK_INSTANCE_NAME, STORAGE_CLASS_NAME)");
+  conn.executeNonQuery("CREATE TEMPORARY TABLE TEMP_TAPE_FILE_BATCH(ARCHIVE_FILE_ID NUMERIC(20,0)) ON COMMIT DROP");
+  conn.executeNonQuery("CREATE INDEX TEMP_T_F_B_ARCHIVE_FILE_ID_I ON TEMP_TAPE_FILE_BATCH(ARCHIVE_FILE_ID)");
+  conn.executeNonQuery("SET CONSTRAINTS ARCHIVE_FILE_DIN_DFI_UN DEFERRED");
+}
+
+void PostgresTapeFileCatalogue::idempotentBatchInsertArchiveFiles(rdbms::Conn &conn,
+  const std::set<TapeFileWritten> &events) const {
+  try {
+    ArchiveFileBatch archiveFileBatch(events.size());
+    const time_t now = time(nullptr);
+
+    // Store the value of each field
+    uint32_t i = 0;
+    for (const auto &event: events) {
+      archiveFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
+      archiveFileBatch.diskInstance.setFieldValue(i, event.diskInstance);
+      archiveFileBatch.diskFileId.setFieldValue(i, event.diskFileId);
+      archiveFileBatch.diskFileUser.setFieldValue(i, event.diskFileOwnerUid);
+      archiveFileBatch.diskFileGroup.setFieldValue(i, event.diskFileGid);
+      archiveFileBatch.size.setFieldValue(i, event.size);
+      archiveFileBatch.checksumBlob.setFieldByteA(conn, i, event.checksumBlob.serialize());
+      // Keep transition ADLER32 checksum up-to-date if it exists
+      std::string adler32str;
+      try {
+        std::string adler32hex = checksum::ChecksumBlob::ByteArrayToHex(event.checksumBlob.at(checksum::ADLER32));
+        uint32_t adler32 = strtoul(adler32hex.c_str(), 0, 16);
+        adler32str = std::to_string(adler32);
+      } catch(exception::ChecksumTypeMismatch &ex) {
+        adler32str = "0";
+      }
+      archiveFileBatch.checksumAdler32.setFieldValue(i, adler32str);
+      archiveFileBatch.storageClassName.setFieldValue(i, event.storageClassName);
+      archiveFileBatch.creationTime.setFieldValue(i, now);
+      archiveFileBatch.reconciliationTime.setFieldValue(i, now);
+      i++;
+    }
+
+    const char *const sql =
+      "COPY TEMP_ARCHIVE_FILE_BATCH("
+        "ARCHIVE_FILE_ID,"
+        "DISK_INSTANCE_NAME,"
+        "DISK_FILE_ID,"
+        "DISK_FILE_UID,"
+        "DISK_FILE_GID,"
+        "SIZE_IN_BYTES,"
+        "CHECKSUM_BLOB,"
+        "CHECKSUM_ADLER32,"
+        "STORAGE_CLASS_NAME,"
+        "CREATION_TIME,"
+        "RECONCILIATION_TIME) "
+      "FROM STDIN --"
+        ":ARCHIVE_FILE_ID,"
+        ":DISK_INSTANCE_NAME,"
+        ":DISK_FILE_ID,"
+        ":DISK_FILE_UID,"
+        ":DISK_FILE_GID,"
+        ":SIZE_IN_BYTES,"
+        ":CHECKSUM_BLOB,"
+        ":CHECKSUM_ADLER32,"
+        ":STORAGE_CLASS_NAME,"
+        ":CREATION_TIME,"
+        ":RECONCILIATION_TIME";
+
+    auto stmt = conn.createStmt(sql);
+    rdbms::wrapper::PostgresStmt &postgresStmt = dynamic_cast<rdbms::wrapper::PostgresStmt &>(stmt.getStmt());
+
+    postgresStmt.setColumn(archiveFileBatch.archiveFileId);
+    postgresStmt.setColumn(archiveFileBatch.diskInstance);
+    postgresStmt.setColumn(archiveFileBatch.diskFileId);
+    postgresStmt.setColumn(archiveFileBatch.diskFileUser);
+    postgresStmt.setColumn(archiveFileBatch.diskFileGroup);
+    postgresStmt.setColumn(archiveFileBatch.size);
+    postgresStmt.setColumn(archiveFileBatch.checksumBlob);
+    postgresStmt.setColumn(archiveFileBatch.checksumAdler32);
+    postgresStmt.setColumn(archiveFileBatch.storageClassName);
+    postgresStmt.setColumn(archiveFileBatch.creationTime);
+    postgresStmt.setColumn(archiveFileBatch.reconciliationTime);
+
+    postgresStmt.executeCopyInsert(archiveFileBatch.nbRows);
+
+    const char *const sql_insert =
+      "INSERT INTO ARCHIVE_FILE("
+        "ARCHIVE_FILE_ID,"
+  	"DISK_INSTANCE_NAME,"
+        "DISK_FILE_ID,"
+        "DISK_FILE_UID,"
+        "DISK_FILE_GID,"
+        "SIZE_IN_BYTES,"
+        "CHECKSUM_BLOB,"
+        "CHECKSUM_ADLER32,"
+        "STORAGE_CLASS_ID,"
+        "CREATION_TIME,"
+        "RECONCILIATION_TIME) "
+      "SELECT "
+        "A.ARCHIVE_FILE_ID,"
+        "A.DISK_INSTANCE_NAME,"
+        "A.DISK_FILE_ID,"
+        "A.DISK_FILE_UID,"
+        "A.DISK_FILE_GID,"
+        "A.SIZE_IN_BYTES,"
+        "A.CHECKSUM_BLOB,"
+        "A.CHECKSUM_ADLER32,"
+        "S.STORAGE_CLASS_ID,"
+        "A.CREATION_TIME,"
+        "A.RECONCILIATION_TIME "
+      "FROM TEMP_ARCHIVE_FILE_BATCH AS A, STORAGE_CLASS AS S "
+        "WHERE A.STORAGE_CLASS_NAME = S.STORAGE_CLASS_NAME "
+      "ORDER BY A.ARCHIVE_FILE_ID "
+      "ON CONFLICT (ARCHIVE_FILE_ID) DO NOTHING";
+
+    // Concerns for bulk insertion in archive_file: deadlock with concurrent
+    // inserts of previously not-existing entry for the same archive file,
+    // hence insert with ORDER BY to define an update order.
+
+    auto stmt_insert = conn.createStmt(sql_insert);
+    stmt_insert.executeNonQuery();
+
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void PostgresTapeFileCatalogue::insertTapeFileBatchIntoTempTable(rdbms::Conn &conn,
+   const std::set<TapeFileWritten> &events) const {
+  try {
+    TempTapeFileBatch tempTapeFileBatch(events.size());
+
+    // Store the value of each field
+    uint32_t i = 0;
+    for (const auto &event: events) {
+      tempTapeFileBatch.archiveFileId.setFieldValue(i, event.archiveFileId);
+      i++;
+    }
+
+    const char *const sql =
+      "COPY TEMP_TAPE_FILE_BATCH("
+        "ARCHIVE_FILE_ID) "
+      "FROM STDIN --"
+        ":ARCHIVE_FILE_ID";
+
+    auto stmt = conn.createStmt(sql);
+    rdbms::wrapper::PostgresStmt &postgresStmt = dynamic_cast<rdbms::wrapper::PostgresStmt &>(stmt.getStmt());
+
+    postgresStmt.setColumn(tempTapeFileBatch.archiveFileId);
+    postgresStmt.executeCopyInsert(tempTapeFileBatch.nbRows);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresTapeFileCatalogue.hpp b/catalogue/rdbms/postgres/PostgresTapeFileCatalogue.hpp
new file mode 100644
index 0000000000..87186a5160
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresTapeFileCatalogue.hpp
@@ -0,0 +1,101 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+
+namespace cta {
+
+namespace utils {
+class Timer;
+}
+
+namespace log {
+class TimingList;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresTapeFileCatalogue : public RdbmsTapeFileCatalogue {
+public:
+  PostgresTapeFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+  ~PostgresTapeFileCatalogue() override = default;
+
+  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override;
+
+private:
+  void  copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+    const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+    log::TimingList *timingList, log::LogContext & lc) const override;
+
+  std::list<cta::catalogue::InsertFileRecycleLog> insertOldCopiesOfFilesIfAnyOnFileRecycleLog(rdbms::Conn& conn);
+
+  /**
+   * Selects the specified tape for update and returns its last FSeq.
+   *
+   * @param conn The database connection.
+   * @param vid The volume identifier of the tape.
+   * @param The last FSeq of the tape.
+   */
+  uint64_t selectTapeForUpdateAndGetLastFSeq(rdbms::Conn &conn, const std::string &vid) const;
+
+  /**
+   * Start a database transaction and then create the temporary
+   * tables TEMP_ARCHIVE_FILE_BATCH and TEMP_TAPE_FILE_BATCH.
+   * Sets deferred mode for one of the db constraints to avoid
+   * violations during concurrent bulk insert.
+   *
+   * @parm conn The database connection.
+   */
+  void beginCreateTemporarySetDeferred(rdbms::Conn &conn) const;
+
+  /**
+   * Batch inserts rows into the ARCHIVE_FILE table that correspond to the
+   * specified TapeFileWritten events.
+   *
+   * This method has idempotent behaviour in the case where an ARCHIVE_FILE
+   * already exists.  Such a situation will occur when a file has more than one
+   * copy on tape.  The first tape copy will cause two successful inserts, one
+   * into the ARCHIVE_FILE table and one into the  TAPE_FILE table.  The second
+   * tape copy will try to do the same, but the insert into the ARCHIVE_FILE
+   * table will fail or simply bounce as the row will already exists.  The
+   * insert into the TABLE_FILE table will succeed because the two TAPE_FILE
+   * rows will be unique.
+   *
+   * @param conn The database connection.
+   * @param events The tape file written events.
+   */
+  void idempotentBatchInsertArchiveFiles(rdbms::Conn &conn, const std::set<TapeFileWritten> &events) const;
+
+  /**
+   * Batch inserts rows into the TAPE_FILE_BATCH temporary table that correspond
+   * to the specified TapeFileWritten events.
+   *
+   * @param conn The database connection.
+   * @param events The tape file written events.
+   */
+  void insertTapeFileBatchIntoTempTable(rdbms::Conn &conn, const std::set<TapeFileWritten> &events) const;
+};  // class PostgresTapeFileCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresTapePoolCatalogue.cpp b/catalogue/rdbms/postgres/PostgresTapePoolCatalogue.cpp
new file mode 100644
index 0000000000..f09debac82
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresTapePoolCatalogue.cpp
@@ -0,0 +1,51 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresTapePoolCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresTapePoolCatalogue::PostgresTapePoolCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsTapePoolCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t PostgresTapePoolCatalogue::getNextTapePoolId(rdbms::Conn &conn) const {
+  try {
+    const char *const sql =
+      "select NEXTVAL('TAPE_POOL_ID_SEQ') AS TAPE_POOL_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("TAPE_POOL_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresTapePoolCatalogue.hpp b/catalogue/rdbms/postgres/PostgresTapePoolCatalogue.hpp
new file mode 100644
index 0000000000..c3334cea7c
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresTapePoolCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapePoolCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresTapePoolCatalogue : public RdbmsTapePoolCatalogue {
+public:
+  PostgresTapePoolCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresTapePoolCatalogue() override = default;
+
+private:
+  uint64_t getNextTapePoolId(rdbms::Conn &conn) const override;
+};  // class PostgresTapePoolCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.cpp b/catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.cpp
new file mode 100644
index 0000000000..65bcd29822
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.cpp
@@ -0,0 +1,50 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+PostgresVirtualOrganizationCatalogue::PostgresVirtualOrganizationCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsVirtualOrganizationCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t PostgresVirtualOrganizationCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
+  try {
+    const char *const sql =
+      "select NEXTVAL('VIRTUAL_ORGANIZATION_ID_SEQ') AS VIRTUAL_ORGANIZATION_ID";
+    auto stmt = conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set is unexpectedly empty");
+    }
+    return rset.columnUint64("VIRTUAL_ORGANIZATION_ID");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.hpp b/catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.hpp
new file mode 100644
index 0000000000..c8e68cf121
--- /dev/null
+++ b/catalogue/rdbms/postgres/PostgresVirtualOrganizationCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class PostgresVirtualOrganizationCatalogue : public RdbmsVirtualOrganizationCatalogue {
+public:
+  PostgresVirtualOrganizationCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~PostgresVirtualOrganizationCatalogue() override = default;
+
+protected:
+  uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
+};  // class PostgresVirtualOrganizationCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.cpp
new file mode 100644
index 0000000000..f2748ed0cf
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.cpp
@@ -0,0 +1,240 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/AutoRollback.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteArchiveFileCatalogue::SqliteArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+  RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsArchiveFileCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void SqliteArchiveFileCatalogue::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName,
+  const uint64_t archiveFileId,
+  log::LogContext &lc) {
+  try {
+    utils::Timer t;
+    auto conn = m_connPool->getConn();
+    const auto getConnTime = t.secs();
+    rdbms::AutoRollback autoRollback(conn);
+    t.reset();
+    const auto archiveFile = getArchiveFileById(conn, archiveFileId);
+    const auto getArchiveFileTime = t.secs();
+
+    if(nullptr == archiveFile.get()) {
+      log::ScopedParamContainer spc(lc);
+      spc.add("fileId", archiveFileId);
+      lc.log(log::WARNING, "Ignoring request to delete archive file because it does not exist in the catalogue");
+      return;
+    }
+
+    if(diskInstanceName != archiveFile->diskInstance) {
+      log::ScopedParamContainer spc(lc);
+      spc.add("fileId", std::to_string(archiveFile->archiveFileID))
+         .add("diskInstance", archiveFile->diskInstance)
+         .add("requestDiskInstance", diskInstanceName)
+         .add("diskFileId", archiveFile->diskFileId)
+         .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
+         .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
+         .add("fileSize", std::to_string(archiveFile->fileSize))
+         .add("creationTime", std::to_string(archiveFile->creationTime))
+         .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
+         .add("storageClass", archiveFile->storageClass)
+         .add("getConnTime", getConnTime)
+         .add("getArchiveFileTime", getArchiveFileTime);
+      archiveFile->checksumBlob.addFirstChecksumToLog(spc);
+      for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
+        std::stringstream tapeCopyLogStream;
+        tapeCopyLogStream << "copy number: " << it->copyNb
+          << " vid: " << it->vid
+          << " fSeq: " << it->fSeq
+          << " blockId: " << it->blockId
+          << " creationTime: " << it->creationTime
+          << " fileSize: " << it->fileSize
+          << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
+          << " copyNb: " << it->copyNb; //this shouldn't be here: repeated field
+        spc.add("TAPE FILE", tapeCopyLogStream.str());
+      }
+      lc.log(log::WARNING, "Failed to delete archive file because the disk instance of the request does not match that "
+        "of the archived file");
+
+      exception::UserError ue;
+      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because the disk instance of "
+        "the request does not match that of the archived file: archiveFileId=" << archiveFileId << " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" <<
+        archiveFile->diskInstance;
+      throw ue;
+    }
+
+    t.reset();
+    {
+      const char *const sql = "BEGIN DEFERRED;";
+      auto stmt = conn.createStmt(sql);
+      stmt.executeNonQuery();
+    }
+    {
+      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+
+    const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter);
+
+    std::set<std::string> vidsToSetDirty;
+    //We will insert the vids to set dirty in a set so that
+    //we limit the calls to setTapeDirty to the number of tapes that contained the deleted tape files
+    for(auto &tapeFile: archiveFile->tapeFiles){
+      vidsToSetDirty.insert(tapeFile.vid);
+    }
+
+    for(auto &vidToSetDirty: vidsToSetDirty){
+      RdbmsCatalogueUtils::setTapeDirty(conn,vidToSetDirty);
+    }
+
+    const auto setTapeDirtyTime = t.secs(utils::Timer::resetCounter);
+
+    {
+      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt.executeNonQuery();
+    }
+    const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter);
+
+    conn.commit();
+    const auto commitTime = t.secs();
+
+    log::ScopedParamContainer spc(lc);
+    spc.add("fileId", std::to_string(archiveFile->archiveFileID))
+       .add("diskInstance", archiveFile->diskInstance)
+       .add("diskFileId", archiveFile->diskFileId)
+       .add("diskFileInfo.owner_uid", archiveFile->diskFileInfo.owner_uid)
+       .add("diskFileInfo.gid", archiveFile->diskFileInfo.gid)
+       .add("fileSize", std::to_string(archiveFile->fileSize))
+       .add("creationTime", std::to_string(archiveFile->creationTime))
+       .add("reconciliationTime", std::to_string(archiveFile->reconciliationTime))
+       .add("storageClass", archiveFile->storageClass)
+       .add("getConnTime", getConnTime)
+       .add("getArchiveFileTime", getArchiveFileTime)
+       .add("deleteFromTapeFileTime", deleteFromTapeFileTime)
+       .add("deleteFromArchiveFileTime", deleteFromArchiveFileTime)
+       .add("setTapeDirtyTime",setTapeDirtyTime)
+       .add("commitTime", commitTime);
+    archiveFile->checksumBlob.addFirstChecksumToLog(spc);
+    for(auto it=archiveFile->tapeFiles.begin(); it!=archiveFile->tapeFiles.end(); it++) {
+      std::stringstream tapeCopyLogStream;
+      tapeCopyLogStream << "copy number: " << it->copyNb
+        << " vid: " << it->vid
+        << " fSeq: " << it->fSeq
+        << " blockId: " << it->blockId
+        << " creationTime: " << it->creationTime
+        << " fileSize: " << it->fileSize
+        << " checksumBlob: " << it->checksumBlob //this shouldn't be here: repeated field
+        << " copyNb: " << static_cast<int>(it->copyNb); //this shouldn't be here: repeated field
+      spc.add("TAPE FILE", tapeCopyLogStream.str());
+    }
+    lc.log(log::INFO, "Archive file deleted from CTA catalogue");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t SqliteArchiveFileCatalogue::getNextArchiveFileId(rdbms::Conn &conn) {
+  try {
+    conn.executeNonQuery("INSERT INTO ARCHIVE_FILE_ID VALUES(NULL)");
+    uint64_t archiveFileId = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      archiveFileId = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM ARCHIVE_FILE_ID");
+
+    return archiveFileId;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// copyArchiveFileToRecycleBinAndDelete
+//------------------------------------------------------------------------------
+void SqliteArchiveFileCatalogue::copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+  const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) {
+  try {
+    utils::Timer t;
+    log::TimingList tl;
+    //We currently do an INSERT INTO and a DELETE FROM
+    //in a single transaction
+    conn.executeNonQuery("BEGIN TRANSACTION");
+    const auto fileRecycleLog = static_cast<RdbmsFileRecycleLogCatalogue*>(m_rdbmsCatalogue->FileRecycleLog().get());
+    fileRecycleLog->copyArchiveFileToFileRecycleLog(conn,request);
+    tl.insertAndReset("insertToRecycleBinTime",t);
+    RdbmsCatalogueUtils::setTapeDirty(conn,request.archiveFileID);
+    tl.insertAndReset("setTapeDirtyTime",t);
+    const auto tapeFileCatalogue = static_cast<RdbmsTapeFileCatalogue*>(m_rdbmsCatalogue->TapeFile().get());
+    tapeFileCatalogue->deleteTapeFiles(conn,request);
+    tl.insertAndReset("deleteTapeFilesTime",t);
+    deleteArchiveFile(conn,request);
+    tl.insertAndReset("deleteArchiveFileTime",t);
+    conn.commit();
+    tl.insertAndReset("commitTime",t);
+    log::ScopedParamContainer spc(lc);
+    spc.add("archiveFileId",request.archiveFileID);
+    spc.add("diskFileId",request.diskFileId);
+    spc.add("diskFilePath",request.diskFilePath);
+    spc.add("diskInstance",request.diskInstance);
+    tl.addToLog(spc);
+    lc.log(log::INFO,"In SqliteCatalogue::copyArchiveFileToRecycleBinAndDelete: ArchiveFile moved to the recycle-bin.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.hpp
new file mode 100644
index 0000000000..392afa0eff
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.hpp
@@ -0,0 +1,47 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteArchiveFileCatalogue : public RdbmsArchiveFileCatalogue {
+public:
+  SqliteArchiveFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteArchiveFileCatalogue() override = default;
+
+  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
+    log::LogContext &lc) override;
+
+private:
+  uint64_t getNextArchiveFileId(rdbms::Conn &conn) override;
+
+  void copyArchiveFileToFileRecyleLogAndDelete(rdbms::Conn & conn,
+    const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) override;
+};  // class SqliteArchiveFileCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteCatalogue.cpp
new file mode 100644
index 0000000000..677737d058
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteCatalogue.cpp
@@ -0,0 +1,86 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+#include "rdbms/Login.hpp"
+
+namespace cta {
+namespace catalogue {
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+SqliteCatalogue::SqliteCatalogue(
+  log::Logger &log,
+  const std::string &filename,
+  const uint64_t nbConns,
+  const uint64_t nbArchiveFileListingConns):
+  RdbmsCatalogue(
+    log,
+    rdbms::Login(rdbms::Login::DBTYPE_SQLITE, "", "", filename, "", 0),
+    nbConns,
+    nbArchiveFileListingConns) {
+  RdbmsCatalogue::m_fileRecycleLog = std::make_unique<SqliteFileRecycleLogCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_storageClass = std::make_unique<SqliteStorageClassCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tapePool = std::make_unique<SqliteTapePoolCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_vo = std::make_unique<SqliteVirtualOrganizationCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_mediaType = std::make_unique<SqliteMediaTypeCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_logicalLibrary = std::make_unique<SqliteLogicalLibraryCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tape = std::make_unique<SqliteTapeCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_archiveFile = std::make_unique<SqliteArchiveFileCatalogue>(m_log, m_connPool, this);
+  RdbmsCatalogue::m_tapeFile = std::make_unique<SqliteTapeFileCatalogue>(m_log, m_connPool, this);
+}
+
+//------------------------------------------------------------------------------
+// createAndPopulateTempTableFxid
+//------------------------------------------------------------------------------
+std::string SqliteCatalogue::createAndPopulateTempTableFxid(rdbms::Conn &conn, const std::optional<std::vector<std::string>> &diskFileIds) const {
+  try {
+    const std::string tempTableName = "TEMP.DISK_FXIDS";
+
+    // Drop any prexisting temporary table and create a new one
+    conn.executeNonQuery("DROP TABLE IF EXISTS " + tempTableName);
+    conn.executeNonQuery("CREATE TEMPORARY TABLE " + tempTableName + "(DISK_FILE_ID TEXT)");
+
+    if(diskFileIds) {
+      auto stmt = conn.createStmt("INSERT INTO " + tempTableName + " VALUES(:DISK_FILE_ID)");
+      for(auto &diskFileId : diskFileIds.value()) {
+        stmt.bindString(":DISK_FILE_ID", diskFileId);
+        stmt.executeNonQuery();
+      }
+    }
+
+    return tempTableName;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteCatalogue.hpp
new file mode 100644
index 0000000000..86e1574c11
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteCatalogue.hpp
@@ -0,0 +1,68 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * An SQLite implementation of the CTA catalogue.
+ */
+class SqliteCatalogue: public RdbmsCatalogue {
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param log Object representing the API to the CTA logging system.
+   * @param filename The filename to be passed to the sqlite3_open() function.
+   * @param nbConns The maximum number of concurrent connections to the
+   * underlying relational database for all operations accept listing archive
+   * files which can be relatively long operations.
+   * @param nbArchiveFileListingConns The maximum number of concurrent
+   * connections to the underlying relational database for the sole purpose of
+   * listing archive files.
+   */
+  SqliteCatalogue(
+    log::Logger &log,
+    const std::string &filename,
+    const uint64_t nbConns,
+    const uint64_t nbArchiveFileListingConns);
+
+public:
+  /**
+   * Destructor.
+   */
+  ~SqliteCatalogue() override = default;
+
+protected:
+  /**
+   * Creates a temporary table from the list of disk file IDs provided in the search criteria.
+   *
+   * @param conn The database connection.
+   * @param diskFileIds List of disk file IDs (fxid).
+   * @return Name of the temporary table
+   */
+  std::string createAndPopulateTempTableFxid(rdbms::Conn &conn,
+    const std::optional<std::vector<std::string>> &diskFileIds) const override;
+}; // class SqliteCatalogue
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.cpp
new file mode 100644
index 0000000000..bbe1db3199
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.cpp
@@ -0,0 +1,118 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/Conn.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteFileRecycleLogCatalogue::SqliteFileRecycleLogCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue)
+  : RdbmsFileRecycleLogCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void SqliteFileRecycleLogCatalogue::restoreEntryInRecycleLog(rdbms::Conn & conn,
+  FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid, log::LogContext & lc) {
+  try {
+    utils::Timer timer;
+    log::TimingList timingList;
+
+    if (!fileRecycleLogItor.hasMore()) {
+      throw cta::exception::UserError("No file in the recycle bin matches the parameters passed");
+    }
+    auto fileRecycleLog = fileRecycleLogItor.next();
+    if (fileRecycleLogItor.hasMore()) {
+      // stop restoring more than one file at once
+      throw cta::exception::UserError("More than one recycle bin file matches the parameters passed");
+    }
+
+    // We currently do all file copies restoring in a single transaction
+    conn.executeNonQuery("BEGIN TRANSACTION");
+    const auto archiveFileCatalogue = static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+    if (auto archiveFilePtr = archiveFileCatalogue->getArchiveFileById(conn, fileRecycleLog.archiveFileId);
+      !archiveFilePtr) {
+      RdbmsFileRecycleLogCatalogue::restoreArchiveFileInRecycleLog(conn, fileRecycleLog, newFid, lc);
+    } else {
+      if (archiveFilePtr->tapeFiles.find(fileRecycleLog.copyNb) != archiveFilePtr->tapeFiles.end()) {
+        // copy with same copy_nb exists, cannot restore
+        UserSpecifiedExistingDeletedFileCopy ex;
+        ex.getMessage() << "Cannot restore file copy with archiveFileId "
+          << std::to_string(fileRecycleLog.archiveFileId)
+          << " and copy_nb " << std::to_string(fileRecycleLog.copyNb)
+          << " because a tapefile with same archiveFileId and copy_nb already exists";
+        throw ex;
+      }
+    }
+
+    RdbmsFileRecycleLogCatalogue::restoreFileCopyInRecycleLog(conn, fileRecycleLog, lc);
+    conn.commit();
+
+    log::ScopedParamContainer spc(lc);
+    timingList.insertAndReset("commitTime", timer);
+    timingList.addToLog(spc);
+    lc.log(log::INFO, "In PostgresFileRecycleLogCatalogue::restoreEntryInRecycleLog: "
+      "all file copies successfully restored.");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+uint64_t SqliteFileRecycleLogCatalogue::getNextFileRecyleLogId(rdbms::Conn &conn) const {
+  try {
+    conn.executeNonQuery("INSERT INTO FILE_RECYCLE_LOG_ID VALUES(NULL)");
+    uint64_t fileRecycleLogId = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      fileRecycleLogId = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM FILE_RECYCLE_LOG_ID");
+
+    return fileRecycleLogId;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.hpp
new file mode 100644
index 0000000000..772bf7f26e
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteFileRecycleLogCatalogue.hpp
@@ -0,0 +1,62 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+
+namespace cta {
+
+namespace utils {
+class Timer;
+}
+
+namespace log {
+class TimingList;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteFileRecycleLogCatalogue : public RdbmsFileRecycleLogCatalogue {
+public:
+  SqliteFileRecycleLogCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+  ~SqliteFileRecycleLogCatalogue() override = default;
+
+private:
+
+  void restoreEntryInRecycleLog(rdbms::Conn & conn, FileRecycleLogItor &fileRecycleLogItor, const std::string &newFid,
+    log::LogContext & lc) override;
+
+  /**
+   * Copy the fileRecycleLog to the TAPE_FILE table and deletes the corresponding FILE_RECYCLE_LOG table entry
+   * @param conn the database connection
+   * @param fileRecycleLog the fileRecycleLog we want to restore
+   * @param lc the log context
+   */
+  void restoreFileCopyInRecycleLog(rdbms::Conn & conn, const common::dataStructures::FileRecycleLog &fileRecycleLog,
+    log::LogContext & lc);
+
+  uint64_t getNextFileRecyleLogId(rdbms::Conn & conn) const override;
+};  // class SqliteFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.cpp
new file mode 100644
index 0000000000..79fa62caf4
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.cpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteLogicalLibraryCatalogue::SqliteLogicalLibraryCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsLogicalLibraryCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t SqliteLogicalLibraryCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) const {
+  try {
+    conn.executeNonQuery("INSERT INTO LOGICAL_LIBRARY_ID VALUES(NULL)");
+    uint64_t logicalLibraryId = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      logicalLibraryId = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM LOGICAL_LIBRARY_ID");
+
+    return logicalLibraryId;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.hpp
new file mode 100644
index 0000000000..e495fb5bd3
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteLogicalLibraryCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsLogicalLibraryCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteLogicalLibraryCatalogue : public RdbmsLogicalLibraryCatalogue {
+public:
+  SqliteLogicalLibraryCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteLogicalLibraryCatalogue() override = default;
+
+private:
+  uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) const override;
+};  // class SqliteFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.cpp
new file mode 100644
index 0000000000..92979ce509
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.cpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteMediaTypeCatalogue::SqliteMediaTypeCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsMediaTypeCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t SqliteMediaTypeCatalogue::getNextMediaTypeId(rdbms::Conn &conn) const {
+  try {
+    conn.executeNonQuery("INSERT INTO MEDIA_TYPE_ID VALUES(NULL)");
+    uint64_t id = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      id = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM MEDIA_TYPE_ID");
+
+    return id;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.hpp
new file mode 100644
index 0000000000..360409d25a
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteMediaTypeCatalogue.hpp
@@ -0,0 +1,39 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include "catalogue/rdbms/RdbmsMediaTypeCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteMediaTypeCatalogue : public RdbmsMediaTypeCatalogue {
+public:
+  SqliteMediaTypeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteMediaTypeCatalogue() override = default;
+
+private:
+  uint64_t getNextMediaTypeId(rdbms::Conn &conn) const override;
+};  // class SqliteMediaTypeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.cpp
new file mode 100644
index 0000000000..b9f68cd5e4
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.cpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteStorageClassCatalogue::SqliteStorageClassCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsStorageClassCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t SqliteStorageClassCatalogue::getNextStorageClassId(rdbms::Conn &conn) {
+  try {
+    conn.executeNonQuery("INSERT INTO STORAGE_CLASS_ID VALUES(NULL)");
+    uint64_t storageClassId = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      storageClassId = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM STORAGE_CLASS_ID");
+
+    return storageClassId;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.hpp
new file mode 100644
index 0000000000..d653c24c0d
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteStorageClassCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsStorageClassCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteStorageClassCatalogue : public RdbmsStorageClassCatalogue {
+public:
+  SqliteStorageClassCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteStorageClassCatalogue() override = default;
+
+private:
+  uint64_t getNextStorageClassId(rdbms::Conn &conn) override;
+};  // class SqliteStorageClassCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteTapeCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteTapeCatalogue.cpp
new file mode 100644
index 0000000000..86c5d9668c
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteTapeCatalogue.cpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteTapeCatalogue::SqliteTapeCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsTapeCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t SqliteTapeCatalogue::getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "LAST_FSEQ AS LAST_FSEQ "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "VID = :VID;";
+
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VID", vid);
+    auto rset = stmt.executeQuery();
+    if (!rset.next()) {
+      throw exception::Exception(std::string("The tape with VID " + vid + " does not exist"));
+    }
+
+    return rset.columnUint64("LAST_FSEQ");
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp
new file mode 100644
index 0000000000..e426f9b70a
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp
@@ -0,0 +1,42 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapeCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteTapeCatalogue : public RdbmsTapeCatalogue {
+public:
+  SqliteTapeCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteTapeCatalogue() override = default;
+
+private:
+  friend class SqliteTapeFileCatalogue;
+  uint64_t getTapeLastFSeq(rdbms::Conn &conn, const std::string &vid) const override;
+};  // class SqliteTapeCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.cpp
new file mode 100644
index 0000000000..5f93b418eb
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.cpp
@@ -0,0 +1,202 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <string>
+
+#include "catalogue/ArchiveFileRow.hpp"
+#include "catalogue/ArchiveFileRowWithoutTimestamps.hpp"
+#include "catalogue/rdbms/RdbmsArchiveFileCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/rdbms/RdbmsFileRecycleLogCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteTapeCatalogue.hpp"
+#include "catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/TapeItemWritten.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/FileSizeMismatch.hpp"
+#include "common/exception/TapeFseqMismatch.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/TimingList.hpp"
+#include "common/Timer.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+#include "rdbms/PrimaryKeyError.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteTapeFileCatalogue::SqliteTapeFileCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue *rdbmsCatalogue)
+  : RdbmsTapeFileCatalogue(log, connPool, rdbmsCatalogue) {}
+
+void SqliteTapeFileCatalogue::copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+  const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+  log::TimingList *timingList, log::LogContext & lc) const {
+  conn.executeNonQuery("BEGIN TRANSACTION");
+  const auto fileRecycleLogCatalogue = static_cast<RdbmsFileRecycleLogCatalogue*>(
+    RdbmsTapeFileCatalogue::m_rdbmsCatalogue->FileRecycleLog().get());
+  fileRecycleLogCatalogue->copyTapeFilesToFileRecycleLog(conn, file, reason);
+  timingList->insertAndReset("insertToRecycleBinTime", *timer);
+  RdbmsCatalogueUtils::setTapeDirty(conn, file.archiveFileID);
+  timingList->insertAndReset("setTapeDirtyTime", *timer);
+  deleteTapeFiles(conn, file);
+  timingList->insertAndReset("deleteTapeFilesTime", *timer);
+  conn.commit();
+}
+
+void SqliteTapeFileCatalogue::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &events) {
+  try {
+    if(events.empty()) {
+      return;
+    }
+
+    auto firstEventItor = events.cbegin();
+    const auto &firstEvent = **firstEventItor;;
+    checkTapeItemWrittenFieldsAreSet(__FUNCTION__, firstEvent);
+
+    // The SQLite implementation of this method relies on the fact that a tape
+    // cannot be physically mounted in two or more drives at the same time
+    //
+    // Given the above assumption regarding the laws of physics, a simple lock
+    // on the mutex of the SqliteCatalogue object is enough to emulate an
+    // Oracle SELECT FOR UPDATE
+    threading::MutexLocker locker(m_rdbmsCatalogue->m_mutex);
+    auto conn = m_connPool->getConn();
+
+    const uint64_t lastFSeq
+      = static_cast<SqliteTapeCatalogue*>(m_rdbmsCatalogue->Tape().get())->getTapeLastFSeq(conn, firstEvent.vid);
+    uint64_t expectedFSeq = lastFSeq + 1;
+    uint64_t totalLogicalBytesWritten = 0;
+    uint64_t filesCount = 0;
+
+    for(const auto &eventP: events) {
+      const auto & event = *eventP;
+      checkTapeItemWrittenFieldsAreSet(__FUNCTION__, event);
+
+      if(event.vid != firstEvent.vid) {
+        throw exception::Exception(std::string("VID mismatch: expected=") + firstEvent.vid + " actual=" + event.vid);
+      }
+
+      if(expectedFSeq != event.fSeq) {
+        exception::TapeFseqMismatch ex;
+        ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
+          firstEvent.fSeq;
+        throw ex;
+      }
+      expectedFSeq++;
+
+
+      try {
+        // If this is a file (as opposed to a placeholder), do the full processing.
+        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(event);
+        totalLogicalBytesWritten += fileEvent.size;
+        filesCount++;
+      } catch (std::bad_cast&) {}
+    }
+
+    auto lastEventItor = events.cend();
+    lastEventItor--;
+    const TapeItemWritten &lastEvent = **lastEventItor;
+    RdbmsCatalogueUtils::updateTape(conn, lastEvent.vid, lastEvent.fSeq, totalLogicalBytesWritten, filesCount,
+      lastEvent.tapeDrive);
+
+    for(const auto &event : events) {
+      try {
+        // If this is a file (as opposed to a placeholder), do the full processing.
+        const auto &fileEvent=dynamic_cast<const TapeFileWritten &>(*event);
+        fileWrittenToTape(conn, fileEvent);
+      } catch (std::bad_cast&) {}
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void SqliteTapeFileCatalogue::fileWrittenToTape(rdbms::Conn &conn, const TapeFileWritten &event) {
+  try {
+    checkTapeFileWrittenFieldsAreSet(__FUNCTION__, event);
+
+    // Try to insert a row into the ARCHIVE_FILE table - it is normal this will
+    // fail if another tape copy has already been written to tape
+    try {
+      ArchiveFileRowWithoutTimestamps row;
+      row.archiveFileId = event.archiveFileId;
+      row.diskFileId = event.diskFileId;
+      row.diskInstance = event.diskInstance;
+      row.size = event.size;
+      row.checksumBlob = event.checksumBlob;
+      row.storageClassName = event.storageClassName;
+      row.diskFileOwnerUid = event.diskFileOwnerUid;
+      row.diskFileGid = event.diskFileGid;
+      static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get())->insertArchiveFile(conn, row);
+    } catch(rdbms::PrimaryKeyError &) {
+      // Ignore this error
+    } catch(...) {
+      throw;
+    }
+
+    const time_t now = time(nullptr);
+    const auto archiveFileCatalogue = static_cast<RdbmsArchiveFileCatalogue*>(m_rdbmsCatalogue->ArchiveFile().get());
+    const auto archiveFileRow = archiveFileCatalogue->getArchiveFileRowById(conn, event.archiveFileId);
+
+    if(nullptr == archiveFileRow) {
+      // This should never happen
+      exception::Exception ex;
+      ex.getMessage() << "Failed to find archive file row: archiveFileId=" << event.archiveFileId;
+      throw ex;
+    }
+
+    std::ostringstream fileContext;
+    fileContext << "archiveFileId=" << event.archiveFileId << ", diskInstanceName=" << event.diskInstance <<
+      ", diskFileId=" << event.diskFileId;
+
+    if(archiveFileRow->size != event.size) {
+      catalogue::FileSizeMismatch ex;
+      ex.getMessage() << "File size mismatch: expected=" << archiveFileRow->size << ", actual=" << event.size << ": "
+        << fileContext.str();
+      throw ex;
+    }
+
+    archiveFileRow->checksumBlob.validate(event.checksumBlob);
+
+    // Insert the tape file
+    common::dataStructures::TapeFile tapeFile;
+    tapeFile.vid            = event.vid;
+    tapeFile.fSeq           = event.fSeq;
+    tapeFile.blockId        = event.blockId;
+    tapeFile.fileSize       = event.size;
+    tapeFile.copyNb         = event.copyNb;
+    tapeFile.creationTime   = now;
+    insertTapeFile(conn, tapeFile, event.archiveFileId);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.hpp
new file mode 100644
index 0000000000..2ae92a01dd
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteTapeFileCatalogue.hpp
@@ -0,0 +1,55 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapeFileCatalogue.hpp"
+
+namespace cta {
+
+namespace utils {
+class Timer;
+}
+
+namespace log {
+class TimingList;
+}
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteTapeFileCatalogue : public RdbmsTapeFileCatalogue {
+public:
+  SqliteTapeFileCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue *rdbmsCatalogue);
+  ~SqliteTapeFileCatalogue() override = default;
+
+  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override;
+
+private:
+  void  copyTapeFileToFileRecyleLogAndDeleteTransaction(rdbms::Conn & conn,
+    const cta::common::dataStructures::ArchiveFile &file, const std::string &reason, utils::Timer *timer,
+    log::TimingList *timingList, log::LogContext & lc) const override;
+
+  void fileWrittenToTape(rdbms::Conn &conn, const TapeFileWritten &event);
+};  // class SqliteTapeFileCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.cpp
new file mode 100644
index 0000000000..a4b27104ff
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.cpp
@@ -0,0 +1,60 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteTapePoolCatalogue::SqliteTapePoolCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsTapePoolCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t SqliteTapePoolCatalogue::getNextTapePoolId(rdbms::Conn &conn) const {
+  try {
+    conn.executeNonQuery("INSERT INTO TAPE_POOL_ID VALUES(NULL)");
+    uint64_t tapePoolId = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      tapePoolId = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM TAPE_POOL_ID");
+
+    return tapePoolId;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.hpp
new file mode 100644
index 0000000000..5536d31527
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteTapePoolCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsTapePoolCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteTapePoolCatalogue : public RdbmsTapePoolCatalogue {
+public:
+  SqliteTapePoolCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteTapePoolCatalogue() override = default;
+
+private:
+  uint64_t getNextTapePoolId(rdbms::Conn &conn) const override;
+};  // class SqliteTapePoolCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.cpp b/catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.cpp
new file mode 100644
index 0000000000..7d7b7b1a84
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.cpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "rdbms/Conn.hpp"
+#include "rdbms/ConnPool.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SqliteVirtualOrganizationCatalogue::SqliteVirtualOrganizationCatalogue(log::Logger &log,
+  std::shared_ptr<rdbms::ConnPool> connPool, RdbmsCatalogue* rdbmsCatalogue)
+  : RdbmsVirtualOrganizationCatalogue(log, connPool, rdbmsCatalogue) {}
+
+uint64_t SqliteVirtualOrganizationCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
+  try {
+    conn.executeNonQuery("INSERT INTO VIRTUAL_ORGANIZATION_ID VALUES(NULL)");
+    uint64_t virtualOrganizationId = 0;
+    {
+      const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
+      auto stmt = conn.createStmt(sql);
+      auto rset = stmt.executeQuery();
+      if(!rset.next()) {
+        throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
+      }
+      virtualOrganizationId = rset.columnUint64("ID");
+      if(rset.next()) {
+        throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
+      }
+    }
+    conn.executeNonQuery("DELETE FROM VIRTUAL_ORGANIZATION_ID");
+
+    return virtualOrganizationId;
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.hpp b/catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.hpp
new file mode 100644
index 0000000000..d5d21cf3a6
--- /dev/null
+++ b/catalogue/rdbms/sqlite/SqliteVirtualOrganizationCatalogue.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "catalogue/rdbms/RdbmsVirtualOrganizationCatalogue.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+class RdbmsCatalogue;
+
+class SqliteVirtualOrganizationCatalogue : public RdbmsVirtualOrganizationCatalogue {
+public:
+  SqliteVirtualOrganizationCatalogue(log::Logger &log, std::shared_ptr<rdbms::ConnPool> connPool,
+    RdbmsCatalogue* rdbmsCatalogue);
+  ~SqliteVirtualOrganizationCatalogue() override = default;
+
+private:
+  uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
+};  // class SqliteFileRecycleLogCatalogue
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..bb9ce62f4f
--- /dev/null
+++ b/catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.cpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+
+namespace cta {
+namespace catalogue {
+
+AdminUserCatalogueRetryWrapper::AdminUserCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void AdminUserCatalogueRetryWrapper::createAdminUser(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &username, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->AdminUser()->createAdminUser(admin, username, comment);},
+    m_maxTriesToConnect);
+}
+
+void AdminUserCatalogueRetryWrapper::deleteAdminUser(const std::string &username) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->AdminUser()->deleteAdminUser(username);},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::AdminUser> AdminUserCatalogueRetryWrapper::getAdminUsers() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->AdminUser()->getAdminUsers();}, m_maxTriesToConnect);
+}
+
+void AdminUserCatalogueRetryWrapper::modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &username, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->AdminUser()->modifyAdminUserComment(admin, username,
+    comment);},
+    m_maxTriesToConnect);
+}
+
+bool AdminUserCatalogueRetryWrapper::isAdmin(const common::dataStructures::SecurityIdentity &identity) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->AdminUser()->isAdmin(identity);}, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..796dd818de
--- /dev/null
+++ b/catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.hpp
@@ -0,0 +1,59 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/AdminUserCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class AdminUserCatalogueRetryWrapper : public AdminUserCatalogue {
+public:
+  AdminUserCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~AdminUserCatalogueRetryWrapper() override = default;
+
+  void createAdminUser(const common::dataStructures::SecurityIdentity &admin, const std::string &username,
+    const std::string &comment) override;
+
+  void deleteAdminUser(const std::string &username) override;
+
+  std::list<common::dataStructures::AdminUser> getAdminUsers() const override;
+
+  void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &username, const std::string &comment) override;
+
+  bool isAdmin(const common::dataStructures::SecurityIdentity &identity) const override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..9f8c7c3cd3
--- /dev/null
+++ b/catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.cpp
@@ -0,0 +1,127 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
+#include "common/dataStructures/ArchiveFileSummary.hpp"
+
+namespace cta {
+namespace catalogue {
+
+ArchiveFileCatalogueRetryWrapper::ArchiveFileCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+uint64_t ArchiveFileCatalogueRetryWrapper::checkAndGetNextArchiveFileId(const std::string &diskInstanceName,
+  const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName, storageClassName, user);
+  }, m_maxTriesToConnect);
+}
+
+common::dataStructures::ArchiveFileQueueCriteria ArchiveFileCatalogueRetryWrapper::getArchiveFileQueueCriteria(
+  const std::string &diskInstanceName, const std::string &storageClassName,
+  const common::dataStructures::RequesterIdentity &user) {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getArchiveFileQueueCriteria(diskInstanceName, storageClassName, user);
+  }, m_maxTriesToConnect);
+}
+
+ArchiveFileItor ArchiveFileCatalogueRetryWrapper::getArchiveFilesItor(
+  const TapeFileSearchCriteria &searchCriteria) const {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+  }, m_maxTriesToConnect);
+}
+
+common::dataStructures::ArchiveFile ArchiveFileCatalogueRetryWrapper::getArchiveFileForDeletion(
+  const TapeFileSearchCriteria &searchCriteria) const {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getArchiveFileForDeletion(searchCriteria);
+  }, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::ArchiveFile> ArchiveFileCatalogueRetryWrapper::getFilesForRepack(
+  const std::string &vid, const uint64_t startFSeq, const uint64_t maxNbFiles) const {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFSeq, maxNbFiles);
+  }, m_maxTriesToConnect);
+}
+
+ArchiveFileItor ArchiveFileCatalogueRetryWrapper::getArchiveFilesForRepackItor(const std::string &vid,
+  const uint64_t startFSeq) const {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getArchiveFilesForRepackItor(vid, startFSeq);
+  }, m_maxTriesToConnect);
+}
+
+common::dataStructures::ArchiveFileSummary ArchiveFileCatalogueRetryWrapper::getTapeFileSummary(
+  const TapeFileSearchCriteria &searchCriteria) const {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+  }, m_maxTriesToConnect);
+}
+
+common::dataStructures::ArchiveFile ArchiveFileCatalogueRetryWrapper::getArchiveFileById(const uint64_t id) const {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->getArchiveFileById(id);
+  }, m_maxTriesToConnect);
+}
+
+void ArchiveFileCatalogueRetryWrapper::modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+  const std::string& newStorageClassName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveFile()->modifyArchiveFileStorageClassId(
+    archiveFileId, newStorageClassName);}, m_maxTriesToConnect);
+}
+
+void ArchiveFileCatalogueRetryWrapper::modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId,
+  const std::string& fxId, const std::string &diskInstance) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveFile()->modifyArchiveFileFxIdAndDiskInstance(
+    archiveId, fxId, diskInstance);}, m_maxTriesToConnect);
+}
+
+void ArchiveFileCatalogueRetryWrapper::DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName,
+  const uint64_t archiveFileId, log::LogContext &lc) {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(diskInstanceName, archiveFileId, lc);
+  }, m_maxTriesToConnect);
+}
+
+void ArchiveFileCatalogueRetryWrapper::updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
+  const std::string &diskFileId) {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->updateDiskFileId(archiveFileId, diskInstance, diskFileId);
+  }, m_maxTriesToConnect);
+}
+
+void ArchiveFileCatalogueRetryWrapper::moveArchiveFileToRecycleLog(
+  const common::dataStructures::DeleteArchiveRequest &request, log::LogContext & lc) {
+  return retryOnLostConnection(m_log, [&]{
+    return m_catalogue->ArchiveFile()->moveArchiveFileToRecycleLog(request, lc);
+  }, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..ac830e5d6c
--- /dev/null
+++ b/catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.hpp
@@ -0,0 +1,84 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/ArchiveFileCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class ArchiveFileCatalogueRetryWrapper : public ArchiveFileCatalogue {
+public:
+  ArchiveFileCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~ArchiveFileCatalogueRetryWrapper() override = default;
+
+  uint64_t checkAndGetNextArchiveFileId(const std::string &diskInstanceName, const std::string &storageClassName,
+    const common::dataStructures::RequesterIdentity &user) override;
+
+  common::dataStructures::ArchiveFileQueueCriteria getArchiveFileQueueCriteria(const std::string &diskInstanceName,
+    const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user) override;
+
+  ArchiveFileItor getArchiveFilesItor(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  common::dataStructures::ArchiveFile getArchiveFileForDeletion(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  std::list<common::dataStructures::ArchiveFile> getFilesForRepack(const std::string &vid, const uint64_t startFSeq,
+    const uint64_t maxNbFiles) const override;
+
+  ArchiveFileItor getArchiveFilesForRepackItor(const std::string &vid, const uint64_t startFSeq) const override;
+
+  common::dataStructures::ArchiveFileSummary getTapeFileSummary(
+    const TapeFileSearchCriteria &searchCriteria = TapeFileSearchCriteria()) const override;
+
+  common::dataStructures::ArchiveFile getArchiveFileById(const uint64_t id) const override;
+
+  void modifyArchiveFileStorageClassId(const uint64_t archiveFileId,
+    const std::string& newStorageClassName) const override;
+
+  void modifyArchiveFileFxIdAndDiskInstance(const uint64_t archiveId, const std::string& fxId,
+    const std::string &diskInstance) const override;
+
+  void moveArchiveFileToRecycleLog(const common::dataStructures::DeleteArchiveRequest &request,
+    log::LogContext & lc) override;
+
+  void updateDiskFileId(uint64_t archiveFileId, const std::string &diskInstance,
+    const std::string &diskFileId) override;
+
+  void DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(const std::string &diskInstanceName, const uint64_t archiveFileId,
+    log::LogContext &lc) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..6c21cffef6
--- /dev/null
+++ b/catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.cpp
@@ -0,0 +1,70 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/ArchiveRoute.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+
+namespace cta {
+namespace catalogue {
+
+ArchiveRouteCatalogueRetryWrapper::ArchiveRouteCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void ArchiveRouteCatalogueRetryWrapper::createArchiveRoute(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName,
+  const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveRoute()->createArchiveRoute(admin,
+    storageClassName, copyNb, tapePoolName, comment);}, m_maxTriesToConnect);
+}
+
+void ArchiveRouteCatalogueRetryWrapper::deleteArchiveRoute(const std::string &storageClassName, const uint32_t copyNb) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveRoute()->deleteArchiveRoute(storageClassName,
+    copyNb);}, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::ArchiveRoute> ArchiveRouteCatalogueRetryWrapper::getArchiveRoutes() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveRoute()->getArchiveRoutes();},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::ArchiveRoute> ArchiveRouteCatalogueRetryWrapper::getArchiveRoutes(
+  const std::string &storageClassName, const std::string &tapePoolName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveRoute()->getArchiveRoutes(storageClassName,
+    tapePoolName);}, m_maxTriesToConnect);
+}
+
+void ArchiveRouteCatalogueRetryWrapper::modifyArchiveRouteTapePoolName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName, const uint32_t copyNb,
+  const std::string &tapePoolName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveRoute()->modifyArchiveRouteTapePoolName(admin,
+    storageClassName, copyNb, tapePoolName);}, m_maxTriesToConnect);
+}
+
+void ArchiveRouteCatalogueRetryWrapper::modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->ArchiveRoute()->modifyArchiveRouteComment(admin,
+    storageClassName, copyNb, comment);}, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..70ce4a8ca4
--- /dev/null
+++ b/catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.hpp
@@ -0,0 +1,63 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/ArchiveRouteCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class ArchiveRouteCatalogueRetryWrapper : public ArchiveRouteCatalogue {
+public:
+  ArchiveRouteCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~ArchiveRouteCatalogueRetryWrapper() override = default;
+
+  void createArchiveRoute(const common::dataStructures::SecurityIdentity &admin, const std::string &storageClassName,
+    const uint32_t copyNb, const std::string &tapePoolName, const std::string &comment) override;
+
+  void deleteArchiveRoute(const std::string &storageClassName, const uint32_t copyNb) override;
+
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes() const override;
+
+  std::list<common::dataStructures::ArchiveRoute> getArchiveRoutes(const std::string &storageClassName,
+    const std::string &tapePoolName) const override;
+
+  void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName) override;
+
+  void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &storageClassName, const uint32_t copyNb, const std::string &comment) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/CatalogueRetryWrapper.cpp b/catalogue/retrywrappers/CatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..e7f9a7473e
--- /dev/null
+++ b/catalogue/retrywrappers/CatalogueRetryWrapper.cpp
@@ -0,0 +1,163 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/retrywrappers/AdminUserCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/ArchiveFileCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/ArchiveRouteCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/CatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/SchemaCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/TapeCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/TapePoolCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+CatalogueRetryWrapper::CatalogueRetryWrapper(log::Logger &log, std::unique_ptr<Catalogue> catalogue,
+  const uint32_t maxTriesToConnect):
+  m_log(log),
+  m_catalogue(std::move(catalogue)),
+  m_maxTriesToConnect(maxTriesToConnect),
+  m_schema(std::make_unique<SchemaCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_adminUser(std::make_unique<AdminUserCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_diskSystem(std::make_unique<DiskSystemCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_diskInstance(std::make_unique<DiskInstanceCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_diskInstanceSpace(std::make_unique<DiskInstanceSpaceCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_vo(std::make_unique<VirtualOrganizationCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_archiveRoute(std::make_unique<ArchiveRouteCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_mediaType(std::make_unique<MediaTypeCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_storageClass(std::make_unique<StorageClassCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_tapePool(std::make_unique<TapePoolCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_tape(std::make_unique<TapeCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_mountPolicy(std::make_unique<MountPolicyCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_requesterActivityMountRule(std::make_unique<RequesterActivityMountRuleCatalogueRetryWrapper>(m_catalogue, m_log,
+    m_maxTriesToConnect)),
+  m_requesterMountRule(std::make_unique<RequesterMountRuleCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_requesterGroupMountRule(std::make_unique<RequesterGroupMountRuleCatalogueRetryWrapper>(m_catalogue, m_log,
+    m_maxTriesToConnect)),
+  m_logicalLibrary(std::make_unique<LogicalLibraryCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_tapeFile(std::make_unique<TapeFileCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_fileRecycleLog(std::make_unique<FileRecycleLogCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_driveConfig(std::make_unique<DriveConfigCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_driveState(std::make_unique<DriveStateCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)),
+  m_archiveFile(std::make_unique<ArchiveFileCatalogueRetryWrapper>(m_catalogue, m_log, m_maxTriesToConnect)) {
+}
+
+const std::unique_ptr<SchemaCatalogue>& CatalogueRetryWrapper::Schema() {
+  return m_schema;
+}
+
+const std::unique_ptr<AdminUserCatalogue>& CatalogueRetryWrapper::AdminUser() {
+  return m_adminUser;
+}
+
+const std::unique_ptr<DiskSystemCatalogue>& CatalogueRetryWrapper::DiskSystem() {
+  return m_diskSystem;
+}
+
+const std::unique_ptr<DiskInstanceCatalogue>& CatalogueRetryWrapper::DiskInstance() {
+  return m_diskInstance;
+}
+
+const std::unique_ptr<DiskInstanceSpaceCatalogue>& CatalogueRetryWrapper::DiskInstanceSpace() {
+  return m_diskInstanceSpace;
+}
+
+const std::unique_ptr<VirtualOrganizationCatalogue>& CatalogueRetryWrapper::VO() {
+  return m_vo;
+}
+
+const std::unique_ptr<ArchiveRouteCatalogue>& CatalogueRetryWrapper::ArchiveRoute() {
+  return m_archiveRoute;
+}
+
+const std::unique_ptr<MediaTypeCatalogue>& CatalogueRetryWrapper::MediaType() {
+  return m_mediaType;
+}
+
+const std::unique_ptr<StorageClassCatalogue>& CatalogueRetryWrapper::StorageClass() {
+  return m_storageClass;
+}
+
+const std::unique_ptr<TapePoolCatalogue>& CatalogueRetryWrapper::TapePool() {
+  return m_tapePool;
+}
+
+const std::unique_ptr<TapeCatalogue>& CatalogueRetryWrapper::Tape() {
+  return m_tape;
+}
+
+const std::unique_ptr<MountPolicyCatalogue>& CatalogueRetryWrapper::MountPolicy() {
+  return m_mountPolicy;
+}
+
+const std::unique_ptr<RequesterActivityMountRuleCatalogue>& CatalogueRetryWrapper::RequesterActivityMountRule() {
+  return m_requesterActivityMountRule;
+}
+
+const std::unique_ptr<RequesterMountRuleCatalogue>& CatalogueRetryWrapper::RequesterMountRule() {
+  return m_requesterMountRule;
+}
+
+const std::unique_ptr<RequesterGroupMountRuleCatalogue>& CatalogueRetryWrapper::RequesterGroupMountRule() {
+  return m_requesterGroupMountRule;
+}
+
+const std::unique_ptr<LogicalLibraryCatalogue>& CatalogueRetryWrapper::LogicalLibrary() {
+  return m_logicalLibrary;
+}
+
+const std::unique_ptr<TapeFileCatalogue>& CatalogueRetryWrapper::TapeFile() {
+  return m_tapeFile;
+}
+
+const std::unique_ptr<FileRecycleLogCatalogue>& CatalogueRetryWrapper::FileRecycleLog() {
+  return m_fileRecycleLog;
+}
+
+const std::unique_ptr<DriveConfigCatalogue>& CatalogueRetryWrapper::DriveConfig() {
+  return m_driveConfig;
+}
+
+const std::unique_ptr<ArchiveFileCatalogue>& CatalogueRetryWrapper::ArchiveFile() {
+  return m_archiveFile;
+}
+
+const std::unique_ptr<DriveStateCatalogue>& CatalogueRetryWrapper::DriveState() {
+  return m_driveState;
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/CatalogueRetryWrapper.hpp b/catalogue/retrywrappers/CatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..29b6973171
--- /dev/null
+++ b/catalogue/retrywrappers/CatalogueRetryWrapper.hpp
@@ -0,0 +1,116 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+
+namespace catalogue {
+
+/**
+ * Wrapper around a CTA catalogue object that retries a method if a
+ * LostConnectionException is thrown.
+ */
+class CatalogueRetryWrapper: public Catalogue {
+public:
+  /**
+   * Constructor.
+   *
+   * @param log Object representing the API to the CTA logging system.
+   * @param catalogue The catalogue to be wrapped.
+   * @param maxTriesToConnect The maximum number of times a single method should
+   * try to connect to the database in the event of LostDatabaseConnection
+   * exceptions being thrown.
+   */
+  CatalogueRetryWrapper(log::Logger &log, std::unique_ptr<Catalogue> catalogue, const uint32_t maxTriesToConnect = 3);
+
+  CatalogueRetryWrapper(CatalogueRetryWrapper &) = delete;
+
+  ~CatalogueRetryWrapper() override = default;
+
+  CatalogueRetryWrapper &operator=(const CatalogueRetryWrapper &) = delete;
+
+  const std::unique_ptr<SchemaCatalogue>& Schema() override;
+  const std::unique_ptr<AdminUserCatalogue>& AdminUser() override;
+  const std::unique_ptr<DiskSystemCatalogue>& DiskSystem() override;
+  const std::unique_ptr<DiskInstanceCatalogue>& DiskInstance() override;
+  const std::unique_ptr<DiskInstanceSpaceCatalogue>& DiskInstanceSpace() override;
+  const std::unique_ptr<VirtualOrganizationCatalogue>& VO() override;
+  const std::unique_ptr<ArchiveRouteCatalogue>& ArchiveRoute() override;
+  const std::unique_ptr<MediaTypeCatalogue>& MediaType() override;
+  const std::unique_ptr<StorageClassCatalogue>& StorageClass() override;
+  const std::unique_ptr<TapePoolCatalogue>& TapePool() override;
+  const std::unique_ptr<TapeCatalogue>& Tape() override;
+  const std::unique_ptr<MountPolicyCatalogue>& MountPolicy() override;
+  const std::unique_ptr<RequesterActivityMountRuleCatalogue>& RequesterActivityMountRule() override;
+  const std::unique_ptr<RequesterMountRuleCatalogue>& RequesterMountRule() override;
+  const std::unique_ptr<RequesterGroupMountRuleCatalogue>& RequesterGroupMountRule() override;
+  const std::unique_ptr<LogicalLibraryCatalogue>& LogicalLibrary() override;
+  const std::unique_ptr<DriveConfigCatalogue>& DriveConfig() override;
+  const std::unique_ptr<DriveStateCatalogue>& DriveState() override;
+  const std::unique_ptr<TapeFileCatalogue>& TapeFile() override;
+  const std::unique_ptr<FileRecycleLogCatalogue>& FileRecycleLog() override;
+  const std::unique_ptr<ArchiveFileCatalogue>& ArchiveFile() override;
+
+protected:
+  /**
+   * Object representing the API to the CTA logging system.
+   */
+  log::Logger &m_log;
+
+  /**
+   * The wrapped catalogue.
+   */
+  std::unique_ptr<Catalogue> m_catalogue;
+
+  /**
+   * The maximum number of times a single method should try to connect to the
+   * database in the event of LostDatabaseConnection exceptions being thrown.
+   */
+  uint32_t m_maxTriesToConnect;
+
+private:
+  std::unique_ptr<SchemaCatalogue> m_schema;
+  std::unique_ptr<AdminUserCatalogue> m_adminUser;
+  std::unique_ptr<DiskSystemCatalogue> m_diskSystem;
+  std::unique_ptr<DiskInstanceCatalogue> m_diskInstance;
+  std::unique_ptr<DiskInstanceSpaceCatalogue> m_diskInstanceSpace;
+  std::unique_ptr<VirtualOrganizationCatalogue> m_vo;
+  std::unique_ptr<ArchiveRouteCatalogue> m_archiveRoute;
+  std::unique_ptr<MediaTypeCatalogue> m_mediaType;
+  std::unique_ptr<StorageClassCatalogue> m_storageClass;
+  std::unique_ptr<TapePoolCatalogue> m_tapePool;
+  std::unique_ptr<TapeCatalogue> m_tape;
+  std::unique_ptr<MountPolicyCatalogue> m_mountPolicy;
+  std::unique_ptr<RequesterActivityMountRuleCatalogue> m_requesterActivityMountRule;
+  std::unique_ptr<RequesterMountRuleCatalogue> m_requesterMountRule;
+  std::unique_ptr<RequesterGroupMountRuleCatalogue> m_requesterGroupMountRule;
+  std::unique_ptr<LogicalLibraryCatalogue> m_logicalLibrary;
+  std::unique_ptr<TapeFileCatalogue> m_tapeFile;
+  std::unique_ptr<FileRecycleLogCatalogue> m_fileRecycleLog;
+  std::unique_ptr<DriveConfigCatalogue> m_driveConfig;
+  std::unique_ptr<DriveStateCatalogue> m_driveState;
+  std::unique_ptr<ArchiveFileCatalogue> m_archiveFile;
+};  // class CatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..6c68450833
--- /dev/null
+++ b/catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.cpp
@@ -0,0 +1,61 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+DiskInstanceCatalogueRetryWrapper::DiskInstanceCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void DiskInstanceCatalogueRetryWrapper::createDiskInstance(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{
+      return m_catalogue->DiskInstance()->createDiskInstance(admin, name, comment);
+    }, m_maxTriesToConnect);
+}
+
+void DiskInstanceCatalogueRetryWrapper::deleteDiskInstance(const std::string &name) {
+  return retryOnLostConnection(m_log, [&]{
+      return m_catalogue->DiskInstance()->deleteDiskInstance(name);
+    }, m_maxTriesToConnect);
+}
+
+void DiskInstanceCatalogueRetryWrapper::modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{
+      return m_catalogue->DiskInstance()->modifyDiskInstanceComment(admin, name, comment);
+    }, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::DiskInstance> DiskInstanceCatalogueRetryWrapper::getAllDiskInstances() const {
+  return retryOnLostConnection(m_log, [&]{
+      return m_catalogue->DiskInstance()->getAllDiskInstances();
+    }, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..fdb254267b
--- /dev/null
+++ b/catalogue/retrywrappers/DiskInstanceCatalogueRetryWrapper.hpp
@@ -0,0 +1,57 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/DiskInstanceCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class DiskInstanceCatalogueRetryWrapper : public DiskInstanceCatalogue {
+public:
+  DiskInstanceCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~DiskInstanceCatalogueRetryWrapper() override = default;
+
+  void createDiskInstance(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+  void deleteDiskInstance(const std::string &name) override;
+
+  void modifyDiskInstanceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  std::list<common::dataStructures::DiskInstance> getAllDiskInstances() const override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class DiskInstanceCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..b1d9cfe58b
--- /dev/null
+++ b/catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.cpp
@@ -0,0 +1,88 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/DiskInstanceSpace.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+DiskInstanceSpaceCatalogueRetryWrapper::DiskInstanceSpaceCatalogueRetryWrapper(
+  const std::unique_ptr<Catalogue>& catalogue, log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void DiskInstanceSpaceCatalogueRetryWrapper::deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->deleteDiskInstanceSpace(name,
+    diskInstance);},
+    m_maxTriesToConnect);
+}
+
+void DiskInstanceSpaceCatalogueRetryWrapper::createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name,
+  const std::string &diskInstance,
+  const std::string &freeSpaceQueryURL,
+  const uint64_t refreshInterval,
+  const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(admin, name,
+    diskInstance, freeSpaceQueryURL, refreshInterval, comment);},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::DiskInstanceSpace> DiskInstanceSpaceCatalogueRetryWrapper::getAllDiskInstanceSpaces() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();},
+    m_maxTriesToConnect);
+}
+
+void DiskInstanceSpaceCatalogueRetryWrapper::modifyDiskInstanceSpaceComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceComment(admin, name,
+    diskInstance, comment);},
+    m_maxTriesToConnect);
+}
+
+void DiskInstanceSpaceCatalogueRetryWrapper::modifyDiskInstanceSpaceRefreshInterval(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const uint64_t refreshInterval) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceRefreshInterval(admin, name,
+    diskInstance, refreshInterval);},
+    m_maxTriesToConnect);
+}
+
+void DiskInstanceSpaceCatalogueRetryWrapper::modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+  const std::string &diskInstance, const uint64_t freeSpace) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceFreeSpace(name,
+    diskInstance, freeSpace);},
+    m_maxTriesToConnect);
+}
+
+void DiskInstanceSpaceCatalogueRetryWrapper::modifyDiskInstanceSpaceQueryURL(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstance,
+  const std::string &freeSpaceQueryURL) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceQueryURL(admin, name,
+    diskInstance, freeSpaceQueryURL);},
+    m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..859c82eae3
--- /dev/null
+++ b/catalogue/retrywrappers/DiskInstanceSpaceCatalogueRetryWrapper.hpp
@@ -0,0 +1,70 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/DiskInstanceSpaceCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class DiskInstanceSpaceCatalogueRetryWrapper : public DiskInstanceSpaceCatalogue {
+public:
+  DiskInstanceSpaceCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~DiskInstanceSpaceCatalogueRetryWrapper() override = default;
+
+  void deleteDiskInstanceSpace(const std::string &name, const std::string &diskInstance) override;
+
+  void createDiskInstanceSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name,
+    const std::string &diskInstance,
+    const std::string &freeSpaceQueryURL,
+    const uint64_t refreshInterval,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::DiskInstanceSpace> getAllDiskInstanceSpaces() const override;
+
+  void modifyDiskInstanceSpaceComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &comment) override;
+
+  void modifyDiskInstanceSpaceRefreshInterval(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const uint64_t refreshInterval) override;
+
+  void modifyDiskInstanceSpaceFreeSpace(const std::string &name,
+    const std::string &diskInstance, const uint64_t freeSpace) override;
+
+  void modifyDiskInstanceSpaceQueryURL(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstance, const std::string &freeSpaceQueryURL) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class DiskInstancSpaceCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..bca23dbf3f
--- /dev/null
+++ b/catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.cpp
@@ -0,0 +1,93 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/log/Logger.hpp"
+#include "disk/DiskSystem.hpp"
+
+namespace cta {
+namespace catalogue {
+
+DiskSystemCatalogueRetryWrapper::DiskSystemCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void DiskSystemCatalogueRetryWrapper::createDiskSystem(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &diskInstanceName, const std::string &diskInstanceSpaceName,
+  const std::string &fileRegexp, const uint64_t targetedFreeSpace, const time_t sleepTime,const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->createDiskSystem(admin, name,
+    diskInstanceName, diskInstanceSpaceName, fileRegexp, targetedFreeSpace, sleepTime, comment);}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::deleteDiskSystem(const std::string &name) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->deleteDiskSystem(name);},
+    m_maxTriesToConnect);
+}
+
+disk::DiskSystemList DiskSystemCatalogueRetryWrapper::getAllDiskSystems() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->getAllDiskSystems();}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &fileRegexp) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->modifyDiskSystemFileRegexp(admin, name,
+    fileRegexp);}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::modifyDiskSystemTargetedFreeSpace(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+  const uint64_t targetedFreeSpace) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->modifyDiskSystemTargetedFreeSpace(admin,
+    name, targetedFreeSpace);}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->modifyDiskSystemComment(admin, name,
+    comment);}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
+  const std::string& name, const uint64_t sleepTime) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->modifyDiskSystemSleepTime(admin, name,
+    sleepTime);}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::modifyDiskSystemDiskInstanceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &diskInstanceName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->modifyDiskSystemDiskInstanceName(admin,
+    name, diskInstanceName);}, m_maxTriesToConnect);
+}
+
+void DiskSystemCatalogueRetryWrapper::modifyDiskSystemDiskInstanceSpaceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+  const std::string &diskInstanceSpaceName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->modifyDiskSystemDiskInstanceSpaceName(admin,
+    name, diskInstanceSpaceName);}, m_maxTriesToConnect);
+}
+
+bool DiskSystemCatalogueRetryWrapper::diskSystemExists(const std::string &name) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DiskSystem()->diskSystemExists(name);},
+    m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..3dbe05074b
--- /dev/null
+++ b/catalogue/retrywrappers/DiskSystemCatalogueRetryWrapper.hpp
@@ -0,0 +1,75 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/DiskSystemCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class DiskSystemCatalogueRetryWrapper : public DiskSystemCatalogue {
+public:
+  DiskSystemCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~DiskSystemCatalogueRetryWrapper() override = default;
+
+  void createDiskSystem(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &diskInstanceName, const std::string &diskInstanceSpaceName, const std::string &fileRegexp,
+    const uint64_t targetedFreeSpace, const time_t sleepTime, const std::string &comment) override;
+
+  void deleteDiskSystem(const std::string &name) override;
+
+  disk::DiskSystemList getAllDiskSystems() const override;
+
+  void modifyDiskSystemFileRegexp(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &fileRegexp) override;
+
+  void modifyDiskSystemTargetedFreeSpace(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t targetedFreeSpace) override;
+
+  void modifyDiskSystemComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyDiskSystemSleepTime(const common::dataStructures::SecurityIdentity& admin,
+    const std::string& name, const uint64_t sleepTime) override;
+
+  void modifyDiskSystemDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceName) override;
+
+  void modifyDiskSystemDiskInstanceSpaceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &diskInstanceSpaceName) override;
+
+  bool diskSystemExists(const std::string &name) const override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class DiskSystemCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..3676d29a87
--- /dev/null
+++ b/catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.cpp
@@ -0,0 +1,71 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+DriveConfigCatalogueRetryWrapper::DriveConfigCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void DriveConfigCatalogueRetryWrapper::createTapeDriveConfig(const std::string &driveName, const std::string &category,
+  const std::string &keyName, const std::string &value, const std::string &source) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveConfig()->createTapeDriveConfig(driveName, category,
+    keyName, value, source);},
+    m_maxTriesToConnect);
+}
+
+std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> DriveConfigCatalogueRetryWrapper::getTapeDriveConfigs() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveConfig()->getTapeDriveConfigs();},
+    m_maxTriesToConnect);
+}
+
+std::list<std::pair<std::string, std::string>> DriveConfigCatalogueRetryWrapper::getTapeDriveConfigNamesAndKeys() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveConfig()->getTapeDriveConfigNamesAndKeys();},
+    m_maxTriesToConnect);
+}
+
+void DriveConfigCatalogueRetryWrapper::modifyTapeDriveConfig(const std::string &driveName, const std::string &category,
+  const std::string &keyName, const std::string &value, const std::string &source) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveConfig()->modifyTapeDriveConfig(driveName, category,
+    keyName, value, source);},
+    m_maxTriesToConnect);
+}
+
+std::optional<std::tuple<std::string, std::string, std::string>> DriveConfigCatalogueRetryWrapper::getTapeDriveConfig(
+  const std::string &tapeDriveName,
+  const std::string &keyName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, keyName);},
+    m_maxTriesToConnect);
+}
+
+void DriveConfigCatalogueRetryWrapper::deleteTapeDriveConfig(const std::string &tapeDriveName,
+  const std::string &keyName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName,
+    keyName);},
+    m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..185fdc1007
--- /dev/null
+++ b/catalogue/retrywrappers/DriveConfigCatalogueRetryWrapper.hpp
@@ -0,0 +1,62 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/interfaces/DriveConfigCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DriveConfigCatalogueRetryWrapper: public DriveConfigCatalogue {
+public:
+  DriveConfigCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+
+  ~DriveConfigCatalogueRetryWrapper() override = default;
+
+  void createTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::list<DriveConfig> getTapeDriveConfigs() const override;
+
+  std::list<std::pair<std::string, std::string>> getTapeDriveConfigNamesAndKeys() const override;
+
+  void modifyTapeDriveConfig(const std::string &tapeDriveName, const std::string &category,
+    const std::string &keyName, const std::string &value, const std::string &source) override;
+
+  std::optional<std::tuple<std::string, std::string, std::string>> getTapeDriveConfig(const std::string &tapeDriveName,
+    const std::string &keyName) const override;
+
+  void deleteTapeDriveConfig(const std::string &tapeDriveName, const std::string &keyName) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..127fa03b01
--- /dev/null
+++ b/catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.cpp
@@ -0,0 +1,104 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+DriveStateCatalogueRetryWrapper::DriveStateCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void DriveStateCatalogueRetryWrapper::createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->createTapeDrive(tapeDrive);},
+    m_maxTriesToConnect);
+}
+
+std::list<std::string> DriveStateCatalogueRetryWrapper::getTapeDriveNames() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->getTapeDriveNames();},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::TapeDrive> DriveStateCatalogueRetryWrapper::getTapeDrives() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->getTapeDrives();}, m_maxTriesToConnect);
+}
+
+std::optional<common::dataStructures::TapeDrive> DriveStateCatalogueRetryWrapper::getTapeDrive(
+  const std::string &tapeDriveName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->getTapeDrive(tapeDriveName);},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::setDesiredTapeDriveState(const std::string& tapeDriveName,
+  const common::dataStructures::DesiredDriveState &desiredState) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->setDesiredTapeDriveState(tapeDriveName,
+    desiredState);},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+  const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->setDesiredTapeDriveStateComment(
+    tapeDriveName, comment);},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::updateTapeDriveStatistics(const std::string& tapeDriveName,
+  const std::string& host, const std::string& logicalLibrary,
+  const common::dataStructures::TapeDriveStatistics& statistics) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->updateTapeDriveStatistics(tapeDriveName,
+    host, logicalLibrary, statistics);},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->updateTapeDriveStatus(tapeDrive);},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::deleteTapeDrive(const std::string &tapeDriveName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->deleteTapeDrive(tapeDriveName);},
+    m_maxTriesToConnect);
+}
+
+std::map<std::string, uint64_t> DriveStateCatalogueRetryWrapper::getDiskSpaceReservations() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->getDiskSpaceReservations();},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->reserveDiskSpace(driveName, mountId,
+    diskSpaceReservation, lc);},
+    m_maxTriesToConnect);
+}
+
+void DriveStateCatalogueRetryWrapper::releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+  const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->DriveState()->releaseDiskSpace(driveName, mountId,
+    diskSpaceReservation, lc);},
+    m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..5276de0084
--- /dev/null
+++ b/catalogue/retrywrappers/DriveStateCatalogueRetryWrapper.hpp
@@ -0,0 +1,78 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+#include "catalogue/interfaces/DriveStateCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class DriveStateCatalogueRetryWrapper: public DriveStateCatalogue {
+public:
+  DriveStateCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+
+  ~DriveStateCatalogueRetryWrapper() override = default;
+
+  void createTapeDrive(const common::dataStructures::TapeDrive &tapeDrive) override;
+
+  std::list<std::string> getTapeDriveNames() const override;
+
+  std::list<common::dataStructures::TapeDrive> getTapeDrives() const override;
+
+  std::optional<common::dataStructures::TapeDrive> getTapeDrive(const std::string &tapeDriveName) const override;
+
+  void setDesiredTapeDriveState(const std::string& tapeDriveName,
+      const common::dataStructures::DesiredDriveState &desiredState) override;
+
+  void setDesiredTapeDriveStateComment(const std::string& tapeDriveName,
+    const std::string &comment) override;
+
+  void updateTapeDriveStatistics(const std::string& tapeDriveName,
+    const std::string& host, const std::string& logicalLibrary,
+    const common::dataStructures::TapeDriveStatistics& statistics) override;
+
+  void updateTapeDriveStatus(const common::dataStructures::TapeDrive &tapeDrive) override;
+
+  void deleteTapeDrive(const std::string &tapeDriveName) override;
+
+  std::map<std::string, uint64_t> getDiskSpaceReservations() const override;
+
+  void reserveDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
+
+  void releaseDiskSpace(const std::string& driveName, const uint64_t mountId,
+    const DiskSpaceReservationRequest& diskSpaceReservation, log::LogContext & lc) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..cf296cb4fd
--- /dev/null
+++ b/catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.cpp
@@ -0,0 +1,54 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace cta {
+namespace catalogue {
+
+FileRecycleLogCatalogueRetryWrapper::FileRecycleLogCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect):
+  m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+FileRecycleLogItor FileRecycleLogCatalogueRetryWrapper::getFileRecycleLogItor(
+  const RecycleTapeFileSearchCriteria & searchCriteria) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->FileRecycleLog()->getFileRecycleLogItor(searchCriteria);},
+    m_maxTriesToConnect);
+}
+
+void FileRecycleLogCatalogueRetryWrapper::restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+  const std::string &newFid) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->FileRecycleLog()->restoreFileInRecycleLog(searchCriteria,
+    newFid);},
+    m_maxTriesToConnect);
+}
+
+void FileRecycleLogCatalogueRetryWrapper::deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->FileRecycleLog()->deleteFilesFromRecycleLog(vid, lc);},
+    m_maxTriesToConnect);
+}
+
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..f6d21f7fe0
--- /dev/null
+++ b/catalogue/retrywrappers/FileRecycleLogCatalogueRetryWrapper.hpp
@@ -0,0 +1,58 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/RecyleTapeFileSearchCriteria.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class FileRecycleLogCatalogueRetryWrapper: public FileRecycleLogCatalogue {
+public:
+  FileRecycleLogCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+
+  ~FileRecycleLogCatalogueRetryWrapper() override = default;
+
+  FileRecycleLogItor getFileRecycleLogItor(
+    const RecycleTapeFileSearchCriteria & searchCriteria = RecycleTapeFileSearchCriteria()) const override;
+
+  void restoreFileInRecycleLog(const RecycleTapeFileSearchCriteria & searchCriteria,
+    const std::string &newFid) override;
+
+  void deleteFilesFromRecycleLog(const std::string& vid, log::LogContext& lc) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};
+
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..6ea7b4833d
--- /dev/null
+++ b/catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.cpp
@@ -0,0 +1,75 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/LogicalLibrary.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+
+namespace cta {
+namespace catalogue {
+
+LogicalLibraryCatalogueRetryWrapper::LogicalLibraryCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void LogicalLibraryCatalogueRetryWrapper::createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool isDisabled, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->createLogicalLibrary(admin, name,
+    isDisabled, comment);}, m_maxTriesToConnect);
+}
+
+void LogicalLibraryCatalogueRetryWrapper::deleteLogicalLibrary(const std::string &name) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->deleteLogicalLibrary(name);},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::LogicalLibrary> LogicalLibraryCatalogueRetryWrapper::getLogicalLibraries() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->getLogicalLibraries();},
+    m_maxTriesToConnect);
+}
+
+void LogicalLibraryCatalogueRetryWrapper::modifyLogicalLibraryName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->modifyLogicalLibraryName(admin,
+    currentName, newName);}, m_maxTriesToConnect);
+}
+
+void LogicalLibraryCatalogueRetryWrapper::modifyLogicalLibraryComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->modifyLogicalLibraryComment(admin,
+    name, comment);}, m_maxTriesToConnect);
+}
+
+void LogicalLibraryCatalogueRetryWrapper::modifyLogicalLibraryDisabledReason(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &disabledReason) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->modifyLogicalLibraryDisabledReason(
+    admin, name, disabledReason);}, m_maxTriesToConnect);
+}
+
+void LogicalLibraryCatalogueRetryWrapper::setLogicalLibraryDisabled(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool disabledValue) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->LogicalLibrary()->setLogicalLibraryDisabled(admin,
+    name, disabledValue);}, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..0f5f210a04
--- /dev/null
+++ b/catalogue/retrywrappers/LogicalLibraryCatalogueRetryWrapper.hpp
@@ -0,0 +1,75 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/LogicalLibraryCatalogue.hpp"
+
+namespace cta {
+
+namespace common {
+namespace dataStructures {
+struct LogicalLibrary;
+struct SecurityIdentity;
+}
+}
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class LogicalLibraryCatalogueRetryWrapper: public LogicalLibraryCatalogue {
+public:
+  LogicalLibraryCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~LogicalLibraryCatalogueRetryWrapper() override = default;
+
+  void createLogicalLibrary(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool isDisabled, const std::string &comment) override;
+
+  void deleteLogicalLibrary(const std::string &name) override;
+
+  std::list<common::dataStructures::LogicalLibrary> getLogicalLibraries() const override;
+
+  void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) override;
+
+  void modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyLogicalLibraryDisabledReason(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &disabledReason) override;
+
+  void setLogicalLibraryDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool disabledValue) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class LogicalLibraryCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..e579454f09
--- /dev/null
+++ b/catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.cpp
@@ -0,0 +1,111 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+MediaTypeCatalogueRetryWrapper::MediaTypeCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void MediaTypeCatalogueRetryWrapper::createMediaType(const common::dataStructures::SecurityIdentity &admin,
+  const MediaType &mediaType) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->createMediaType(admin, mediaType);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::deleteMediaType(const std::string &name) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->deleteMediaType(name);},
+    m_maxTriesToConnect);
+}
+
+std::list<MediaTypeWithLogs> MediaTypeCatalogueRetryWrapper::getMediaTypes() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->getMediaTypes();},
+    m_maxTriesToConnect);
+}
+
+MediaType MediaTypeCatalogueRetryWrapper::getMediaTypeByVid(const std::string & vid) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->getMediaTypeByVid(vid);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeName(admin, currentName, newName);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &cartridge) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeCartridge(admin, name, cartridge);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeCapacityInBytes(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t capacityInBytes) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeCapacityInBytes(admin, name, capacityInBytes);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypePrimaryDensityCode(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t primaryDensityCode) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypePrimaryDensityCode(admin, name, primaryDensityCode);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeSecondaryDensityCode(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint8_t secondaryDensityCode) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeSecondaryDensityCode(admin, name, secondaryDensityCode);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint32_t> &nbWraps) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeNbWraps(admin, name, nbWraps);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint64_t> &minLPos) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeMinLPos(admin, name, minLPos);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::optional<std::uint64_t> &maxLPos) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeMaxLPos(admin, name, maxLPos);},
+    m_maxTriesToConnect);
+}
+
+void MediaTypeCatalogueRetryWrapper::modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MediaType()->modifyMediaTypeComment(admin, name, comment);},
+    m_maxTriesToConnect);
+}
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..55899c00ff
--- /dev/null
+++ b/catalogue/retrywrappers/MediaTypeCatalogueRetryWrapper.hpp
@@ -0,0 +1,82 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/MediaTypeCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class MediaTypeCatalogueRetryWrapper : public MediaTypeCatalogue {
+public:
+  MediaTypeCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~MediaTypeCatalogueRetryWrapper() override = default;
+
+  void createMediaType(const common::dataStructures::SecurityIdentity &admin, const MediaType &mediaType) override;
+
+  void deleteMediaType(const std::string &name) override;
+
+  std::list<MediaTypeWithLogs> getMediaTypes() const override;
+
+  MediaType getMediaTypeByVid(const std::string & vid) const override;
+
+  void modifyMediaTypeName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) override;
+
+  void modifyMediaTypeCartridge(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &cartridge) override;
+
+  void modifyMediaTypeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const uint64_t capacityInBytes) override;
+
+  void modifyMediaTypePrimaryDensityCode(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+   const uint8_t primaryDensityCode) override;
+
+  void modifyMediaTypeSecondaryDensityCode(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint8_t secondaryDensityCode) override;
+
+  void modifyMediaTypeNbWraps(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint32_t> &nbWraps) override;
+
+  void modifyMediaTypeMinLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &minLPos) override;
+
+  void modifyMediaTypeMaxLPos(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::optional<std::uint64_t> &maxLPos) override;
+
+  void modifyMediaTypeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class MediaTypeCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..dd912b2c8a
--- /dev/null
+++ b/catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.cpp
@@ -0,0 +1,96 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
+#include "common/dataStructures/ArchiveFileSummary.hpp"
+
+namespace cta {
+namespace catalogue {
+
+MountPolicyCatalogueRetryWrapper::MountPolicyCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void MountPolicyCatalogueRetryWrapper::createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+  const CreateMountPolicyAttributes & mountPolicy) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->createMountPolicy(admin, mountPolicy);},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::MountPolicy> MountPolicyCatalogueRetryWrapper::getMountPolicies() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->getMountPolicies();},
+    m_maxTriesToConnect);
+}
+
+std::optional<common::dataStructures::MountPolicy> MountPolicyCatalogueRetryWrapper::getMountPolicy(
+  const std::string &mountPolicyName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->getMountPolicy(mountPolicyName);},
+    m_maxTriesToConnect);
+}
+
+
+std::list<common::dataStructures::MountPolicy> MountPolicyCatalogueRetryWrapper::getCachedMountPolicies() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->getCachedMountPolicies();},
+    m_maxTriesToConnect);
+}
+
+void MountPolicyCatalogueRetryWrapper::deleteMountPolicy(const std::string &name) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->deleteMountPolicy(name);},
+    m_maxTriesToConnect);
+}
+
+void MountPolicyCatalogueRetryWrapper::modifyMountPolicyArchivePriority(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t archivePriority) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->modifyMountPolicyArchivePriority(admin,
+    name, archivePriority);}, m_maxTriesToConnect);
+}
+
+void MountPolicyCatalogueRetryWrapper::modifyMountPolicyArchiveMinRequestAge(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t minArchiveRequestAge) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->modifyMountPolicyArchiveMinRequestAge(
+    admin, name, minArchiveRequestAge);}, m_maxTriesToConnect);
+}
+
+void MountPolicyCatalogueRetryWrapper::modifyMountPolicyRetrievePriority(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t retrievePriority) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->modifyMountPolicyRetrievePriority(admin,
+    name, retrievePriority);}, m_maxTriesToConnect);
+}
+void MountPolicyCatalogueRetryWrapper::modifyMountPolicyRetrieveMinRequestAge(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+  const uint64_t minRetrieveRequestAge) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->modifyMountPolicyRetrieveMinRequestAge(
+    admin, name, minRetrieveRequestAge);}, m_maxTriesToConnect);
+}
+
+void MountPolicyCatalogueRetryWrapper::modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->MountPolicy()->modifyMountPolicyComment(admin, name,
+    comment);}, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..7ea8e6f36c
--- /dev/null
+++ b/catalogue/retrywrappers/MountPolicyCatalogueRetryWrapper.hpp
@@ -0,0 +1,74 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/MountPolicyCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class MountPolicyCatalogueRetryWrapper : public MountPolicyCatalogue {
+public:
+  MountPolicyCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~MountPolicyCatalogueRetryWrapper() override = default;
+
+  void createMountPolicy(const common::dataStructures::SecurityIdentity &admin,
+    const CreateMountPolicyAttributes & mountPolicy) override;
+
+  std::list<common::dataStructures::MountPolicy> getMountPolicies() const override;
+
+  std::optional<common::dataStructures::MountPolicy> getMountPolicy(
+    const std::string &mountPolicyName) const override;
+
+  std::list<common::dataStructures::MountPolicy> getCachedMountPolicies() const override;
+
+  void deleteMountPolicy(const std::string &name) override;
+
+  void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t archivePriority) override;
+
+  void modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minArchiveRequestAge) override;
+
+  void modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t retrievePriority) override;
+
+  void modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t minRetrieveRequestAge) override;
+
+  void modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..2b7c14df40
--- /dev/null
+++ b/catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.cpp
@@ -0,0 +1,79 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RequesterActivityMountRuleCatalogueRetryWrapper::RequesterActivityMountRuleCatalogueRetryWrapper(
+  const std::unique_ptr<Catalogue>& catalogue, log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void RequesterActivityMountRuleCatalogueRetryWrapper::modifyRequesterActivityMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &activityRegex, const std::string &mountPolicy) {
+  return retryOnLostConnection(m_log, [&](){
+    m_catalogue->RequesterActivityMountRule()->modifyRequesterActivityMountRulePolicy(admin, instanceName, requesterName,
+    activityRegex, mountPolicy);
+  }, m_maxTriesToConnect);
+}
+
+void RequesterActivityMountRuleCatalogueRetryWrapper::modifyRequesterActivityMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &activityRegex, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&](){
+    m_catalogue->RequesterActivityMountRule()->modifyRequesterActivityMountRuleComment(admin, instanceName, requesterName,
+    activityRegex, comment);
+  }, m_maxTriesToConnect);
+}
+
+void RequesterActivityMountRuleCatalogueRetryWrapper::createRequesterActivityMountRule(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &mountPolicyName,
+  const std::string &diskInstance, const std::string &requesterName, const std::string &activityRegex,
+  const std::string &comment) {
+  return retryOnLostConnection(m_log, [&](){
+    m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(admin, mountPolicyName, diskInstance,
+    requesterName, activityRegex, comment);
+  }, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::RequesterActivityMountRule>
+  RequesterActivityMountRuleCatalogueRetryWrapper::getRequesterActivityMountRules() const {
+  return retryOnLostConnection(m_log, [&](){
+    return m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+  }, m_maxTriesToConnect);
+}
+
+void RequesterActivityMountRuleCatalogueRetryWrapper::deleteRequesterActivityMountRule(
+  const std::string &diskInstanceName, const std::string &requesterName, const std::string &activityRegex) {
+  return retryOnLostConnection(m_log, [&](){
+    m_catalogue->RequesterActivityMountRule()->deleteRequesterActivityMountRule(diskInstanceName, requesterName,
+    activityRegex);
+  }, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..51eac5b454
--- /dev/null
+++ b/catalogue/retrywrappers/RequesterActivityMountRuleCatalogueRetryWrapper.hpp
@@ -0,0 +1,64 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/RequesterActivityMountRuleCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class RequesterActivityMountRuleCatalogueRetryWrapper : public RequesterActivityMountRuleCatalogue {
+public:
+  RequesterActivityMountRuleCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~RequesterActivityMountRuleCatalogueRetryWrapper() override = default;
+
+  void modifyRequesterActivityMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &mountPolicy) override;
+
+  void modifyRequesterActivityMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &activityRegex,
+    const std::string &comment) override;
+
+  void createRequesterActivityMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+    const std::string &activityRegex, const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterActivityMountRule> getRequesterActivityMountRules() const override;
+
+  void deleteRequesterActivityMountRule(const std::string &diskInstanceName, const std::string &requesterName,
+    const std::string &activityRegex) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..c190a09560
--- /dev/null
+++ b/catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.cpp
@@ -0,0 +1,74 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/RequesterGroupMountRule.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RequesterGroupMountRuleCatalogueRetryWrapper::RequesterGroupMountRuleCatalogueRetryWrapper(
+  const std::unique_ptr<Catalogue>& catalogue, log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void RequesterGroupMountRuleCatalogueRetryWrapper::modifyRequesterGroupMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterGroupName, const std::string &mountPolicy) {
+  return retryOnLostConnection(m_log, [&] {
+    return m_catalogue->RequesterGroupMountRule()->modifyRequesterGroupMountRulePolicy(admin, instanceName,
+    requesterGroupName, mountPolicy);}, m_maxTriesToConnect);
+}
+
+void RequesterGroupMountRuleCatalogueRetryWrapper::modifyRequesterGroupMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterGroupName, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&] {
+    return m_catalogue->RequesterGroupMountRule()->modifyRequesterGroupMountRuleComment(admin, instanceName,
+    requesterGroupName, comment);}, m_maxTriesToConnect);
+}
+
+void RequesterGroupMountRuleCatalogueRetryWrapper::createRequesterGroupMountRule(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &mountPolicyName,
+  const std::string &diskInstanceName, const std::string &requesterGroupName, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&] {
+    return m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(admin, mountPolicyName,
+    diskInstanceName, requesterGroupName, comment);}, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::RequesterGroupMountRule>
+  RequesterGroupMountRuleCatalogueRetryWrapper::getRequesterGroupMountRules() const {
+  return retryOnLostConnection(m_log, [&] {
+    return m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();}, m_maxTriesToConnect);
+}
+
+
+void RequesterGroupMountRuleCatalogueRetryWrapper::deleteRequesterGroupMountRule(const std::string &diskInstanceName,
+  const std::string &requesterGroupName) {
+  return retryOnLostConnection(m_log, [&] {
+    return m_catalogue->RequesterGroupMountRule()->deleteRequesterGroupMountRule(diskInstanceName,
+    requesterGroupName);}, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..0d8c340095
--- /dev/null
+++ b/catalogue/retrywrappers/RequesterGroupMountRuleCatalogueRetryWrapper.hpp
@@ -0,0 +1,63 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/RequesterGroupMountRuleCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class RequesterGroupMountRuleCatalogueRetryWrapper : public RequesterGroupMountRuleCatalogue {
+public:
+  RequesterGroupMountRuleCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~RequesterGroupMountRuleCatalogueRetryWrapper() override = default;
+
+  void modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) override;
+
+  void modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) override;
+
+  void createRequesterGroupMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstanceName, const std::string &requesterGroupName,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterGroupMountRule> getRequesterGroupMountRules() const override;
+
+
+  void deleteRequesterGroupMountRule(const std::string &diskInstanceName,
+    const std::string &requesterGroupName) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..77f24e51a2
--- /dev/null
+++ b/catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.cpp
@@ -0,0 +1,73 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+
+namespace cta {
+namespace catalogue {
+
+RequesterMountRuleCatalogueRetryWrapper::RequesterMountRuleCatalogueRetryWrapper(
+  const std::unique_ptr<Catalogue>& catalogue, log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {}
+
+void RequesterMountRuleCatalogueRetryWrapper::modifyRequesterMountRulePolicy(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &mountPolicy) {
+  return retryOnLostConnection(m_log, [&]() {
+    m_catalogue->RequesterMountRule()->modifyRequesterMountRulePolicy(admin, instanceName, requesterName, mountPolicy);
+  }, m_maxTriesToConnect);
+}
+
+void RequesterMountRuleCatalogueRetryWrapper::modifyRequesteMountRuleComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &instanceName,
+  const std::string &requesterName, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]() {
+    m_catalogue->RequesterMountRule()->modifyRequesteMountRuleComment(admin, instanceName, requesterName, comment);
+  }, m_maxTriesToConnect);
+}
+
+void RequesterMountRuleCatalogueRetryWrapper::createRequesterMountRule(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &mountPolicyName,
+  const std::string &diskInstance, const std::string &requesterName, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]() {
+    m_catalogue->RequesterMountRule()->createRequesterMountRule(admin, mountPolicyName, diskInstance, requesterName, comment);
+  }, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::RequesterMountRule> RequesterMountRuleCatalogueRetryWrapper::getRequesterMountRules() const {
+  return retryOnLostConnection(m_log, [&]() {
+    return m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  }, m_maxTriesToConnect);
+}
+
+void RequesterMountRuleCatalogueRetryWrapper::deleteRequesterMountRule(const std::string &diskInstanceName,
+  const std::string &requesterName) {
+  return retryOnLostConnection(m_log, [&]() {
+    m_catalogue->RequesterMountRule()->deleteRequesterMountRule(diskInstanceName, requesterName);
+  }, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..1084f195af
--- /dev/null
+++ b/catalogue/retrywrappers/RequesterMountRuleCatalogueRetryWrapper.hpp
@@ -0,0 +1,61 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/RequesterMountRuleCatalogue.hpp"
+
+namespace cta {
+
+namespace log {
+class Logger;
+}
+
+namespace catalogue {
+
+class Catalogue;
+
+class RequesterMountRuleCatalogueRetryWrapper : public RequesterMountRuleCatalogue {
+public:
+  RequesterMountRuleCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~RequesterMountRuleCatalogueRetryWrapper() override = default;
+
+  void modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) override;
+
+  void modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &instanceName, const std::string &requesterName, const std::string &comment) override;
+
+  void createRequesterMountRule(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &mountPolicyName, const std::string &diskInstance, const std::string &requesterName,
+    const std::string &comment) override;
+
+  std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const override;
+
+  void deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/SchemaCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/SchemaCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..1d04b4d988
--- /dev/null
+++ b/catalogue/retrywrappers/SchemaCatalogueRetryWrapper.cpp
@@ -0,0 +1,46 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/SchemaCatalogueRetryWrapper.hpp"
+#include "catalogue/SchemaVersion.hpp"
+
+namespace cta {
+namespace catalogue {
+
+SchemaCatalogueRetryWrapper::SchemaCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+SchemaVersion SchemaCatalogueRetryWrapper::getSchemaVersion() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Schema()->getSchemaVersion();}, m_maxTriesToConnect);
+}
+
+void SchemaCatalogueRetryWrapper::verifySchemaVersion() {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Schema()->verifySchemaVersion();}, m_maxTriesToConnect);
+}
+
+void SchemaCatalogueRetryWrapper::ping() {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Schema()->ping();}, m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/SchemaCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/SchemaCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..94e87407c7
--- /dev/null
+++ b/catalogue/retrywrappers/SchemaCatalogueRetryWrapper.hpp
@@ -0,0 +1,49 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/SchemaCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class Catalogue;
+class SchemaVersion;
+
+class SchemaCatalogueRetryWrapper: public SchemaCatalogue {
+public:
+  SchemaCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~SchemaCatalogueRetryWrapper() override = default;
+
+  SchemaVersion getSchemaVersion() const override;
+
+  void verifySchemaVersion() override;
+
+  void ping() override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..fe2d92bbc5
--- /dev/null
+++ b/catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.cpp
@@ -0,0 +1,84 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+
+namespace cta {
+namespace catalogue {
+
+StorageClassCatalogueRetryWrapper::StorageClassCatalogueRetryWrapper(
+  const std::unique_ptr<Catalogue>& catalogue, log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void StorageClassCatalogueRetryWrapper::createStorageClass(
+  const common::dataStructures::SecurityIdentity &admin,
+  const common::dataStructures::StorageClass &storageClass) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->createStorageClass(admin, storageClass);},
+    m_maxTriesToConnect);
+}
+
+void StorageClassCatalogueRetryWrapper::deleteStorageClass(const std::string &storageClassName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->deleteStorageClass(storageClassName);},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::StorageClass> StorageClassCatalogueRetryWrapper::getStorageClasses() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->getStorageClasses();},
+    m_maxTriesToConnect);
+}
+
+common::dataStructures::StorageClass StorageClassCatalogueRetryWrapper::getStorageClass(const std::string &name) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->getStorageClass(name);},
+    m_maxTriesToConnect);
+}
+
+void StorageClassCatalogueRetryWrapper::modifyStorageClassNbCopies(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbCopies) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->modifyStorageClassNbCopies(admin, name,
+    nbCopies);},
+    m_maxTriesToConnect);
+}
+
+void StorageClassCatalogueRetryWrapper::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->modifyStorageClassComment(admin, name,
+    comment);},
+    m_maxTriesToConnect);
+}
+
+void StorageClassCatalogueRetryWrapper::modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->modifyStorageClassVo(admin, name, vo);},
+    m_maxTriesToConnect);
+}
+
+void StorageClassCatalogueRetryWrapper::modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->StorageClass()->modifyStorageClassName(admin, currentName,
+    newName);},
+    m_maxTriesToConnect);
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..0462e0905b
--- /dev/null
+++ b/catalogue/retrywrappers/StorageClassCatalogueRetryWrapper.hpp
@@ -0,0 +1,67 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/interfaces/StorageClassCatalogue.hpp"
+#include "common/log/Logger.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class Catalogue;
+
+class StorageClassCatalogueRetryWrapper: public StorageClassCatalogue {
+public:
+  StorageClassCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~StorageClassCatalogueRetryWrapper() override = default;
+
+  void createStorageClass(
+    const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::StorageClass &storageClass) override;
+
+  void deleteStorageClass(const std::string &storageClassName) override;
+
+  std::list<common::dataStructures::StorageClass> getStorageClasses() const override;
+
+  common::dataStructures::StorageClass getStorageClass(const std::string &name) const override;
+
+  void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const uint64_t nbCopies) override;
+
+  void modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &name, const std::string &comment) override;
+
+  void modifyStorageClassVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) override;
+
+  void modifyStorageClassName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &currentName, const std::string &newName) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class StorageClassCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/TapeCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/TapeCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..ae5c7db332
--- /dev/null
+++ b/catalogue/retrywrappers/TapeCatalogueRetryWrapper.cpp
@@ -0,0 +1,191 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/TapeCatalogueRetryWrapper.hpp"
+#include "catalogue/TapeForWriting.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+
+namespace cta {
+namespace catalogue {
+
+TapeCatalogueRetryWrapper::TapeCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &log,
+  const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void TapeCatalogueRetryWrapper::createTape(const common::dataStructures::SecurityIdentity &admin,
+  const CreateTapeAttributes & tape) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->createTape(admin, tape);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::deleteTape(const std::string &vid) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->deleteTape(vid);}, m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::Tape> TapeCatalogueRetryWrapper::getTapes(
+  const TapeSearchCriteria &searchCriteria) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getTapes(searchCriteria);}, m_maxTriesToConnect);
+}
+
+common::dataStructures::VidToTapeMap TapeCatalogueRetryWrapper::getTapesByVid(const std::string& vid) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getTapesByVid(vid);}, m_maxTriesToConnect);
+}
+
+common::dataStructures::VidToTapeMap TapeCatalogueRetryWrapper::getTapesByVid(const std::set<std::string> &vids) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getTapesByVid(vids);}, m_maxTriesToConnect);
+}
+
+std::map<std::string, std::string> TapeCatalogueRetryWrapper::getVidToLogicalLibrary(
+  const std::set<std::string> &vids) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getVidToLogicalLibrary(vids);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::reclaimTape(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, cta::log::LogContext & lc) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->reclaimTape(admin, vid, lc);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::checkTapeForLabel(const std::string &vid) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->checkTapeForLabel(vid);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::tapeLabelled(const std::string &vid, const std::string &drive) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->tapeLabelled(vid, drive);}, m_maxTriesToConnect);
+}
+
+uint64_t TapeCatalogueRetryWrapper::getNbFilesOnTape(const std::string &vid) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getNbFilesOnTape(vid);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &mediaType) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeMediaType(admin, vid, mediaType);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &vendor) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeVendor(admin, vid, vendor);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &logicalLibraryName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeLogicalLibraryName(admin, vid,
+    logicalLibraryName);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &tapePoolName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeTapePoolName(admin, vid,
+    tapePoolName);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &encryptionKeyName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeEncryptionKeyName(admin, vid,
+    encryptionKeyName);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &verificationStatus) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeVerificationStatus(admin, vid,
+    verificationStatus);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeState(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const common::dataStructures::Tape::State & state,
+  const std::optional<common::dataStructures::Tape::State> & prev_state,
+  const std::optional<std::string> & stateReason) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeState(admin,vid, state, prev_state,
+    stateReason);}, m_maxTriesToConnect);
+}
+
+bool TapeCatalogueRetryWrapper::tapeExists(const std::string &vid) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->tapeExists(vid);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::setTapeFull(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const bool fullValue) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->setTapeFull(admin, vid, fullValue);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string & reason) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->setTapeRepackingDisabled(admin, vid, reason);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+  const bool dirtyValue) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->setTapeDirty(admin, vid, dirtyValue);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::setTapeIsFromCastorInUnitTests(const std::string &vid) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->setTapeIsFromCastorInUnitTests(vid);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::setTapeDisabled(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string & reason) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->setTapeDisabled(admin, vid, reason);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::setTapeDirty(const std::string & vid) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->setTapeDirty(vid);}, m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::modifyTapeComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::optional<std::string> &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->modifyTapeComment(admin, vid, comment);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::tapeMountedForArchive(const std::string &vid, const std::string &drive) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->tapeMountedForArchive(vid, drive);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::tapeMountedForRetrieve(const std::string &vid, const std::string &drive) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->tapeMountedForRetrieve(vid, drive);},
+    m_maxTriesToConnect);
+}
+
+void TapeCatalogueRetryWrapper::noSpaceLeftOnTape(const std::string &vid) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->noSpaceLeftOnTape(vid);}, m_maxTriesToConnect);
+}
+
+std::list<TapeForWriting> TapeCatalogueRetryWrapper::getTapesForWriting(const std::string &logicalLibraryName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getTapesForWriting(logicalLibraryName);},
+    m_maxTriesToConnect);
+}
+
+common::dataStructures::Label::Format TapeCatalogueRetryWrapper::getTapeLabelFormat(const std::string& vid) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->Tape()->getTapeLabelFormat(vid);}, m_maxTriesToConnect);
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/TapeCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/TapeCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..0c45d2a7da
--- /dev/null
+++ b/catalogue/retrywrappers/TapeCatalogueRetryWrapper.hpp
@@ -0,0 +1,118 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/TapeCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class Catalogue;
+class SchemaVersion;
+
+class TapeCatalogueRetryWrapper: public TapeCatalogue {
+public:
+  TapeCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~TapeCatalogueRetryWrapper() override = default;
+
+  void createTape(const common::dataStructures::SecurityIdentity &admin, const CreateTapeAttributes & tape) override;
+
+  void deleteTape(const std::string &vid) override;
+
+  std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria &searchCriteria) const override;
+
+  common::dataStructures::VidToTapeMap getTapesByVid(const std::string& vid) const override;
+
+  common::dataStructures::VidToTapeMap getTapesByVid(const std::set<std::string> &vids) const override;
+
+  std::map<std::string, std::string> getVidToLogicalLibrary(const std::set<std::string> &vids) const override;
+
+  void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    cta::log::LogContext & lc) override;
+
+  void checkTapeForLabel(const std::string &vid) override;
+
+  void tapeLabelled(const std::string &vid, const std::string &drive) override;
+
+  uint64_t getNbFilesOnTape(const std::string &vid) const override;
+
+  void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &mediaType) override;
+
+  void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &vendor) override;
+
+  void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &logicalLibraryName) override;
+
+  void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &tapePoolName) override;
+
+  void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &encryptionKeyName) override;
+
+  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string &verificationStatus) override;
+
+  void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid,
+    const common::dataStructures::Tape::State & state,
+    const std::optional<common::dataStructures::Tape::State> & prev_state,
+    const std::optional<std::string> & stateReason) override;
+
+  bool tapeExists(const std::string &vid) const override;
+
+  void setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool fullValue) override;
+
+  void setTapeDirty(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const bool dirtyValue) override;
+
+  void setTapeIsFromCastorInUnitTests(const std::string &vid) override;
+
+  void setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) override;
+
+  void setTapeRepackingDisabled(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::string & reason) override;
+
+  void setTapeDirty(const std::string & vid) override;
+
+  void modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, const std::string &vid,
+    const std::optional<std::string> &comment) override;
+
+  void tapeMountedForArchive(const std::string &vid, const std::string &drive) override;
+
+  void tapeMountedForRetrieve(const std::string &vid, const std::string &drive) override;
+
+  void noSpaceLeftOnTape(const std::string &vid) override;
+
+  std::list<TapeForWriting> getTapesForWriting(const std::string &logicalLibraryName) const override;
+
+  common::dataStructures::Label::Format getTapeLabelFormat(const std::string& vid) const override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..b06c7b9fca
--- /dev/null
+++ b/catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.cpp
@@ -0,0 +1,55 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
+
+namespace cta {
+namespace catalogue {
+
+TapeFileCatalogueRetryWrapper::TapeFileCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void TapeFileCatalogueRetryWrapper::filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapeFile()->filesWrittenToTape(event);},
+    m_maxTriesToConnect);
+}
+
+void TapeFileCatalogueRetryWrapper::deleteTapeFileCopy(common::dataStructures::ArchiveFile &file,
+  const std::string &reason) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapeFile()->deleteTapeFileCopy(file, reason);},
+    m_maxTriesToConnect);
+}
+
+common::dataStructures::RetrieveFileQueueCriteria TapeFileCatalogueRetryWrapper::prepareToRetrieveFile(
+  const std::string &diskInstanceName, const uint64_t archiveFileId,
+  const common::dataStructures::RequesterIdentity &user, const std::optional<std::string> & activity,
+  log::LogContext &lc, const std::optional<std::string> &mountPolicyName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName,
+    archiveFileId, user, activity, lc, mountPolicyName);}, m_maxTriesToConnect);
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..a1ec374968
--- /dev/null
+++ b/catalogue/retrywrappers/TapeFileCatalogueRetryWrapper.hpp
@@ -0,0 +1,51 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/TapeFileCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class Catalogue;
+
+class TapeFileCatalogueRetryWrapper: public TapeFileCatalogue {
+public:
+  TapeFileCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~TapeFileCatalogueRetryWrapper() override = default;
+
+  void filesWrittenToTape(const std::set<TapeItemWrittenPointer> &event) override;
+
+  void deleteTapeFileCopy(common::dataStructures::ArchiveFile &file, const std::string &reason) override;
+
+  common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(const std::string &diskInstanceName,
+    const uint64_t archiveFileId, const common::dataStructures::RequesterIdentity &user,
+    const std::optional<std::string> & activity, log::LogContext &lc,
+    const std::optional<std::string> &mountPolicyName = std::nullopt) override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/TapePoolCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/TapePoolCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..12d916ab4d
--- /dev/null
+++ b/catalogue/retrywrappers/TapePoolCatalogueRetryWrapper.hpp
@@ -0,0 +1,75 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <list>
+
+#include "catalogue/interfaces/TapePoolCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class Catalogue;
+
+class TapePoolCatalogueRetryWrapper: public TapePoolCatalogue {
+public:
+  TapePoolCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~TapePoolCatalogueRetryWrapper() override = default;
+
+  void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+    const std::optional<std::string> &supply, const std::string &comment) override;
+
+  void deleteTapePool(const std::string &name) override;
+
+  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const override;
+
+  std::optional<TapePool> getTapePool(const std::string &tapePoolName) const override;
+
+  void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &vo) override;
+
+  void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const uint64_t nbPartialTapes) override;
+
+  void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &comment) override;
+
+  void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const bool encryptionValue) override;
+
+  void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name,
+    const std::string &supply) override;
+
+  void modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName,
+    const std::string &newName) override;
+
+  bool tapePoolExists(const std::string &tapePoolName) const override;
+
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/TapePoolRetryCatalogueWrapper.cpp b/catalogue/retrywrappers/TapePoolRetryCatalogueWrapper.cpp
new file mode 100644
index 0000000000..f0f0da6452
--- /dev/null
+++ b/catalogue/retrywrappers/TapePoolRetryCatalogueWrapper.cpp
@@ -0,0 +1,96 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/TapePoolCatalogueRetryWrapper.hpp"
+
+namespace cta {
+namespace catalogue {
+
+TapePoolCatalogueRetryWrapper::TapePoolCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue,
+  log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void TapePoolCatalogueRetryWrapper::createTapePool(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue,
+  const std::optional<std::string> &supply, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->createTapePool(admin, name, vo,
+    nbPartialTapes, encryptionValue, supply, comment);}, m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::deleteTapePool(const std::string &name) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->deleteTapePool(name);}, m_maxTriesToConnect);
+}
+
+std::list<TapePool> TapePoolCatalogueRetryWrapper::getTapePools(const TapePoolSearchCriteria &searchCriteria) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->getTapePools(searchCriteria);},
+    m_maxTriesToConnect);
+}
+
+std::optional<TapePool> TapePoolCatalogueRetryWrapper::getTapePool(const std::string &tapePoolName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->getTapePool(tapePoolName);},
+    m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &vo) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->modifyTapePoolVo(admin, name, vo);},
+    m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const uint64_t nbPartialTapes) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->modifyTapePoolNbPartialTapes(admin, name,
+    nbPartialTapes);}, m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->modifyTapePoolComment(admin, name, comment);},
+    m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const bool encryptionValue) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->setTapePoolEncryption(admin, name,
+    encryptionValue);}, m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &name, const std::string &supply) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->modifyTapePoolSupply(admin, name, supply);},
+    m_maxTriesToConnect);
+}
+
+void TapePoolCatalogueRetryWrapper::modifyTapePoolName(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &currentName, const std::string &newName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->modifyTapePoolName(admin, currentName,
+    newName);}, m_maxTriesToConnect);
+}
+
+bool TapePoolCatalogueRetryWrapper::tapePoolExists(const std::string &tapePoolName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->TapePool()->tapePoolExists(tapePoolName);},
+    m_maxTriesToConnect);
+}
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.cpp b/catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.cpp
new file mode 100644
index 0000000000..05d6ad918f
--- /dev/null
+++ b/catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.cpp
@@ -0,0 +1,98 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/retrywrappers/retryOnLostConnection.hpp"
+#include "catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+
+namespace cta {
+namespace catalogue {
+
+VirtualOrganizationCatalogueRetryWrapper::VirtualOrganizationCatalogueRetryWrapper(
+  const std::unique_ptr<Catalogue>& catalogue, log::Logger &log, const uint32_t maxTriesToConnect)
+  : m_catalogue(catalogue), m_log(log), m_maxTriesToConnect(maxTriesToConnect) {
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::createVirtualOrganization(
+  const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->createVirtualOrganization(admin, vo);},
+    m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::deleteVirtualOrganization(const std::string &voName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->deleteVirtualOrganization(voName);},
+    m_maxTriesToConnect);
+}
+
+std::list<common::dataStructures::VirtualOrganization> VirtualOrganizationCatalogueRetryWrapper::getVirtualOrganizations() const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->getVirtualOrganizations();}, m_maxTriesToConnect);
+}
+
+common::dataStructures::VirtualOrganization VirtualOrganizationCatalogueRetryWrapper::getVirtualOrganizationOfTapepool(
+  const std::string & tapepoolName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->getVirtualOrganizationOfTapepool(tapepoolName);},
+    m_maxTriesToConnect);
+}
+
+common::dataStructures::VirtualOrganization VirtualOrganizationCatalogueRetryWrapper::getCachedVirtualOrganizationOfTapepool(
+  const std::string & tapepoolName) const {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->getCachedVirtualOrganizationOfTapepool(tapepoolName);},
+    m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::modifyVirtualOrganizationName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+  const std::string &newVoName) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->modifyVirtualOrganizationName(admin, currentVoName,
+    newVoName);}, m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::modifyVirtualOrganizationReadMaxDrives(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t readMaxDrives) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->modifyVirtualOrganizationReadMaxDrives(admin, voName,
+    readMaxDrives);}, m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::modifyVirtualOrganizationWriteMaxDrives(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t writeMaxDrives) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->modifyVirtualOrganizationWriteMaxDrives(admin, voName,
+    writeMaxDrives);}, m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::modifyVirtualOrganizationMaxFileSize(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const uint64_t maxFileSize) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->modifyVirtualOrganizationMaxFileSize(admin, voName,
+    maxFileSize);}, m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::modifyVirtualOrganizationComment(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &comment) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->modifyVirtualOrganizationComment(admin, voName,
+    comment);}, m_maxTriesToConnect);
+}
+
+void VirtualOrganizationCatalogueRetryWrapper::modifyVirtualOrganizationDiskInstanceName(
+  const common::dataStructures::SecurityIdentity &admin, const std::string &voName, const std::string &diskInstance) {
+  return retryOnLostConnection(m_log, [&]{return m_catalogue->VO()->modifyVirtualOrganizationDiskInstanceName(admin,
+    voName, diskInstance);}, m_maxTriesToConnect);
+}
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.hpp b/catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.hpp
new file mode 100644
index 0000000000..216247e7a1
--- /dev/null
+++ b/catalogue/retrywrappers/VirtualOrganizationCatalogueRetryWrapper.hpp
@@ -0,0 +1,73 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "catalogue/interfaces/VirtualOrganizationCatalogue.hpp"
+
+namespace cta {
+namespace catalogue {
+
+class Catalogue;
+class SchemaVersion;
+
+class VirtualOrganizationCatalogueRetryWrapper: public VirtualOrganizationCatalogue {
+public:
+  VirtualOrganizationCatalogueRetryWrapper(const std::unique_ptr<Catalogue>& catalogue, log::Logger &m_log,
+    const uint32_t maxTriesToConnect);
+  ~VirtualOrganizationCatalogueRetryWrapper() override = default;
+
+  void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin,
+    const common::dataStructures::VirtualOrganization &vo) override;
+
+  void deleteVirtualOrganization(const std::string &voName) override;
+  std::list<common::dataStructures::VirtualOrganization> getVirtualOrganizations() const override;
+
+  common::dataStructures::VirtualOrganization getVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const override;
+
+  common::dataStructures::VirtualOrganization getCachedVirtualOrganizationOfTapepool(
+    const std::string & tapepoolName) const override;
+
+  void modifyVirtualOrganizationName(
+    const common::dataStructures::SecurityIdentity &admin, const std::string &currentVoName,
+    const std::string &newVoName) override;
+
+  void modifyVirtualOrganizationReadMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t readMaxDrives) override;
+
+  void modifyVirtualOrganizationWriteMaxDrives(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t writeMaxDrives) override;
+
+  void modifyVirtualOrganizationMaxFileSize(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const uint64_t maxFileSize) override;
+
+  void modifyVirtualOrganizationComment(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &comment) override;
+
+  void modifyVirtualOrganizationDiskInstanceName(const common::dataStructures::SecurityIdentity &admin,
+    const std::string &voName, const std::string &diskInstance) override;
+private:
+  const std::unique_ptr<Catalogue>& m_catalogue;
+  log::Logger &m_log;
+  uint32_t m_maxTriesToConnect;
+};  // class SchemaCatalogueRetryWrapper
+
+}  // namespace catalogue
+}  // namespace cta
\ No newline at end of file
diff --git a/catalogue/retryOnLostConnection.hpp b/catalogue/retrywrappers/retryOnLostConnection.hpp
similarity index 94%
rename from catalogue/retryOnLostConnection.hpp
rename to catalogue/retrywrappers/retryOnLostConnection.hpp
index 08214bcbfd..df2cd0ac39 100644
--- a/catalogue/retryOnLostConnection.hpp
+++ b/catalogue/retrywrappers/retryOnLostConnection.hpp
@@ -17,16 +17,14 @@
 
 #pragma once
 
-#include "common/exception/Exception.hpp"
-#include "common/exception/LostDatabaseConnection.hpp"
-//#include "common/checksum/ChecksumTypeMismatch.hpp"
-//#include "common/checksum/ChecksumValueMismatch.hpp"
-#include "common/log/Logger.hpp"
-
 #include <list>
 #include <stdint.h>
 #include <type_traits>
 
+#include "common/exception/Exception.hpp"
+#include "common/exception/LostDatabaseConnection.hpp"
+#include "common/log/Logger.hpp"
+
 namespace cta {
 namespace catalogue {
 
@@ -68,5 +66,5 @@ typename std::result_of<T()>::type retryOnLostConnection(log::Logger &log, const
   }
 }
 
-} // namespace catalogue
-} // namespace cta
+}  // namespace catalogue
+}  // namespace cta
diff --git a/catalogue/tests/CatalogueTestUtils.cpp b/catalogue/tests/CatalogueTestUtils.cpp
new file mode 100644
index 0000000000..ee80b31a45
--- /dev/null
+++ b/catalogue/tests/CatalogueTestUtils.cpp
@@ -0,0 +1,511 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <list>
+#include <iostream>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
+#include "common/dataStructures/ArchiveFileSummary.hpp"
+#include "common/dataStructures/ArchiveRoute.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/DiskInstanceSpace.hpp"
+#include "common/dataStructures/EntryLog.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/dataStructures/LogicalLibrary.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+#include "common/dataStructures/RequesterGroupMountRule.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/Tape.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/DummyLogger.hpp"
+#include "disk/DiskSystem.hpp"
+
+namespace unitTests {
+
+std::unique_ptr<cta::catalogue::Catalogue> CatalogueTestUtils::createCatalogue(
+  cta::catalogue::CatalogueFactory *const *const catalogueFactoryPtrPtr, cta::log::LogContext *lc) {
+  try {
+    if (nullptr == catalogueFactoryPtrPtr) {
+      throw cta::exception::Exception("Global pointer to the catalogue factory pointer for unit-tests in null");
+    }
+    if (nullptr == (*catalogueFactoryPtrPtr)) {
+      throw cta::exception::Exception("Global pointer to the catalogue factoryfor unit-tests in null");
+    }
+
+    auto catalogue = (*catalogueFactoryPtrPtr)->create();
+    CatalogueTestUtils::wipeDatabase(catalogue.get(), lc);
+    return catalogue;
+  } catch(cta::exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void CatalogueTestUtils::wipeDatabase(cta::catalogue::Catalogue *catalogue, cta::log::LogContext *lc) {
+  {
+    const auto adminUsers = catalogue->AdminUser()->getAdminUsers();
+    for(auto &adminUser: adminUsers) {
+      catalogue->AdminUser()->deleteAdminUser(adminUser.name);
+    }
+  }
+  {
+    const auto archiveRoutes = catalogue->ArchiveRoute()->getArchiveRoutes();
+    for(auto &archiveRoute: archiveRoutes) {
+      catalogue->ArchiveRoute()->deleteArchiveRoute(archiveRoute.storageClassName,
+        archiveRoute.copyNb);
+    }
+  }
+  {
+    const auto rules = catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+    for(const auto &rule: rules) {
+      catalogue->RequesterActivityMountRule()->deleteRequesterActivityMountRule(rule.diskInstance, rule.name,
+        rule.activityRegex);
+    }
+  }
+  {
+    const auto rules = catalogue->RequesterMountRule()->getRequesterMountRules();
+    for(const auto &rule: rules) {
+      catalogue->RequesterMountRule()->deleteRequesterMountRule(rule.diskInstance, rule.name);
+    }
+  }
+  {
+    const auto rules = catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+    for(const auto &rule: rules) {
+      catalogue->RequesterGroupMountRule()->deleteRequesterGroupMountRule(rule.diskInstance, rule.name);
+    }
+  }
+  {
+    // The iterator returned from catalogue->ArchiveFile()->getArchiveFilesItor() will lock
+    // an SQLite file database, so copy all of its results into a list in
+    // order to release the lock before moving on to deleting database rows
+    auto itor = catalogue->ArchiveFile()->getArchiveFilesItor();
+    std::list<cta::common::dataStructures::ArchiveFile> archiveFiles;
+    while(itor.hasMore()) {
+      archiveFiles.push_back(itor.next());
+    }
+
+    for(const auto &archiveFile: archiveFiles) {
+      catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(archiveFile.diskInstance,
+        archiveFile.archiveFileID, *lc);
+    }
+  }
+
+  {
+    //Delete all the entries from the recycle log table
+    auto itor = catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    while(itor.hasMore()){
+      catalogue->FileRecycleLog()->deleteFilesFromRecycleLog(itor.next().vid, *lc);
+    }
+  }
+  {
+    const auto tapes = catalogue->Tape()->getTapes();
+    for(const auto &tape: tapes) {
+      catalogue->Tape()->deleteTape(tape.vid);
+    }
+  }
+  {
+    const auto mediaTypes = catalogue->MediaType()->getMediaTypes();
+    for(const auto &mediaType: mediaTypes) {
+      catalogue->MediaType()->deleteMediaType(mediaType.name);
+    }
+  }
+  {
+    const auto storageClasses = catalogue->StorageClass()->getStorageClasses();
+    for(const auto &storageClass: storageClasses) {
+      catalogue->StorageClass()->deleteStorageClass(storageClass.name);
+    }
+  }
+  {
+    const auto tapePools = catalogue->TapePool()->getTapePools();
+    for(const auto &tapePool: tapePools) {
+      catalogue->TapePool()->deleteTapePool(tapePool.name);
+    }
+  }
+  {
+    const auto logicalLibraries = catalogue->LogicalLibrary()->getLogicalLibraries();
+    for(const auto &logicalLibrary: logicalLibraries) {
+      catalogue->LogicalLibrary()->deleteLogicalLibrary(logicalLibrary.name);
+    }
+  }
+  {
+    const auto mountPolicies = catalogue->MountPolicy()->getMountPolicies();
+    for(const auto &mountPolicy: mountPolicies) {
+      catalogue->MountPolicy()->deleteMountPolicy(mountPolicy.name);
+    }
+  }
+  {
+    const auto diskSystems = catalogue->DiskSystem()->getAllDiskSystems();
+    for(const auto &ds: diskSystems) {
+      catalogue->DiskSystem()->deleteDiskSystem(ds.name);
+    }
+  }
+  {
+    const auto diskInstanceSpaces = catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    for(const auto &dis: diskInstanceSpaces) {
+      catalogue->DiskInstanceSpace()->deleteDiskInstanceSpace(dis.name, dis.diskInstance);
+    }
+  }
+  {
+    const auto virtualOrganizations = catalogue->VO()->getVirtualOrganizations();
+    for(const auto &vo: virtualOrganizations) {
+      catalogue->VO()->deleteVirtualOrganization(vo.name);
+    }
+  }
+  {
+    const auto diskInstances = catalogue->DiskInstance()->getAllDiskInstances();
+    for(const auto &di: diskInstances) {
+      catalogue->DiskInstance()->deleteDiskInstance(di.name);
+    }
+  }
+  checkWipedDatabase(catalogue);
+}
+
+void CatalogueTestUtils::checkWipedDatabase(cta::catalogue::Catalogue *catalogue) {
+  if(!catalogue->AdminUser()->getAdminUsers().empty()) {
+    throw cta::exception::Exception("Found one of more admin users after emptying the database");
+  }
+
+  if(catalogue->ArchiveFile()->getArchiveFilesItor().hasMore()) {
+    throw cta::exception::Exception("Found one of more archive files after emptying the database");
+  }
+
+  if(!catalogue->ArchiveRoute()->getArchiveRoutes().empty()) {
+    throw cta::exception::Exception("Found one of more archive routes after emptying the database");
+  }
+
+  if(catalogue->FileRecycleLog()->getFileRecycleLogItor().hasMore()){
+    throw cta::exception::Exception("Found one or more files in the file recycle log after emptying the database");
+  }
+
+  if(!catalogue->DiskSystem()->getAllDiskSystems().empty()) {
+    throw cta::exception::Exception("Found one of more disk systems after emptying the database");
+  }
+
+  if (!catalogue->DiskInstance()->getAllDiskInstances().empty()) {
+    throw cta::exception::Exception("Found one of more disk instances after emptying the database");
+  }
+
+  if(!catalogue->LogicalLibrary()->getLogicalLibraries().empty()) {
+    throw cta::exception::Exception("Found one of more logical libraries after emptying the database");
+  }
+
+  if(!catalogue->MediaType()->getMediaTypes().empty()) {
+    throw cta::exception::Exception("Found one of more media types after emptying the database");
+  }
+
+  if(!catalogue->MountPolicy()->getMountPolicies().empty()) {
+    throw cta::exception::Exception("Found one of more mount policies after emptying the database");
+  }
+
+  if(!catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty()) {
+    throw cta::exception::Exception("Found one of more requester group mount rules after emptying the database");
+  }
+
+  if(!catalogue->RequesterMountRule()->getRequesterMountRules().empty()) {
+    throw cta::exception::Exception("Found one of more requester mount rules after emptying the database");
+  }
+
+  if(!catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty()) {
+    throw cta::exception::Exception("Found one of more requester activity mount rules after emptying the database");
+  }
+
+  if(!catalogue->StorageClass()->getStorageClasses().empty()) {
+    throw cta::exception::Exception("Found one of more storage classes after emptying the database");
+  }
+
+  if(!catalogue->Tape()->getTapes().empty()) {
+    throw cta::exception::Exception("Found one of more tapes after emptying the database");
+  }
+
+  if(!catalogue->TapePool()->getTapePools().empty()) {
+    throw cta::exception::Exception("Found one of more tape pools after emptying the database");
+  }
+
+  if(!catalogue->VO()->getVirtualOrganizations().empty()) {
+    throw cta::exception::Exception("Found one of more virtual organizations after emptying the database");
+  }
+}
+
+cta::common::dataStructures::SecurityIdentity CatalogueTestUtils::getLocalAdmin() {
+  cta::common::dataStructures::SecurityIdentity localAdmin;
+  localAdmin.username = "local_admin_user";
+  localAdmin.host = "local_admin_host";
+
+  return localAdmin;
+}
+
+cta::common::dataStructures::SecurityIdentity CatalogueTestUtils::getAdmin() {
+  cta::common::dataStructures::SecurityIdentity admin;
+  admin.username = "admin_user_name";
+  admin.host = "admin_host";
+
+  return admin;
+}
+
+cta::common::dataStructures::DiskInstance CatalogueTestUtils::getDiskInstance() {
+  cta::common::dataStructures::DiskInstance di;
+  di.name = "disk instance";
+  di.comment = "Creation of disk instance";
+  return di;
+}
+
+cta::common::dataStructures::VirtualOrganization CatalogueTestUtils::getVo() {
+  cta::common::dataStructures::VirtualOrganization vo;
+  vo.name = "vo";
+  vo.comment = "Creation of virtual organization vo";
+  vo.readMaxDrives = 1;
+  vo.writeMaxDrives = 1;
+  vo.maxFileSize = 0;
+  vo.diskInstanceName = getDiskInstance().name;
+  return vo;
+}
+
+cta::common::dataStructures::VirtualOrganization CatalogueTestUtils::getAnotherVo() {
+  cta::common::dataStructures::VirtualOrganization vo;
+  vo.name = "anotherVo";
+  vo.comment = "Creation of another virtual organization vo";
+  vo.readMaxDrives = 1;
+  vo.writeMaxDrives = 1;
+  vo.maxFileSize = 0;
+  vo.diskInstanceName = getDiskInstance().name;
+  return vo;
+}
+
+cta::common::dataStructures::StorageClass CatalogueTestUtils::getStorageClass() {
+  cta::common::dataStructures::StorageClass storageClass;
+  storageClass.name = "storage_class_single_copy";
+  storageClass.nbCopies = 1;
+  storageClass.vo.name = getVo().name;
+  storageClass.comment = "Creation of storage class with 1 copy on tape";
+  return storageClass;
+}
+
+cta::common::dataStructures::StorageClass CatalogueTestUtils::getAnotherStorageClass() {
+  cta::common::dataStructures::StorageClass storageClass;
+  storageClass.name = "another_storage_class";
+  storageClass.nbCopies = 1;
+  storageClass.vo.name = getVo().name;
+  storageClass.comment = "Creation of another storage class";
+  return storageClass;
+}
+
+cta::common::dataStructures::StorageClass CatalogueTestUtils::getStorageClassDualCopy() {
+  cta::common::dataStructures::StorageClass storageClass;
+  storageClass.name = "storage_class_dual_copy";
+  storageClass.nbCopies = 2;
+  storageClass.vo.name = getVo().name;
+  storageClass.comment = "Creation of storage class with 2 copies on tape";
+  return storageClass;
+}
+
+cta::common::dataStructures::StorageClass CatalogueTestUtils::getStorageClassTripleCopy() {
+  cta::common::dataStructures::StorageClass storageClass;
+  storageClass.name = "storage_class_triple_copy";
+  storageClass.nbCopies = 3;
+  storageClass.vo.name = getVo().name;
+  storageClass.comment = "Creation of storage class with 3 copies on tape";
+  return storageClass;
+}
+
+cta::catalogue::MediaType CatalogueTestUtils::getMediaType() {
+  cta::catalogue::MediaType mediaType;
+  mediaType.name = "media_type";
+  mediaType.capacityInBytes = (uint64_t)10 * 1000 * 1000 * 1000 * 1000;
+  mediaType.cartridge = "cartridge";
+  mediaType.comment = "comment";
+  mediaType.maxLPos = 100;
+  mediaType.minLPos = 1;
+  mediaType.nbWraps = 500;
+  mediaType.primaryDensityCode = 50;
+  mediaType.secondaryDensityCode = 50;
+  return mediaType;
+}
+
+cta::catalogue::CreateTapeAttributes CatalogueTestUtils::getTape1() {
+  cta::catalogue::CreateTapeAttributes tape;
+  tape.vid = "VIDONE";
+  tape.mediaType = getMediaType().name;
+  tape.vendor = "vendor";
+  tape.logicalLibraryName = "logical_library";
+  tape.tapePoolName = "tape_pool";
+  tape.full = false;
+  tape.state = cta::common::dataStructures::Tape::ACTIVE;
+  tape.comment = "Creation of tape one";
+  return tape;
+}
+
+cta::catalogue::CreateTapeAttributes CatalogueTestUtils::getTape2() {
+  // Tape 2 is an exact copy of tape 1 except for its VID and comment
+  auto tape = getTape1();
+  tape.vid = "VIDTWO";
+  tape.comment = "Creation of tape two";
+  return tape;
+}
+
+cta::catalogue::CreateTapeAttributes CatalogueTestUtils::getTape3() {
+  // Tape 3 is an exact copy of tape 1 except for its VID and comment
+  auto tape = getTape1();
+  tape.vid = "VIDTHREE";
+  tape.comment = "Creation of tape three";
+  return tape;
+}
+
+cta::catalogue::CreateMountPolicyAttributes CatalogueTestUtils::getMountPolicy1() {
+  cta::catalogue::CreateMountPolicyAttributes mountPolicy;
+  mountPolicy.name = "mount_policy";
+  mountPolicy.archivePriority = 1;
+  mountPolicy.minArchiveRequestAge = 2;
+  mountPolicy.retrievePriority = 3;
+  mountPolicy.minRetrieveRequestAge = 4;
+  mountPolicy.comment = "Create mount policy";
+  return mountPolicy;
+}
+
+cta::catalogue::CreateMountPolicyAttributes CatalogueTestUtils::getMountPolicy2() {
+  // Higher priority mount policy
+  cta::catalogue::CreateMountPolicyAttributes mountPolicy;
+  mountPolicy.name = "mount_policy_2";
+  mountPolicy.archivePriority = 2;
+  mountPolicy.minArchiveRequestAge = 1;
+  mountPolicy.retrievePriority = 4;
+  mountPolicy.minRetrieveRequestAge = 3;
+  mountPolicy.comment = "Create mount policy";
+  return mountPolicy;
+}
+
+std::map<std::string, cta::catalogue::TapePool> CatalogueTestUtils::tapePoolListToMap(
+  const std::list<cta::catalogue::TapePool> &listOfTapePools) {
+  using namespace cta;
+
+  try {
+    std::map<std::string, cta::catalogue::TapePool> m;
+
+    for(auto &tapePool: listOfTapePools) {
+      if(m.end() != m.find(tapePool.name)) {
+        exception::Exception ex;
+        ex.getMessage() << "Tape pool " << tapePool.name << " is a duplicate";
+        throw ex;
+      }
+      m[tapePool.name] = tapePool;
+    }
+
+    return m;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+std::map<std::string, cta::common::dataStructures::Tape> CatalogueTestUtils::tapeListToMap(
+  const std::list<cta::common::dataStructures::Tape> &listOfTapes) {
+  using namespace cta;
+
+  try {
+    std::map<std::string, cta::common::dataStructures::Tape> vidToTape;
+
+    for (auto &tape: listOfTapes) {
+      if(vidToTape.end() != vidToTape.find(tape.vid)) {
+        throw exception::Exception(std::string("Duplicate VID: value=") + tape.vid);
+      }
+      vidToTape[tape.vid] = tape;
+    }
+
+    return vidToTape;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+std::map<uint64_t, cta::common::dataStructures::ArchiveFile> CatalogueTestUtils::archiveFileItorToMap(
+  cta::catalogue::ArchiveFileItor &itor) {
+  using namespace cta;
+
+  try {
+    std::map<uint64_t, common::dataStructures::ArchiveFile> m;
+    while(itor.hasMore()) {
+      const auto archiveFile = itor.next();
+      if(m.end() != m.find(archiveFile.archiveFileID)) {
+        exception::Exception ex;
+        ex.getMessage() << "Archive file with ID " << archiveFile.archiveFileID << " is a duplicate";
+        throw ex;
+      }
+      m[archiveFile.archiveFileID] = archiveFile;
+    }
+    return m;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+std::map<uint64_t, cta::common::dataStructures::ArchiveFile> CatalogueTestUtils::archiveFileListToMap(
+  const std::list<cta::common::dataStructures::ArchiveFile> &listOfArchiveFiles) {
+  using namespace cta;
+
+  try {
+    std::map<uint64_t, common::dataStructures::ArchiveFile> archiveIdToArchiveFile;
+
+    for (auto &archiveFile: listOfArchiveFiles) {
+      if(archiveIdToArchiveFile.end() != archiveIdToArchiveFile.find(archiveFile.archiveFileID)) {
+        throw exception::Exception(std::string("Duplicate archive file ID: value=") + std::to_string(archiveFile.archiveFileID));
+      }
+      archiveIdToArchiveFile[archiveFile.archiveFileID] = archiveFile;
+    }
+
+    return archiveIdToArchiveFile;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+std::map<std::string, cta::common::dataStructures::AdminUser> CatalogueTestUtils::adminUserListToMap(
+  const std::list<cta::common::dataStructures::AdminUser> &listOfAdminUsers) {
+  using namespace cta;
+
+  try {
+    std::map<std::string, common::dataStructures::AdminUser> m;
+
+    for(auto &adminUser: listOfAdminUsers) {
+      if(m.end() != m.find(adminUser.name)) {
+        exception::Exception ex;
+        ex.getMessage() << "Admin user " << adminUser.name << " is a duplicate";
+        throw ex;
+      }
+      m[adminUser.name] = adminUser;
+    }
+    return m;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/CatalogueTestUtils.hpp b/catalogue/tests/CatalogueTestUtils.hpp
new file mode 100644
index 0000000000..897d38df02
--- /dev/null
+++ b/catalogue/tests/CatalogueTestUtils.hpp
@@ -0,0 +1,106 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <list>
+#include <map>
+
+namespace cta {
+namespace catalogue {
+class Catalogue;
+class CatalogueFactory;
+template <typename Item>
+class CatalogueItor;
+class CreateMountPolicyAttributes;
+class CreateTapeAttributes;
+class MediaType;
+class TapePool;
+}  // namespace catalogue
+
+namespace common {
+namespace dataStructures {
+class AdminUser;
+class ArchiveFile;
+class DiskInstance;
+class LogicalLibrary;
+class SecurityIdentity;
+class StorageClass;
+class Tape;
+class VirtualOrganization;
+}  // namespace dataStructures
+}  // namespace common
+
+namespace log {
+class LogContext;
+}  // namespace log
+}  // namespace cta
+
+namespace unitTests {
+
+const uint32_t PUBLIC_DISK_USER = 9751;
+const uint32_t PUBLIC_DISK_GROUP = 9752;
+const uint32_t DISK_FILE_OWNER_UID = 9753;
+const uint32_t DISK_FILE_GID = 9754;
+const uint32_t NON_EXISTENT_DISK_FILE_OWNER_UID = 9755;
+const uint32_t NON_EXISTENT_DISK_FILE_GID = 9756;
+
+class CatalogueTestUtils {
+public:
+  static std::unique_ptr<cta::catalogue::Catalogue> createCatalogue(
+    cta::catalogue::CatalogueFactory *const *const catalogueFactoryPtrPtr, cta::log::LogContext *lc);
+  static cta::common::dataStructures::SecurityIdentity getLocalAdmin();
+  static cta::common::dataStructures::SecurityIdentity getAdmin();
+  static cta::common::dataStructures::DiskInstance getDiskInstance();
+  static cta::common::dataStructures::VirtualOrganization getVo();
+  static cta::common::dataStructures::VirtualOrganization getAnotherVo();
+  static cta::common::dataStructures::StorageClass getStorageClass();
+  static cta::common::dataStructures::StorageClass getAnotherStorageClass();
+  static cta::common::dataStructures::StorageClass getStorageClassDualCopy();
+  static cta::common::dataStructures::StorageClass getStorageClassTripleCopy();
+  static cta::catalogue::MediaType getMediaType();
+  static cta::catalogue::CreateTapeAttributes getTape1();
+  static cta::catalogue::CreateTapeAttributes getTape2();
+  static cta::catalogue::CreateTapeAttributes getTape3();
+  static cta::catalogue::CreateMountPolicyAttributes getMountPolicy1();
+  static cta::catalogue::CreateMountPolicyAttributes getMountPolicy2();
+
+  static std::map<std::string, cta::catalogue::TapePool> tapePoolListToMap(
+    const std::list<cta::catalogue::TapePool> &listOfTapePools);
+
+  static std::map<std::string, cta::common::dataStructures::LogicalLibrary> logicalLibraryListToMap(
+    const std::list<cta::common::dataStructures::LogicalLibrary> &listOfLibs);
+
+
+  static std::map<std::string, cta::common::dataStructures::Tape> tapeListToMap(
+    const std::list<cta::common::dataStructures::Tape> &listOfTapes);
+
+  static std::map<uint64_t, cta::common::dataStructures::ArchiveFile> archiveFileItorToMap(
+    cta::catalogue::CatalogueItor<cta::common::dataStructures::ArchiveFile> &itor);
+
+  static std::map<uint64_t, cta::common::dataStructures::ArchiveFile> archiveFileListToMap(
+    const std::list<cta::common::dataStructures::ArchiveFile> &listOfArchiveFiles);
+
+  static std::map<std::string, cta::common::dataStructures::AdminUser> adminUserListToMap(
+    const std::list<cta::common::dataStructures::AdminUser> &listOfAdminUsers);
+
+private:
+  static void wipeDatabase(cta::catalogue::Catalogue *catalogue, cta::log::LogContext *lc);
+  static void checkWipedDatabase(cta::catalogue::Catalogue *catalogue);
+};
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/DbConfigVersionOfCatalogueTest.cpp b/catalogue/tests/DbConfigVersionOfCatalogueTest.cpp
new file mode 100644
index 0000000000..56f4fab812
--- /dev/null
+++ b/catalogue/tests/DbConfigVersionOfCatalogueTest.cpp
@@ -0,0 +1,65 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/tests/modules/AdminUserCatalogueTest.hpp"
+#include "catalogue/tests/modules/ArchiveFileCatalogueTest.hpp"
+#include "catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp"
+#include "catalogue/tests/modules/DiskInstanceCatalogueTest.hpp"
+#include "catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp"
+#include "catalogue/tests/modules/DiskSystemCatalogueTest.hpp"
+#include "catalogue/tests/modules/DriveConfigCatalogueTest.hpp"
+#include "catalogue/tests/modules/DriveStateCatalogueTest.hpp"
+#include "catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp"
+#include "catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp"
+#include "catalogue/tests/modules/MediaTypeCatalogueTest.hpp"
+#include "catalogue/tests/modules/MountPolicyCatalogueTest.hpp"
+#include "catalogue/tests/modules/RequesterActivityMountRuleTest.hpp"
+#include "catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp"
+#include "catalogue/tests/modules/RequesterMountRuleTest.hpp"
+#include "catalogue/tests/modules/SchemaCatalogueTest.hpp"
+#include "catalogue/tests/modules/StorageClassCatalogueTest.hpp"
+#include "catalogue/tests/modules/TapeCatalogueTest.hpp"
+#include "catalogue/tests/modules/TapeFileCatalogueTest.hpp"
+#include "catalogue/tests/modules/TapePoolCatalogueTest.hpp"
+#include "catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp"
+#include "tests/GlobalCatalogueFactoryForUnitTests.hpp"
+
+namespace unitTests {
+
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_SchemaTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_AdminUserTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_DiskSystemTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_DiskInstanceTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_DiskInstanceSpaceTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_VirtualOrganizationTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_ArchiveRouteTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_MediaTypeTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_StorageClassTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_TapePoolTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_TapeTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_MountPolicyTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_RequesterActivityMountRuleTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_RequesterMountRuleTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_RequesterGroupMountRuleTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_LogicalLibraryTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_TapeFileTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_FileRecycleLogTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_DriveConfigTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_DriveStateTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_catalogue_ArchiveFileTest, ::testing::Values(&g_catalogueFactoryForUnitTests));
+
+}  // namespace unitTests
diff --git a/catalogue/tests/DriveConfigCatalogueTest.cpp b/catalogue/tests/DriveConfigCatalogueTest.cpp
new file mode 100644
index 0000000000..53aa7b4efc
--- /dev/null
+++ b/catalogue/tests/DriveConfigCatalogueTest.cpp
@@ -0,0 +1,287 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/tests/DriveConfigCatalogueTest.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/SourcedParameter.hpp"
+
+namespace unitTests {
+
+cta_catalogue_DriveConfigTest::cta_catalogue_DriveConfigTest()
+  : m_dummyLog("dummy", "dummy") {
+}
+
+void cta_catalogue_DriveConfigTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  try {
+    cta::catalogue::CatalogueFactory *const *const catalogueFactoryPtrPtr = GetParam();
+
+    if (nullptr == catalogueFactoryPtrPtr) {
+      throw cta::exception::Exception("Global pointer to the catalogue factory pointer for unit-tests in null");
+    }
+
+    if (nullptr == (*catalogueFactoryPtrPtr)) {
+      throw cta::exception::Exception("Global pointer to the catalogue factoryfor unit-tests in null");
+    }
+
+    m_catalogue = (*catalogueFactoryPtrPtr)->create();
+
+    {
+      const auto configurationTapeNamesAndKeys = m_catalogue->DriveConfig()->getTapeDriveConfigNamesAndKeys();
+      for (const auto& nameAndKey : configurationTapeNamesAndKeys) {
+        m_catalogue->DriveConfig()->deleteTapeDriveConfig(nameAndKey.first, nameAndKey.second);
+      }
+    }
+  } catch(cta::exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void cta_catalogue_DriveConfigTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, getTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig.value();
+  ASSERT_EQ(daemonUserName.category(), category);
+  ASSERT_EQ(daemonUserName.value(), value);
+  ASSERT_EQ(daemonUserName.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, getAllDrivesConfigs) {
+  std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> tapeDriveConfigs;
+  // Create 100 tape drives
+  for (size_t i = 0; i < 100; i++) {
+    std::stringstream ss;
+    ss << "VDSTK" << std::setw(5) << std::setfill('0') << i;
+
+    cta::SourcedParameter<std::string> daemonUserName {
+      "taped", "DaemonUserName", "cta", "Compile time default"};
+    m_catalogue->DriveConfig()->createTapeDriveConfig(ss.str(), daemonUserName.category(), daemonUserName.key(),
+      daemonUserName.value(), daemonUserName.source());
+    tapeDriveConfigs.push_back({ss.str(), daemonUserName.category(), daemonUserName.key(), daemonUserName.value(),
+      daemonUserName.source()});
+    cta::SourcedParameter<std::string> defaultConfig {
+      "taped", "defaultConfig", "cta", "Random Default Config for Testing"};
+    m_catalogue->DriveConfig()->createTapeDriveConfig(ss.str(), defaultConfig.category(), defaultConfig.key(),
+      defaultConfig.value(), defaultConfig.source());
+    tapeDriveConfigs.push_back({ss.str(), defaultConfig.category(), defaultConfig.key(), defaultConfig.value(),
+      defaultConfig.source()});
+  }
+  const auto drivesConfigs = m_catalogue->DriveConfig()->getTapeDriveConfigs();
+  ASSERT_EQ(tapeDriveConfigs.size(), drivesConfigs.size());
+  for (const auto& dc : drivesConfigs) {
+    m_catalogue->DriveConfig()->deleteTapeDriveConfig(dc.tapeDriveName, dc.keyName);
+  }
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, setSourcedParameterWithEmptyValue) {
+  const std::string tapeDriveName = "VDSTK11";
+
+  cta::SourcedParameter<std::string> raoLtoOptions {
+    "taped", "RAOLTOAlgorithmOptions", "", "Compile time default"
+  };
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, raoLtoOptions.category(), raoLtoOptions.key(),
+    raoLtoOptions.value(), raoLtoOptions.source());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, raoLtoOptions.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig.value();
+  ASSERT_EQ(raoLtoOptions.category(), category);
+  ASSERT_EQ("", value);
+  ASSERT_EQ(raoLtoOptions.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, raoLtoOptions.key());
+
+  cta::SourcedParameter<std::string> backendPath{
+    "ObjectStore", "BackendPath"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, backendPath.category(), backendPath.key(),
+    backendPath.value(), backendPath.source());
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, backendPath.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  std::tie(category, value, source) = driveConfig.value();
+  ASSERT_EQ(backendPath.category(), category);
+  ASSERT_EQ("", value);
+  ASSERT_EQ("", source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, backendPath.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, failTogetTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const std::string wrongKey = "wrongKey";
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(wrongName, daemonUserName.key());
+  ASSERT_FALSE(driveConfig);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, wrongKey);
+  ASSERT_FALSE(driveConfig);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(wrongName, wrongKey);
+  ASSERT_FALSE(driveConfig);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, failTodeleteTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const std::string wrongKey = "wrongKey";
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(wrongName, daemonUserName.key());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, wrongKey);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(wrongName, wrongKey);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  // Good deletion
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_FALSE(driveConfig);
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, multipleDriveConfig) {
+  const std::string tapeDriveName1 = "VDSTK11";
+  const std::string tapeDriveName2 = "VDSTK12";
+
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+  cta::SourcedParameter<std::string> daemonGroupName {
+    "taped", "DaemonGroupName", "tape", "Compile time default"};
+
+  // Combinations of tapeDriveName1/2 and daemonUserName and daemonGroupName
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+  auto driveConfig1UserName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName1, daemonUserName.key());
+  auto driveConfig2UserName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName2, daemonUserName.key());
+  auto driveConfig1GroupName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName1, daemonGroupName.key());
+  auto driveConfig2GroupName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName2, daemonGroupName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig1UserName));
+  ASSERT_TRUE(static_cast<bool>(driveConfig2UserName));
+  ASSERT_TRUE(static_cast<bool>(driveConfig1GroupName));
+  ASSERT_TRUE(static_cast<bool>(driveConfig2GroupName));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig1UserName.value();
+  ASSERT_EQ(daemonUserName.category(), category);
+  ASSERT_EQ(daemonUserName.value(), value);
+  ASSERT_EQ(daemonUserName.source(), source);
+  std::tie(category, value, source) = driveConfig2UserName.value();
+  ASSERT_EQ(daemonUserName.category(), category);
+  ASSERT_EQ(daemonUserName.value(), value);
+  ASSERT_EQ(daemonUserName.source(), source);
+  std::tie(category, value, source) = driveConfig1GroupName.value();
+  ASSERT_EQ(daemonGroupName.category(), category);
+  ASSERT_EQ(daemonGroupName.value(), value);
+  ASSERT_EQ(daemonGroupName.source(), source);
+  std::tie(category, value, source) = driveConfig2GroupName.value();
+  ASSERT_EQ(daemonGroupName.category(), category);
+  ASSERT_EQ(daemonGroupName.value(), value);
+  ASSERT_EQ(daemonGroupName.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName1, daemonUserName.key());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName1, daemonGroupName.key());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName2, daemonUserName.key());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName2, daemonGroupName.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, getNamesAndKeysOfMultipleDriveConfig) {
+  const std::string tapeDriveName1 = "VDSTK11";
+  const std::string tapeDriveName2 = "VDSTK12";
+
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+  cta::SourcedParameter<std::string> daemonGroupName {
+    "taped", "DaemonGroupName", "tape", "Compile time default"};
+
+  // Combinations of tapeDriveName1/2 and daemonUserName and daemonGroupName
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+
+  const auto configurationTapeNamesAndKeys = m_catalogue->DriveConfig()->getTapeDriveConfigNamesAndKeys();
+
+  for (const auto& nameAndKey : configurationTapeNamesAndKeys) {
+    m_catalogue->DriveConfig()->deleteTapeDriveConfig(nameAndKey.first, nameAndKey.second);
+  }
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, modifyTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+  // Both share same key
+  cta::SourcedParameter<std::string> daemonUserName1 {
+    "taped1", "DaemonUserName", "cta1", "Compile time1 default"};
+  cta::SourcedParameter<std::string> daemonUserName2 {
+    "taped2", "DaemonUserName", "cta2", "Compile time2 default"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName1.category(), daemonUserName1.key(),
+    daemonUserName1.value(), daemonUserName1.source());
+  const auto driveConfig1 = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName1.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig1));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig1.value();
+  ASSERT_NE(daemonUserName2.category(), category);
+  ASSERT_NE(daemonUserName2.value(), value);
+  ASSERT_NE(daemonUserName2.source(), source);
+  m_catalogue->DriveConfig()->modifyTapeDriveConfig(tapeDriveName, daemonUserName2.category(), daemonUserName2.key(),
+    daemonUserName2.value(), daemonUserName2.source());
+  const auto driveConfig2 = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName1.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig2));
+  std::tie(category, value, source) = driveConfig2.value();
+  ASSERT_EQ(daemonUserName2.category(), category);
+  ASSERT_EQ(daemonUserName2.value(), value);
+  ASSERT_EQ(daemonUserName2.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName1.key());
+}
+
+}  // namespace unitTests
diff --git a/catalogue/tests/DriveConfigCatalogueTest.hpp b/catalogue/tests/DriveConfigCatalogueTest.hpp
new file mode 100644
index 0000000000..c03102f6c9
--- /dev/null
+++ b/catalogue/tests/DriveConfigCatalogueTest.hpp
@@ -0,0 +1,42 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DriveConfigTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DriveConfigTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/DriveStateCatalogueTest.cpp b/catalogue/tests/DriveStateCatalogueTest.cpp
new file mode 100644
index 0000000000..743c1fa550
--- /dev/null
+++ b/catalogue/tests/DriveStateCatalogueTest.cpp
@@ -0,0 +1,1547 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/TapeDrivesCatalogueState.hpp"
+#include "catalogue/tests/DriveStateCatalogueTest.hpp"
+#include "common/dataStructures/DesiredDriveState.hpp"
+#include "common/dataStructures/DriveInfo.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/SourcedParameter.hpp"
+
+namespace unitTests {
+
+namespace {
+cta::common::dataStructures::SecurityIdentity getAdmin() {
+  cta::common::dataStructures::SecurityIdentity admin;
+  admin.username = "admin_user_name";
+  admin.host = "admin_host";
+  return admin;
+}
+
+cta::common::dataStructures::TapeDrive getTapeDriveWithMandatoryElements(const std::string &driveName) {
+  cta::common::dataStructures::TapeDrive tapeDrive;
+  tapeDrive.driveName = driveName;
+  tapeDrive.host = "admin_host";
+  tapeDrive.logicalLibrary = "VLSTK10";
+  tapeDrive.mountType = cta::common::dataStructures::MountType::NoMount;
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  tapeDrive.desiredUp = false;
+  tapeDrive.desiredForceDown = false;
+  return tapeDrive;
+}
+
+cta::common::dataStructures::TapeDrive getTapeDriveWithAllElements(const std::string &driveName) {
+  cta::common::dataStructures::TapeDrive tapeDrive;
+  tapeDrive.driveName = driveName;
+  tapeDrive.host = "admin_host";
+  tapeDrive.logicalLibrary = "VLSTK10";
+  tapeDrive.mountType = cta::common::dataStructures::MountType::NoMount;
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  tapeDrive.desiredUp = false;
+  tapeDrive.desiredForceDown = false;
+  tapeDrive.diskSystemName = "dummyDiskSystemName";
+  tapeDrive.reservedBytes = 694498291384;
+  tapeDrive.reservationSessionId = 0;
+
+  tapeDrive.sessionStartTime = 1001;
+  tapeDrive.mountStartTime = 1002;
+  tapeDrive.transferStartTime = 1003;
+  tapeDrive.unloadStartTime = 1004;
+  tapeDrive.unmountStartTime = 1005;
+  tapeDrive.drainingStartTime = 1006;
+  tapeDrive.downOrUpStartTime = 1007;
+  tapeDrive.probeStartTime = 1008;
+  tapeDrive.cleanupStartTime = 1009;
+  tapeDrive.startStartTime = 1010;
+  tapeDrive.shutdownTime = 1011;
+
+  tapeDrive.reasonUpDown = "Random Reason";
+
+  tapeDrive.currentVid = "VIDONE";
+  tapeDrive.ctaVersion = "v1.0.0";
+  tapeDrive.currentPriority = 3;
+  tapeDrive.currentActivity = "Activity1";
+  tapeDrive.currentTapePool = "tape_pool_0";
+  tapeDrive.nextMountType = cta::common::dataStructures::MountType::Retrieve;
+  tapeDrive.nextVid = "VIDTWO";
+  tapeDrive.nextTapePool = "tape_pool_1";
+  tapeDrive.nextPriority = 1;
+  tapeDrive.nextActivity = "Activity2";
+
+  tapeDrive.devFileName = "fileName";
+  tapeDrive.rawLibrarySlot = "librarySlot1";
+
+  tapeDrive.currentVo = "VO_ONE";
+  tapeDrive.nextVo = "VO_TWO";
+
+  tapeDrive.userComment = "Random comment";
+  tapeDrive.creationLog = cta::common::dataStructures::EntryLog("user_name_1", "host_1", 100002);
+  tapeDrive.lastModificationLog = cta::common::dataStructures::EntryLog("user_name_2", "host_2", 10032131);
+
+  return tapeDrive;
+}
+}  // namespace
+
+cta_catalogue_DriveStateTest::cta_catalogue_DriveStateTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(getAdmin()) {
+}
+
+void cta_catalogue_DriveStateTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  try {
+    cta::catalogue::CatalogueFactory *const *const catalogueFactoryPtrPtr = GetParam();
+
+    if (nullptr == catalogueFactoryPtrPtr) {
+      throw cta::exception::Exception("Global pointer to the catalogue factory pointer for unit-tests in null");
+    }
+
+    if (nullptr == (*catalogueFactoryPtrPtr)) {
+      throw cta::exception::Exception("Global pointer to the catalogue factoryfor unit-tests in null");
+    }
+
+    m_catalogue = (*catalogueFactoryPtrPtr)->create();
+
+    {
+      const auto tapeDriveNames = m_catalogue->DriveState()->getTapeDriveNames();
+      for (const auto& name : tapeDriveNames) {
+        m_catalogue->DriveState()->deleteTapeDrive(name);
+      }
+    }
+  } catch(cta::exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+void cta_catalogue_DriveStateTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveNames) {
+  const std::list<std::string> tapeDriveNames = {"VDSTK11", "VDSTK12", "VDSTK21", "VDSTK22"};
+  for (const auto& name : tapeDriveNames) {
+    const auto tapeDrive = getTapeDriveWithMandatoryElements(name);
+    m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  }
+  const auto storedTapeDriveNames = m_catalogue->DriveState()->getTapeDriveNames();
+  ASSERT_EQ(tapeDriveNames, storedTapeDriveNames);
+  for (const auto& name : tapeDriveNames) {
+    m_catalogue->DriveState()->deleteTapeDrive(name);
+  }
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getAllTapeDrives) {
+  std::list<std::string> tapeDriveNames;
+  // Create 100 tape drives
+  for (size_t i = 0; i < 100; i++) {
+    std::stringstream ss;
+    ss << "VDSTK" << std::setw(5) << std::setfill('0') << i;
+    tapeDriveNames.push_back(ss.str());
+  }
+  std::list<cta::common::dataStructures::TapeDrive> tapeDrives;
+  for (const auto& name : tapeDriveNames) {
+    const auto tapeDrive = getTapeDriveWithMandatoryElements(name);
+    m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+    tapeDrives.push_back(tapeDrive);
+  }
+  auto storedTapeDrives = m_catalogue->DriveState()->getTapeDrives();
+  ASSERT_EQ(tapeDriveNames.size(), storedTapeDrives.size());
+  while (!storedTapeDrives.empty()) {
+    const auto storedTapeDrive = storedTapeDrives.front();
+    const auto tapeDrive = tapeDrives.front();
+    storedTapeDrives.pop_front();
+    tapeDrives.pop_front();
+    ASSERT_EQ(tapeDrive, storedTapeDrive);
+  }
+  for (const auto& name : tapeDriveNames) {
+    m_catalogue->DriveState()->deleteTapeDrive(name);
+  }
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDrive) {
+  const std::string tapeDriveName = "VDSTK11";
+  const auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(tapeDrive, storedTapeDrive);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithEmptyEntryLog) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.creationLog = cta::common::dataStructures::EntryLog("", "", 0);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().creationLog);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithNonExistingLogicalLibrary) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().logicalLibraryDisabled);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithDisabledLogicalLibrary) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->createLogicalLibrary(m_admin, tapeDrive.logicalLibrary, true, "comment");
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(storedTapeDrive.value().logicalLibraryDisabled);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+  m_catalogue->deleteLogicalLibrary(tapeDrive.logicalLibrary);
+}
+
+
+TEST_P(cta_catalogue_DriveStateTest, failToGetTapeDrive) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(wrongName);
+  ASSERT_FALSE(storedTapeDrive);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDriveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, failToDeleteTapeDrive) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  m_catalogue->DriveState()->deleteTapeDrive(wrongName);
+  auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDriveName);
+  storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithAllElements) {
+  const std::string tapeDriveName = "VDSTK11";
+  const auto tapeDrive = getTapeDriveWithAllElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(tapeDrive, storedTapeDrive.value());
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, multipleTapeDrives) {
+  const std::string tapeDriveName1 = "VDSTK11";
+  const std::string tapeDriveName2 = "VDSTK12";
+  const auto tapeDrive1 = getTapeDriveWithMandatoryElements(tapeDriveName1);
+  const auto tapeDrive2 = getTapeDriveWithAllElements(tapeDriveName2);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive1);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive2);
+  const auto storedTapeDrive1 = m_catalogue->DriveState()->getTapeDrive(tapeDrive1.driveName);
+  const auto storedTapeDrive2 = m_catalogue->DriveState()->getTapeDrive(tapeDrive2.driveName);
+  ASSERT_EQ(tapeDrive1, storedTapeDrive1);
+  ASSERT_EQ(tapeDrive2, storedTapeDrive2);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive1.driveName);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive2.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateEmpty) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.reasonUpDown = "Previous reason";
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  {
+    cta::common::dataStructures::DesiredDriveState desiredState;
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reasonUpDown));
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value(), tapeDrive.reasonUpDown.value());
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateWithEmptyReason) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  {
+    cta::common::dataStructures::DesiredDriveState desiredState;
+    desiredState.reason = "";
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  // SqlLite (InMemory) returns an empty string and Oracle returns a std::nullopt
+  if (storedTapeDrive.value().reasonUpDown) {
+    ASSERT_TRUE(storedTapeDrive.value().reasonUpDown.value().empty());
+  } else {
+    ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reasonUpDown));
+  }
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredState) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::common::dataStructures::DesiredDriveState desiredState;
+  desiredState.up = false;
+  desiredState.forceDown = true;
+  desiredState.reason = "reason";
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp , desiredState.up);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown , desiredState.forceDown);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value() , desiredState.reason);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateComment) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  // It should keep this Desired Status
+  tapeDrive.desiredUp = true;
+  tapeDrive.desiredForceDown = false;
+  tapeDrive.reasonUpDown = "reason";
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::common::dataStructures::DesiredDriveState desiredState;
+  // It should update only the comment
+  const std::string comment = "New Comment";
+  desiredState.up = false;
+  desiredState.forceDown = true;
+  desiredState.reason = "reason2";
+  desiredState.comment = comment;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp , tapeDrive.desiredUp);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown , tapeDrive.desiredForceDown);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value() , tapeDrive.reasonUpDown);
+  ASSERT_EQ(storedTapeDrive.value().userComment.value() , comment);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateEmptyComment) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  // It should keep this Desired Status
+  tapeDrive.desiredUp = true;
+  tapeDrive.desiredForceDown = false;
+  tapeDrive.reasonUpDown = "reason";
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::common::dataStructures::DesiredDriveState desiredState;
+  // It should update only the comment
+  const std::string comment = "";
+  desiredState.up = false;
+  desiredState.forceDown = true;
+  desiredState.reason = "reason2";
+  desiredState.comment = comment;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp , tapeDrive.desiredUp);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown , tapeDrive.desiredForceDown);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value() , tapeDrive.reasonUpDown);
+  // SqlLite (InMemory) returns an empty string and Oracle returns a std::nullopt
+  if (storedTapeDrive.value().userComment) {
+    ASSERT_TRUE(storedTapeDrive.value().userComment.value().empty());
+  } else {
+    ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().userComment));
+  }
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setTapeDriveStatistics) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Transferring;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatsInputs inputs;
+  inputs.reportTime = time(nullptr);
+  inputs.bytesTransferred = 123456789;
+  inputs.filesTransferred = 987654321;
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatistics(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().bytesTransferedInSession.value(), inputs.bytesTransferred);
+  ASSERT_EQ(storedTapeDrive.value().filesTransferedInSession.value(), inputs.filesTransferred);
+  const auto lastModificationLog = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host,
+    inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value() , lastModificationLog);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setTapeDriveStatisticsInNoTransferingStatus) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::ReportDriveStatsInputs inputs;
+  inputs.reportTime = time(nullptr);
+  inputs.bytesTransferred = 123456789;
+  inputs.filesTransferred = 987654321;
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatistics(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().bytesTransferedInSession);
+  ASSERT_FALSE(storedTapeDrive.value().filesTransferedInSession);
+  ASSERT_FALSE(storedTapeDrive.value().lastModificationLog);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusSameAsPrevious) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  // We update keeping the same status, so it has to update only the lastModificationLog
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = tapeDrive.driveStatus;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 123456;
+  inputs.filesTransferred = 987654;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(driveInfo.driveName, storedTapeDrive.value().driveName);
+  ASSERT_EQ(inputs.status, storedTapeDrive.value().driveStatus);
+  ASSERT_NE(inputs.mountType, storedTapeDrive.value().mountType);  // Not update this value
+  ASSERT_EQ(driveInfo.host, storedTapeDrive.value().host);
+  ASSERT_EQ(driveInfo.logicalLibrary, storedTapeDrive.value().logicalLibrary);
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(log, storedTapeDrive.value().lastModificationLog.value());
+  ASSERT_FALSE(storedTapeDrive.value().bytesTransferedInSession);
+  ASSERT_FALSE(storedTapeDrive.value().filesTransferedInSession);
+  // Disk reservations are not updated by updateTapeDriveStatus()
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusSameTransferingAsPrevious) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Transferring;
+  tapeDrive.sessionStartTime = time(nullptr);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto test = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(tapeDrive.sessionStartTime, test.value().sessionStartTime.value());
+  // We update keeping the same status, so it has to update only the lastModificationLog
+  const uint64_t elapsedTime = 1000;
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = tapeDrive.driveStatus;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = tapeDrive.sessionStartTime.value() + elapsedTime;
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 123456;
+  inputs.filesTransferred = 987654;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(driveInfo.driveName, storedTapeDrive.value().driveName);
+  ASSERT_EQ(inputs.status, storedTapeDrive.value().driveStatus);
+  ASSERT_NE(inputs.mountType, storedTapeDrive.value().mountType);  // Not update this value
+  ASSERT_EQ(driveInfo.host, storedTapeDrive.value().host);
+  ASSERT_EQ(driveInfo.logicalLibrary, storedTapeDrive.value().logicalLibrary);
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(log, storedTapeDrive.value().lastModificationLog.value());
+  ASSERT_EQ(inputs.byteTransferred, storedTapeDrive.value().bytesTransferedInSession.value());
+  ASSERT_EQ(inputs.filesTransferred, storedTapeDrive.value().filesTransferedInSession.value());
+  // It will keep names and bytes, because it isn't in state UP
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  // Check elapsed time
+  ASSERT_EQ(storedTapeDrive.value().sessionElapsedTime.value(), inputs.reportTime - tapeDrive.sessionStartTime.value());
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusDown) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Down;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_EQ(storedTapeDrive.value().downOrUpStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, cta::common::dataStructures::MountType::NoMount);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, cta::common::dataStructures::DriveStatus::Down);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp, false);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown, false);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVid));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentTapePool));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVo));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value(), inputs.reason);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUp) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.desiredUp = true;
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_EQ(storedTapeDrive.value().downOrUpStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, cta::common::dataStructures::MountType::NoMount);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, cta::common::dataStructures::DriveStatus::Up);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVid));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentTapePool));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVo));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reasonUpDown));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUpButDesiredIsDown) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::DrainingToDisk;  // To force a change of state
+  tapeDrive.desiredUp = false;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, cta::common::dataStructures::DriveStatus::Down);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value(), inputs.reason);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUpCleanSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  tapeDrive.diskSystemName = "DISK_SYSTEM_NAME";
+  tapeDrive.reservedBytes = 123456789;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUpDontCleanSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  tapeDrive.diskSystemName = std::nullopt;
+  tapeDrive.reservedBytes = std::nullopt;
+  tapeDrive.reservationSessionId = std::nullopt;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusProbing) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Probing;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_EQ(storedTapeDrive.value().probeStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVid));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentTapePool));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVo));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusStarting) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Starting;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_EQ(storedTapeDrive.value().sessionStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_EQ(storedTapeDrive.value().currentActivity.value(), inputs.activity);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusMounting) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Mounting;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_EQ(storedTapeDrive.value().mountStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusTransfering) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Transferring;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_EQ(storedTapeDrive.value().bytesTransferedInSession.value(), inputs.byteTransferred);
+  ASSERT_EQ(storedTapeDrive.value().filesTransferedInSession.value(), inputs.filesTransferred);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_EQ(storedTapeDrive.value().sessionElapsedTime.value(), 0);  // Because it's starting
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_EQ(storedTapeDrive.value().transferStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUnloading) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Unloading;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_EQ(storedTapeDrive.value().unloadStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUnmounting) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Unmounting;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_EQ(storedTapeDrive.value().unmountStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusDrainingToDisk) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::DrainingToDisk;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_EQ(storedTapeDrive.value().drainingStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusCleaningUp) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::CleaningUp;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_EQ(storedTapeDrive.value().cleanupStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusShutdown) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Shutdown;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_EQ(storedTapeDrive.value().shutdownTime.value(), inputs.reportTime);
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+
+TEST_P(cta_catalogue_DriveStateTest, addDiskSpaceReservationWhenItsNull) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = std::nullopt;
+  tapeDrive.reservedBytes = std::nullopt;
+  tapeDrive.reservationSessionId = std::nullopt;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = "space1";
+  const uint64_t reservedBytes = 987654;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 123;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, incrementAnExistingDiskSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "existing_space";
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = tapeDrive.diskSystemName.value();
+  const uint64_t reservedBytes = 852;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = tapeDrive.reservationSessionId.value();
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes + tapeDrive.reservedBytes.value());
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, decrementANonExistingDiskSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = std::nullopt;
+  tapeDrive.reservedBytes = std::nullopt;
+  tapeDrive.reservationSessionId = std::nullopt;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = "space1";
+  const uint64_t reservedBytes = 852;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 123;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, decrementAExistingDiskSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "existing_space";
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request1;
+  const std::string spaceName = tapeDrive.diskSystemName.value();
+  const uint64_t reservedBytes = 852;
+  request1.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = tapeDrive.reservationSessionId.value();
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, request1, dummyLc);
+
+  const auto storedTapeDrive1 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive1.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive1.value().reservedBytes.value(), tapeDrive.reservedBytes.value() - reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive1.value().reservationSessionId.value(), mountId);
+
+  cta::DiskSpaceReservationRequest request2;
+  request2.addRequest(tapeDrive.diskSystemName.value(), tapeDrive.reservedBytes.value() - reservedBytes);
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, request2, dummyLc);
+
+  const auto storedTapeDrive2 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive2.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive2.value().reservedBytes.value(), 0);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive2.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, incrementAnExistingDiskSpaceReservationAndThenLargerDecrement) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "existing_space";
+  tapeDrive.reservedBytes = 10;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest increaseRequest;
+  const std::string spaceName = tapeDrive.diskSystemName.value();
+  const uint64_t reservedBytes = 20;
+  increaseRequest.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = tapeDrive.reservationSessionId.value();
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, increaseRequest, dummyLc);
+
+  const auto storedTapeDrive1 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive1.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive1.value().reservedBytes.value(), reservedBytes + tapeDrive.reservedBytes.value());
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive1.value().reservationSessionId.value(), mountId);
+
+  cta::DiskSpaceReservationRequest decreaseRequest;
+  decreaseRequest.addRequest(tapeDrive.diskSystemName.value(), 100000);  // Decrease a bigger number of reserved bytes
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, decreaseRequest, dummyLc);
+
+  const auto storedTapeDrive2 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive2.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive2.value().reservedBytes.value(), 0);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive2.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, failToIncrementAnOldDiskSystem) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "old_space";
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  // New Disk Space
+  cta::DiskSpaceReservationRequest newRequest;
+  const std::string spaceName = "new_space";
+  const uint64_t reservedBytes = 345;
+  newRequest.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 3;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, newRequest, dummyLc);
+
+  // Decrease Old Space
+  cta::DiskSpaceReservationRequest oldRequest;
+  oldRequest.addRequest(tapeDrive.diskSystemName.value(), tapeDrive.reservedBytes.value());
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, tapeDrive.reservationSessionId.value(), oldRequest,
+    dummyLc);
+
+  // Check it keeps the new disk space system values
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, sameSystemNameButDifferentMountID) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string diskSystemName = "space_name";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = diskSystemName;
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  // New Disk Space
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = diskSystemName;
+  const uint64_t reservedBytes = 345;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 3;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  // Check it keeps the new disk space system values
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), diskSystemName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, failToDecrementAnOldMountIDAndDecrementNewAgain) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string diskSystemName = "space_name";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = diskSystemName;
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  // New Disk Space
+  cta::DiskSpaceReservationRequest newRequest;
+  const uint64_t reservedBytes = 345;
+  newRequest.addRequest(diskSystemName, reservedBytes);
+  const uint64_t mountId = 3;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, newRequest, dummyLc);
+
+  // Decrease Old Space
+  cta::DiskSpaceReservationRequest oldRequest;
+  oldRequest.addRequest(diskSystemName, tapeDrive.reservedBytes.value());
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, tapeDrive.reservationSessionId.value(), oldRequest, dummyLc);
+
+  // Check it keeps the new disk space system values
+  auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), diskSystemName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  // Decrease New Space
+  cta::DiskSpaceReservationRequest decreaseRequest;
+  const uint64_t decreasedBytes = 10;
+  decreaseRequest.addRequest(diskSystemName, decreasedBytes);
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, decreaseRequest, dummyLc);
+
+  // Check it keeps the new disk space system values
+  storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), diskSystemName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes - decreasedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+}  // namespace unitTests
diff --git a/catalogue/tests/DriveStateCatalogueTest.hpp b/catalogue/tests/DriveStateCatalogueTest.hpp
new file mode 100644
index 0000000000..1d9f1aa16e
--- /dev/null
+++ b/catalogue/tests/DriveStateCatalogueTest.hpp
@@ -0,0 +1,45 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DriveStateTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DriveStateTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/InMemoryCatalogueTest.cpp b/catalogue/tests/InMemoryCatalogueTest.cpp
similarity index 94%
rename from catalogue/InMemoryCatalogueTest.cpp
rename to catalogue/tests/InMemoryCatalogueTest.cpp
index 1686837930..650ea4affa 100644
--- a/catalogue/InMemoryCatalogueTest.cpp
+++ b/catalogue/tests/InMemoryCatalogueTest.cpp
@@ -16,6 +16,7 @@
  */
 
 #include "catalogue/InMemoryCatalogue.hpp"
+#include "catalogue/rdbms/RdbmsSchemaCatalogue.hpp"
 #include "common/log/DummyLogger.hpp"
 
 #include <gtest/gtest.h>
@@ -58,7 +59,7 @@ TEST_F(cta_catalogue_InMemoryCatalogue, schemaTables) {
   const uint64_t nbArchiveFileListingConns = 0;
 
   catalogue::InMemoryCatalogue inMemoryCatalogue(dummyLog, nbConns, nbArchiveFileListingConns);
-  const auto tableNameList = inMemoryCatalogue.getTableNames();
+  const auto tableNameList = static_cast<catalogue::RdbmsSchemaCatalogue*>(inMemoryCatalogue.Schema().get())->getTableNames();
   std::set<std::string> tableNameSet;
 
   std::map<std::string, uint32_t> tableNameToListPos;
diff --git a/catalogue/tests/InMemoryVersionOfCatalogueTest.cpp b/catalogue/tests/InMemoryVersionOfCatalogueTest.cpp
new file mode 100644
index 0000000000..3550827235
--- /dev/null
+++ b/catalogue/tests/InMemoryVersionOfCatalogueTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include "catalogue/InMemoryCatalogueFactory.hpp"
+#include "catalogue/tests/modules/AdminUserCatalogueTest.hpp"
+#include "catalogue/tests/modules/ArchiveFileCatalogueTest.hpp"
+#include "catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp"
+#include "catalogue/tests/modules/DiskInstanceCatalogueTest.hpp"
+#include "catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp"
+#include "catalogue/tests/modules/DiskSystemCatalogueTest.hpp"
+#include "catalogue/tests/modules/DriveConfigCatalogueTest.hpp"
+#include "catalogue/tests/modules/DriveStateCatalogueTest.hpp"
+#include "catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp"
+#include "catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp"
+#include "catalogue/tests/modules/MediaTypeCatalogueTest.hpp"
+#include "catalogue/tests/modules/MountPolicyCatalogueTest.hpp"
+#include "catalogue/tests/modules/RequesterActivityMountRuleTest.hpp"
+#include "catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp"
+#include "catalogue/tests/modules/RequesterMountRuleTest.hpp"
+#include "catalogue/tests/modules/SchemaCatalogueTest.hpp"
+#include "catalogue/tests/modules/StorageClassCatalogueTest.hpp"
+#include "catalogue/tests/modules/TapeCatalogueTest.hpp"
+#include "catalogue/tests/modules/TapeFileCatalogueTest.hpp"
+#include "catalogue/tests/modules/TapePoolCatalogueTest.hpp"
+#include "catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp"
+#ifdef STDOUT_LOGGING
+#include "common/log/StdoutLogger.hpp"
+#else
+#include "common/log/DummyLogger.hpp"
+#endif
+
+namespace unitTests {
+
+namespace {
+
+const uint64_t g_in_memory_CatalogueTest_nbConn = 1;
+const uint64_t g_in_memory_nbArchiveFileListingConns = 1;
+const uint64_t g_in_memory_maxTriesToConnect = 1;
+#ifdef STDOUT_LOGGING
+cta::log::StdoutLogger g_in_memory_CatalogueTest_dummyLogger("stdout", "stdout");
+#else
+cta::log::DummyLogger g_in_memory_CatalogueTest_dummyLogger("dummy", "dummy");
+#endif
+
+cta::catalogue::InMemoryCatalogueFactory g_inMemoryCatalogueFactory(g_in_memory_CatalogueTest_dummyLogger,
+  g_in_memory_CatalogueTest_nbConn, g_in_memory_nbArchiveFileListingConns, g_in_memory_maxTriesToConnect);
+
+cta::catalogue::CatalogueFactory *g_inMemoryCatalogueFactoryPtr = &g_inMemoryCatalogueFactory;
+
+}  // anonymous namespace
+
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_SchemaTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_AdminUserTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_DiskSystemTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_DiskInstanceTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_DiskInstanceSpaceTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_VirtualOrganizationTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_ArchiveRouteTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_MediaTypeTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_StorageClassTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_TapePoolTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_TapeTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_MountPolicyTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_RequesterActivityMountRuleTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_RequesterMountRuleTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_RequesterGroupMountRuleTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_LogicalLibraryTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_TapeFileTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_FileRecycleLogTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_DriveConfigTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_DriveStateTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+INSTANTIATE_TEST_CASE_P(InMemory, cta_catalogue_ArchiveFileTest, ::testing::Values(&g_inMemoryCatalogueFactoryPtr));
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/AdminUserCatalogueTest.cpp b/catalogue/tests/modules/AdminUserCatalogueTest.cpp
new file mode 100644
index 0000000000..ad870eb9f1
--- /dev/null
+++ b/catalogue/tests/modules/AdminUserCatalogueTest.cpp
@@ -0,0 +1,214 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/AdminUserCatalogueTest.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_AdminUserTest::cta_catalogue_AdminUserTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_localAdmin(CatalogueTestUtils::getLocalAdmin()),
+    m_admin(CatalogueTestUtils::getAdmin()) {
+}
+
+void cta_catalogue_AdminUserTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_AdminUserTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_AdminUserTest, createAdminUser) {
+  const std::string createAdminUserComment = "Create admin user";
+  m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, createAdminUserComment);
+
+  {
+    std::list<cta::common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->AdminUser()->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const auto& a = admins.front();
+
+    ASSERT_EQ(m_admin.username, a.name);
+    ASSERT_EQ(createAdminUserComment, a.comment);
+    ASSERT_EQ(m_localAdmin.username, a.creationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.creationLog.host);
+    ASSERT_EQ(m_localAdmin.username, a.lastModificationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.lastModificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_AdminUserTest, createAdminUser_same_twice) {
+  m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, "comment 1");
+
+  ASSERT_THROW(m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, "comment 2"),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, deleteAdminUser) {
+  const std::string createAdminUserComment = "Create admin user";
+  m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, createAdminUserComment);
+
+  {
+    std::list<cta::common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->AdminUser()->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const auto& a = admins.front();
+
+    ASSERT_EQ(m_admin.username, a.name);
+    ASSERT_EQ(createAdminUserComment, a.comment);
+    ASSERT_EQ(m_localAdmin.username, a.creationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.creationLog.host);
+    ASSERT_EQ(m_localAdmin.username, a.lastModificationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.lastModificationLog.host);
+  }
+
+  m_catalogue->AdminUser()->deleteAdminUser(m_admin.username);
+
+  ASSERT_TRUE(m_catalogue->AdminUser()->getAdminUsers().empty());
+}
+
+TEST_P(cta_catalogue_AdminUserTest, createAdminUser_emptyStringUsername) {
+  const std::string adminUsername = "";
+  const std::string createAdminUserComment = "Create admin user";
+  ASSERT_THROW(m_catalogue->AdminUser()->createAdminUser(m_localAdmin, adminUsername, createAdminUserComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringUsername);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, createAdminUser_emptyStringComment) {
+  const std::string createAdminUserComment = "";
+  ASSERT_THROW(m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, createAdminUserComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, deleteAdminUser_non_existent) {
+  ASSERT_THROW(m_catalogue->AdminUser()->deleteAdminUser("non_existent_admin_user"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, modifyAdminUserComment) {
+  const std::string createAdminUserComment = "Create admin user";
+  m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, createAdminUserComment);
+
+  {
+    std::list<cta::common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->AdminUser()->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const auto& a = admins.front();
+
+    ASSERT_EQ(m_admin.username, a.name);
+    ASSERT_EQ(createAdminUserComment, a.comment);
+    ASSERT_EQ(m_localAdmin.username, a.creationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.creationLog.host);
+    ASSERT_EQ(m_localAdmin.username, a.lastModificationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.lastModificationLog.host);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->AdminUser()->modifyAdminUserComment(m_localAdmin, m_admin.username, modifiedComment);
+
+  {
+    std::list<cta::common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->AdminUser()->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const auto& a = admins.front();
+
+    ASSERT_EQ(m_admin.username, a.name);
+    ASSERT_EQ(modifiedComment, a.comment);
+    ASSERT_EQ(m_localAdmin.username, a.creationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.creationLog.host);
+    ASSERT_EQ(m_localAdmin.username, a.lastModificationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.lastModificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_AdminUserTest, modifyAdminUserComment_emptyStringUsername) {
+  const std::string adminUsername = "";
+  const std::string modifiedComment = "Modified comment";
+  ASSERT_THROW(m_catalogue->AdminUser()->modifyAdminUserComment(m_localAdmin, adminUsername, modifiedComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringUsername);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, modifyAdminUserComment_emptyStringComment) {
+  const std::string createAdminUserComment = "Create admin user";
+  m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, createAdminUserComment);
+
+  {
+    std::list<cta::common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->AdminUser()->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const cta::common::dataStructures::AdminUser a = admins.front();
+
+    ASSERT_EQ(m_admin.username, a.name);
+    ASSERT_EQ(createAdminUserComment, a.comment);
+    ASSERT_EQ(m_localAdmin.username, a.creationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.creationLog.host);
+    ASSERT_EQ(m_localAdmin.username, a.lastModificationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.lastModificationLog.host);
+  }
+
+  const std::string modifiedComment = "";
+  ASSERT_THROW(m_catalogue->AdminUser()->modifyAdminUserComment(m_localAdmin, m_admin.username, modifiedComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, modifyAdminUserComment_nonExtistentAdminUser) {
+  const std::string modifiedComment = "Modified comment";
+  ASSERT_THROW(m_catalogue->AdminUser()->modifyAdminUserComment(m_localAdmin, m_admin.username, modifiedComment),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_AdminUserTest, isAdmin_false) {
+  ASSERT_FALSE(m_catalogue->AdminUser()->isAdmin(m_admin));
+}
+
+TEST_P(cta_catalogue_AdminUserTest, isAdmin_true) {
+  const std::string createAdminUserComment = "Create admin user";
+  m_catalogue->AdminUser()->createAdminUser(m_localAdmin, m_admin.username, createAdminUserComment);
+
+  {
+    std::list<cta::common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->AdminUser()->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const auto& a = admins.front();
+
+    ASSERT_EQ(m_admin.username, a.name);
+    ASSERT_EQ(createAdminUserComment, a.comment);
+    ASSERT_EQ(m_localAdmin.username, a.creationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.creationLog.host);
+    ASSERT_EQ(m_localAdmin.username, a.lastModificationLog.username);
+    ASSERT_EQ(m_localAdmin.host, a.lastModificationLog.host);
+  }
+
+  ASSERT_TRUE(m_catalogue->AdminUser()->isAdmin(m_admin));
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/AdminUserCatalogueTest.hpp b/catalogue/tests/modules/AdminUserCatalogueTest.hpp
new file mode 100644
index 0000000000..78a165fadb
--- /dev/null
+++ b/catalogue/tests/modules/AdminUserCatalogueTest.hpp
@@ -0,0 +1,46 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_AdminUserTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_AdminUserTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_localAdmin;
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/ArchiveFileCatalogueTest.cpp b/catalogue/tests/modules/ArchiveFileCatalogueTest.cpp
new file mode 100644
index 0000000000..87e75888ca
--- /dev/null
+++ b/catalogue/tests/modules/ArchiveFileCatalogueTest.cpp
@@ -0,0 +1,4897 @@
+#include <gtest/gtest.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/TapeFileSearchCriteria.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/ArchiveFileCatalogueTest.hpp"
+#include "common/dataStructures/AdminUser.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/ArchiveFileQueueCriteria.hpp"
+#include "common/dataStructures/ArchiveFileSummary.hpp"
+#include "common/dataStructures/ArchiveRoute.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+#include "common/dataStructures/RequesterGroupMountRule.hpp"
+#include "common/dataStructures/RequesterIdentity.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/TapeFile.hpp"
+#include "common/exception/FileSizeMismatch.hpp"
+#include "common/exception/TapeFseqMismatch.hpp"
+#include "common/exception/UserErrorWithCacheInfo.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/threading/Mutex.hpp"
+#include "common/threading/MutexLocker.hpp"
+#include "common/threading/Thread.hpp"
+
+namespace unitTests {
+
+cta_catalogue_ArchiveFileTest::cta_catalogue_ArchiveFileTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_tape1(CatalogueTestUtils::getTape1()),
+    m_tape2(CatalogueTestUtils::getTape2()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_storageClassDualCopy(CatalogueTestUtils::getStorageClassDualCopy()) {
+}
+
+void cta_catalogue_ArchiveFileTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_ArchiveFileTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_no_archive_routes) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName,
+    comment);
+
+  const std::list<cta::common::dataStructures::RequesterMountRule> rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+
+  const std::string diskInstance = m_diskInstance.name;
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstance, m_storageClassSingleCopy.name,
+    requesterIdentity), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_no_mount_rules) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  const std::string requesterName = "requester_name";
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstance, m_storageClassSingleCopy.name,
+    requesterIdentity), cta::exception::UserErrorWithCacheInfo);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_after_cached_and_then_deleted_requester_mount_rule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName, comment);
+
+  const std::list<cta::common::dataStructures::RequesterMountRule> rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  // Get an archive ID which should pouplate for the first time the user mount
+  // rule cache
+  m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name,
+    requesterIdentity);
+
+  // Delete the user mount rule which should immediately invalidate the user
+  // mount rule cache
+  m_catalogue->RequesterMountRule()->deleteRequesterMountRule(diskInstanceName, requesterName);
+
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  // Try to get an archive ID which should now fail because there is no user
+  // mount rule and the invalidated user mount rule cache should not hide this
+  // fact
+  ASSERT_THROW(m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), cta::exception::UserErrorWithCacheInfo);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_requester_mount_rule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName, comment);
+
+  const std::list<cta::common::dataStructures::RequesterMountRule> rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  std::set<uint64_t> archiveFileIds;
+  for(uint64_t i = 0; i<10; i++) {
+    const uint64_t archiveFileId =
+      m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name,
+        requesterIdentity);
+
+    const bool archiveFileIdIsNew = archiveFileIds.end() == archiveFileIds.find(archiveFileId);
+    ASSERT_TRUE(archiveFileIdIsNew);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_requester_group_mount_rule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+  ASSERT_EQ(requesterGroupName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = "username";
+  requesterIdentity.group = requesterGroupName;
+
+  std::set<uint64_t> archiveFileIds;
+  for(uint64_t i = 0; i<10; i++) {
+    const uint64_t archiveFileId = m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName,
+      m_storageClassSingleCopy.name, requesterIdentity);
+
+    const bool archiveFileIdIsNew = archiveFileIds.end() == archiveFileIds.find(archiveFileId);
+    ASSERT_TRUE(archiveFileIdIsNew);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_after_cached_and_then_deleted_requester_group_mount_rule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+  ASSERT_EQ(requesterGroupName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = "username";
+  requesterIdentity.group = requesterGroupName;
+
+  // Get an archive ID which should populate for the first time the group mount
+  // rule cache
+  m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name,
+    requesterIdentity);
+
+  // Delete the group mount rule which should immediately invalidate the group
+  // mount rule cache
+  m_catalogue->RequesterGroupMountRule()->deleteRequesterGroupMountRule(diskInstanceName, requesterGroupName);
+
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  // Try to get an archive ID which should now fail because there is no group
+  // mount rule and the invalidated group mount rule cache should not hide this
+  // fact
+  ASSERT_THROW(m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name,
+    requesterIdentity), cta::exception::UserErrorWithCacheInfo);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, checkAndGetNextArchiveFileId_requester_mount_rule_overide) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string requesterRuleComment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName,
+    requesterRuleComment);
+
+  const auto requesterRules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, requesterRules.size());
+
+  const cta::common::dataStructures::RequesterMountRule requesterRule = requesterRules.front();
+
+  ASSERT_EQ(requesterName, requesterRule.name);
+  ASSERT_EQ(mountPolicyName, requesterRule.mountPolicy);
+  ASSERT_EQ(requesterRuleComment, requesterRule.comment);
+  ASSERT_EQ(m_admin.username, requesterRule.creationLog.username);
+  ASSERT_EQ(m_admin.host, requesterRule.creationLog.host);
+  ASSERT_EQ(requesterRule.creationLog, requesterRule.lastModificationLog);
+
+  const std::string requesterGroupRuleComment = "Create mount rule for requester group";
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterName, requesterGroupRuleComment);
+
+  const auto requesterGroupRules =
+    m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, requesterGroupRules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule requesterGroupRule = requesterGroupRules.front();
+
+  ASSERT_EQ(requesterName, requesterGroupRule.name);
+  ASSERT_EQ(mountPolicyName, requesterGroupRule.mountPolicy);
+  ASSERT_EQ(requesterGroupRuleComment, requesterGroupRule.comment);
+  ASSERT_EQ(m_admin.username, requesterGroupRule.creationLog.username);
+  ASSERT_EQ(m_admin.host, requesterGroupRule.creationLog.host);
+  ASSERT_EQ(requesterGroupRule.creationLog, requesterGroupRule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  std::set<uint64_t> archiveFileIds;
+  for(uint64_t i = 0; i<10; i++) {
+    const uint64_t archiveFileId = m_catalogue->ArchiveFile()->checkAndGetNextArchiveFileId(diskInstanceName,
+      m_storageClassSingleCopy.name, requesterIdentity);
+
+    const bool archiveFileIdIsNew = archiveFileIds.end() == archiveFileIds.find(archiveFileId);
+    ASSERT_TRUE(archiveFileIdIsNew);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFileQueueCriteria_no_archive_routes) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName, comment);
+
+  const std::list<cta::common::dataStructures::RequesterMountRule> rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileQueueCriteria(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFileQueueCriteria_requester_mount_rule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName, comment);
+
+  const std::list<cta::common::dataStructures::RequesterMountRule> rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+  m_catalogue->ArchiveFile()->getArchiveFileQueueCriteria(diskInstanceName, m_storageClassSingleCopy.name,
+    requesterIdentity);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFileQueueCriteria_requester_group_mount_rule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+  ASSERT_EQ(requesterGroupName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = "username";
+  requesterIdentity.group = requesterGroupName;
+  m_catalogue->ArchiveFile()->getArchiveFileQueueCriteria(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFileQueueCriteria_requester_mount_rule_overide) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string requesterRuleComment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName, requesterName,
+    requesterRuleComment);
+
+  const auto requesterRules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, requesterRules.size());
+
+  const cta::common::dataStructures::RequesterMountRule requesterRule = requesterRules.front();
+
+  ASSERT_EQ(requesterName, requesterRule.name);
+  ASSERT_EQ(mountPolicyName, requesterRule.mountPolicy);
+  ASSERT_EQ(requesterRuleComment, requesterRule.comment);
+  ASSERT_EQ(m_admin.username, requesterRule.creationLog.username);
+  ASSERT_EQ(m_admin.host, requesterRule.creationLog.host);
+  ASSERT_EQ(requesterRule.creationLog, requesterRule.lastModificationLog);
+
+  const std::string requesterGroupRuleComment = "Create mount rule for requester group";
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterName, requesterGroupRuleComment);
+
+  const auto requesterGroupRules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, requesterGroupRules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule requesterGroupRule = requesterGroupRules.front();
+
+  ASSERT_EQ(requesterName, requesterGroupRule.name);
+  ASSERT_EQ(mountPolicyName, requesterGroupRule.mountPolicy);
+  ASSERT_EQ(requesterGroupRuleComment, requesterGroupRule.comment);
+  ASSERT_EQ(m_admin.username, requesterGroupRule.creationLog.username);
+  ASSERT_EQ(m_admin.host, requesterGroupRule.creationLog.host);
+  ASSERT_EQ(requesterGroupRule.creationLog, requesterGroupRule.lastModificationLog);
+
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string archiveRouteComment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, archiveRouteComment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(archiveRouteComment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+  m_catalogue->ArchiveFile()->getArchiveFileQueueCriteria(diskInstanceName, m_storageClassSingleCopy.name,
+    requesterIdentity);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFiles_non_existance_archiveFileId) {
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+
+  cta::catalogue::TapeFileSearchCriteria searchCriteria;
+  searchCriteria.archiveFileId = 1234;
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFiles_fSeq_without_vid) {
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+
+  cta::catalogue::TapeFileSearchCriteria searchCriteria;
+  searchCriteria.fSeq = 1234;
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFiles_disk_file_id_without_instance) {
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+
+  cta::catalogue::TapeFileSearchCriteria searchCriteria;
+  std::vector<std::string> diskFileIds;
+  diskFileIds.push_back("disk_file_id");
+  searchCriteria.diskFileIds = diskFileIds;
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFiles_existent_storage_class_without_disk_instance) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::list<cta::common::dataStructures::StorageClass> storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+  ASSERT_EQ(1, storageClasses.size());
+
+  {
+    const auto s = storageClasses.front();
+
+    ASSERT_EQ(m_storageClassSingleCopy.name, s.name);
+    ASSERT_EQ(m_storageClassSingleCopy.nbCopies, s.nbCopies);
+    ASSERT_EQ(m_storageClassSingleCopy.comment, s.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = s.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = s.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, getArchiveFiles_non_existent_vid) {
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+
+  cta::catalogue::TapeFileSearchCriteria searchCriteria;
+  searchCriteria.vid = "non_existent_vid";
+
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, updateDiskFileId) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  const std::string newDiskFileId = "9012";
+  m_catalogue->ArchiveFile()->updateDiskFileId(file1Written.archiveFileId, file1Written.diskInstance, newDiskFileId);
+
+  {
+    const auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(newDiskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_many_archive_files) {
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName2);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName2, pool.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(1, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  auto tape2 = m_tape2;
+  tape2.tapePoolName = tapePoolName2;
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName2);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName2, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(1, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  {
+    const auto tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const auto vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(tape1.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(tape1.vid, tape.vid);
+      ASSERT_EQ(tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(tape1.vendor, tape.vendor);
+      ASSERT_EQ(tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(tapePoolName1, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(tape2.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      const auto &tape = it->second;
+      ASSERT_EQ(tape2.vid, tape.vid);
+      ASSERT_EQ(tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(tape2.vendor, tape.vendor);
+      ASSERT_EQ(tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const auto creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const auto lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  const std::string tapeDrive = "tape_drive";
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t nbArchiveFiles = 10; // Must be a multiple of 2 for this test
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  std::set<cta::catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+    std::ostringstream diskFileId;
+    diskFileId << (12345677 + i);
+
+    // Tape copy 1 written to tape
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = i;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = i;
+    fileWritten.blockId = i * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+  }
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(2, tapes.size());
+    {
+      auto it = vidToTape.find(tape1.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape1.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+    {
+      auto it = vidToTape.find(tape2.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape2.vid, it->second.vid);
+      ASSERT_EQ(0, it->second.lastFSeq);
+    }
+  }
+
+  std::set<cta::catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy2;
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+    std::ostringstream diskFileId;
+    diskFileId << (12345677 + i);
+
+    // Tape copy 2 written to tape
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = i;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = i;
+    fileWritten.blockId = i * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy2.emplace(fileWrittenUP.release());
+  }
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy2);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName2);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName2, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(2, tapes.size());
+    {
+      auto it = vidToTape.find(tape1.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape1.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+    {
+      auto it = vidToTape.find(tape2.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape2.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 1;
+    searchCriteria.diskInstance = diskInstance;
+    std::vector<std::string> diskFileIds;
+    diskFileIds.push_back("12345678");
+    searchCriteria.diskFileIds = diskFileIds;
+    searchCriteria.vid = tape1.vid;
+
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+
+    const auto idAndFile = m.find(1);
+    ASSERT_NE(m.end(), idAndFile);
+    const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+    ASSERT_EQ(searchCriteria.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(searchCriteria.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(searchCriteria.diskFileIds->front(), archiveFile.diskFileId);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    ASSERT_EQ(searchCriteria.vid, archiveFile.tapeFiles.begin()->vid);
+  }
+
+  {
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten1;
+      fileWritten1.archiveFileId = i;
+      fileWritten1.diskInstance = diskInstance;
+      fileWritten1.diskFileId = diskFileId.str();
+
+      fileWritten1.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten1.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten1.size = archiveFileSize;
+      fileWritten1.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten1.storageClassName = m_storageClassDualCopy.name;
+      fileWritten1.vid = tape1.vid;
+      fileWritten1.fSeq = i;
+      fileWritten1.blockId = i * 100;
+      fileWritten1.copyNb = 1;
+
+      cta::catalogue::TapeFileWritten fileWritten2 = fileWritten1;
+      fileWritten2.vid = tape2.vid;
+      fileWritten2.copyNb = 2;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten1.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten1.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten1.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten1.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten1.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten1.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten1.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten1.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(m_storageClassDualCopy.nbCopies, archiveFile.tapeFiles.size());
+
+      // Tape copy 1
+      {
+        const auto it = archiveFile.tapeFiles.find(1);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten1.vid, it->vid);
+        ASSERT_EQ(fileWritten1.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten1.blockId, it->blockId);
+        ASSERT_EQ(fileWritten1.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten1.copyNb, it->copyNb);
+      }
+
+      // Tape copy 2
+      {
+        const auto it = archiveFile.tapeFiles.find(2);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten2.vid, it->vid);
+        ASSERT_EQ(fileWritten2.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten2.blockId, it->blockId);
+        ASSERT_EQ(fileWritten2.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten2.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  // Look at all files on tape 1
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.vid = tape1.vid;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten1;
+      fileWritten1.archiveFileId = i;
+      fileWritten1.diskInstance = diskInstance;
+      fileWritten1.diskFileId = diskFileId.str();
+
+      fileWritten1.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten1.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten1.size = archiveFileSize;
+      fileWritten1.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten1.storageClassName = m_storageClassDualCopy.name;
+      fileWritten1.vid = tape1.vid;
+      fileWritten1.fSeq = i;
+      fileWritten1.blockId = i * 100;
+      fileWritten1.copyNb = 1;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten1.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten1.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten1.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten1.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten1.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten1.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten1.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten1.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      // Tape copy 1
+      {
+        const auto it = archiveFile.tapeFiles.find(1);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten1.vid, it->vid);
+        ASSERT_EQ(fileWritten1.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten1.blockId, it->blockId);
+        ASSERT_EQ(fileWritten1.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten1.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  // Look at all files on tape 1
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.vid = tape1.vid;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten1;
+      fileWritten1.archiveFileId = i;
+      fileWritten1.diskInstance = diskInstance;
+      fileWritten1.diskFileId = diskFileId.str();
+
+      fileWritten1.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten1.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten1.size = archiveFileSize;
+      fileWritten1.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten1.storageClassName = m_storageClassDualCopy.name;
+      fileWritten1.vid = tape1.vid;
+      fileWritten1.fSeq = i;
+      fileWritten1.blockId = i * 100;
+      fileWritten1.copyNb = 1;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten1.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten1.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten1.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten1.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten1.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten1.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten1.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten1.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      // Tape copy 1
+      {
+        const auto it = archiveFile.tapeFiles.find(1);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten1.vid, it->vid);
+        ASSERT_EQ(fileWritten1.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten1.blockId, it->blockId);
+        ASSERT_EQ(fileWritten1.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten1.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  // Look at all files on tape 2
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.vid = tape2.vid;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten2;
+      fileWritten2.archiveFileId = i;
+      fileWritten2.diskInstance = diskInstance;
+      fileWritten2.diskFileId = diskFileId.str();
+
+      fileWritten2.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten2.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten2.size = archiveFileSize;
+      fileWritten2.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten2.storageClassName = m_storageClassDualCopy.name;
+      fileWritten2.vid = tape2.vid;
+      fileWritten2.fSeq = i;
+      fileWritten2.blockId = i * 100;
+      fileWritten2.copyNb = 2;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten2.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten2.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten2.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten2.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten2.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten2.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten2.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten2.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      // Tape copy 2
+      {
+        const auto it = archiveFile.tapeFiles.find(2);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten2.vid, it->vid);
+        ASSERT_EQ(fileWritten2.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten2.blockId, it->blockId);
+        ASSERT_EQ(fileWritten2.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten2.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  {
+    const uint64_t startFseq = 1;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesForRepackItor(tape1.vid, startFseq);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten1;
+      fileWritten1.archiveFileId = i;
+      fileWritten1.diskInstance = diskInstance;
+      fileWritten1.diskFileId = diskFileId.str();
+
+      fileWritten1.diskFileOwnerUid     = PUBLIC_DISK_USER;
+      fileWritten1.diskFileGid     = PUBLIC_DISK_GROUP;
+      fileWritten1.size = archiveFileSize;
+      fileWritten1.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten1.storageClassName = m_storageClassDualCopy.name;
+      fileWritten1.vid = tape1.vid;
+      fileWritten1.fSeq = i;
+      fileWritten1.blockId = i * 100;
+      fileWritten1.copyNb = 1;
+
+      cta::catalogue::TapeFileWritten fileWritten2 = fileWritten1;
+      fileWritten2.vid = tape2.vid;
+      fileWritten2.copyNb = 2;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten1.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten1.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten1.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten1.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten1.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten1.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten1.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten1.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(m_storageClassDualCopy.nbCopies, archiveFile.tapeFiles.size());
+
+      // Tape copy 1
+      {
+        const auto it = archiveFile.tapeFiles.find(1);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten1.vid, it->vid);
+        ASSERT_EQ(fileWritten1.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten1.blockId, it->blockId);
+        ASSERT_EQ(fileWritten1.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten1.copyNb, it->copyNb);
+      }
+
+      // Tape copy 2
+      {
+        const auto it = archiveFile.tapeFiles.find(2);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten2.vid, it->vid);
+        ASSERT_EQ(fileWritten2.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten2.blockId, it->blockId);
+        ASSERT_EQ(fileWritten2.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten2.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  for(uint32_t copyNb = 1; copyNb <= 2; copyNb++) {
+    const std::string vid = copyNb == 1 ? tape1.vid : tape2.vid;
+    const uint64_t startFseq = 1;
+    const uint64_t maxNbFiles = nbArchiveFiles;
+    const auto archiveFiles = m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFseq, maxNbFiles);
+    const auto m = CatalogueTestUtils::archiveFileListToMap(archiveFiles);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten;
+      fileWritten.archiveFileId = i;
+      fileWritten.diskInstance = diskInstance;
+      fileWritten.diskFileId = diskFileId.str();
+
+      fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten.size = archiveFileSize;
+      fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten.storageClassName = m_storageClassDualCopy.name;
+      fileWritten.vid = vid;
+      fileWritten.fSeq = i;
+      fileWritten.blockId = i * 100;
+      fileWritten.copyNb = copyNb;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten.storageClassName, archiveFile.storageClass);
+
+      // There is only one tape copy because repack only want the tape file on a
+      // single tape
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      {
+        const auto it = archiveFile.tapeFiles.find(copyNb);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten.vid, it->vid);
+        ASSERT_EQ(fileWritten.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten.blockId, it->blockId);
+        ASSERT_EQ(fileWritten.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  for(uint32_t copyNb = 1; copyNb <= 2; copyNb++) {
+    const std::string vid = copyNb == 1 ? tape1.vid : tape2.vid;
+    const uint64_t startFseq = 1;
+    const uint64_t maxNbFiles = nbArchiveFiles / 2;
+    const auto archiveFiles = m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFseq, maxNbFiles);
+    const auto m = CatalogueTestUtils::archiveFileListToMap(archiveFiles);
+    ASSERT_EQ(nbArchiveFiles / 2, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles / 2; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten;
+      fileWritten.archiveFileId = i;
+      fileWritten.diskInstance = diskInstance;
+      fileWritten.diskFileId = diskFileId.str();
+
+      fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten.size = archiveFileSize;
+      fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten.storageClassName = m_storageClassDualCopy.name;
+      fileWritten.vid = vid;
+      fileWritten.fSeq = i;
+      fileWritten.blockId = i * 100;
+      fileWritten.copyNb = copyNb;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten.storageClassName, archiveFile.storageClass);
+
+      // There is only one tape copy because repack only want the tape file on a
+      // single tape
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      {
+        const auto it = archiveFile.tapeFiles.find(copyNb);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten.vid, it->vid);
+        ASSERT_EQ(fileWritten.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten.blockId, it->blockId);
+        ASSERT_EQ(fileWritten.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  for(uint32_t copyNb = 1; copyNb <= 2; copyNb++) {
+    const std::string vid = copyNb == 1 ? tape1.vid : tape2.vid;
+    const uint64_t startFseq = nbArchiveFiles / 2 + 1;
+    const uint64_t maxNbFiles = nbArchiveFiles / 2;
+    const auto archiveFiles = m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFseq, maxNbFiles);
+    const auto m = CatalogueTestUtils::archiveFileListToMap(archiveFiles);
+    ASSERT_EQ(nbArchiveFiles / 2, m.size());
+
+    for(uint64_t i = nbArchiveFiles / 2 + 1; i <= nbArchiveFiles; i++) {
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten;
+      fileWritten.archiveFileId = i;
+      fileWritten.diskInstance = diskInstance;
+      fileWritten.diskFileId = diskFileId.str();
+
+      fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten.size = archiveFileSize;
+      fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten.storageClassName = m_storageClassDualCopy.name;
+      fileWritten.vid = vid;
+      fileWritten.fSeq = i;
+      fileWritten.blockId = i * 100;
+      fileWritten.copyNb = copyNb;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten.storageClassName, archiveFile.storageClass);
+
+      // There is only one tape copy because repack only want the tape file on a
+      // single tape
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      {
+        const auto it = archiveFile.tapeFiles.find(copyNb);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten.vid, it->vid);
+        ASSERT_EQ(fileWritten.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten.blockId, it->blockId);
+        ASSERT_EQ(fileWritten.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 10;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    ASSERT_EQ(10, m.begin()->first);
+    ASSERT_EQ(10, m.begin()->second.archiveFileID);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(m_storageClassDualCopy.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(m_storageClassDualCopy.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.diskInstance = diskInstance;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(nbArchiveFiles * m_storageClassDualCopy.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(nbArchiveFiles * m_storageClassDualCopy.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.diskInstance = diskInstance;
+    std::vector<std::string> diskFileIds;
+    diskFileIds.push_back("12345687");
+    searchCriteria.diskFileIds = diskFileIds;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    ASSERT_EQ("12345687", m.begin()->second.diskFileId);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(m_storageClassDualCopy.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(m_storageClassDualCopy.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(nbArchiveFiles * m_storageClassDualCopy.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(nbArchiveFiles * m_storageClassDualCopy.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.vid = tape1.vid;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(nbArchiveFiles, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = nbArchiveFiles + 1234;
+    ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria), cta::exception::UserError);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(0, summary.totalBytes);
+    ASSERT_EQ(0, summary.totalFiles);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->setTapeDisabled(m_admin, tape1.vid, "unit Test");
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(1, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(0, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::ACTIVE, std::nullopt,
+    std::nullopt);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->setTapeFull(m_admin, tape1.vid, true);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(1, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(0, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->setTapeFull(m_admin, tape1.vid, false);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(0, pool.nbEmptyTapes);
+    ASSERT_EQ(0, pool.nbDisabledTapes);
+    ASSERT_EQ(0, pool.nbFullTapes);
+    ASSERT_EQ(0, pool.nbReadOnlyTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+}
+
+
+TEST_P(cta_catalogue_ArchiveFileTest, DISABLED_concurrent_filesWrittenToTape_many_archive_files) {
+  std::unique_ptr<cta::catalogue::Catalogue> catalogue2;
+
+  try {
+    cta::catalogue::CatalogueFactory *const *const catalogueFactoryPtrPtr = GetParam();
+
+    if(nullptr == catalogueFactoryPtrPtr) {
+      throw cta::exception::Exception("Global pointer to the catalogue factory pointer for unit-tests in null");
+    }
+
+    if(nullptr == (*catalogueFactoryPtrPtr)) {
+      throw cta::exception::Exception("Global pointer to the catalogue factoryfor unit-tests in null");
+    }
+
+    catalogue2 = (*catalogueFactoryPtrPtr)->create();
+
+  } catch(cta::exception::Exception &ex) {
+    throw cta::exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+
+  class Barrier {
+  public:
+    Barrier(unsigned int count) : m_exited(false) {
+      pthread_barrier_init(&m_barrier, nullptr, count);
+    }
+    ~Barrier() {
+      pthread_barrier_destroy(&m_barrier);
+    }
+    void wait() {
+      pthread_barrier_wait(&m_barrier);
+    }
+    void exit() {
+      cta::threading::MutexLocker lock(m_mtx);
+      m_exited = true;
+    }
+
+    bool hasExited() {
+      cta::threading::MutexLocker lock(m_mtx);
+      return m_exited;
+    }
+
+    cta::threading::Mutex m_mtx;
+    pthread_barrier_t m_barrier;
+    bool m_exited;
+  };
+
+  class filesWrittenThread : public cta::threading::Thread {
+  public:
+    filesWrittenThread(
+        cta::catalogue::Catalogue *const cat,
+        Barrier &barrier,
+        const uint64_t nbArchiveFiles,
+        const uint64_t batchSize,
+        const cta::common::dataStructures::StorageClass &storageClass,
+        const uint64_t &archiveFileSize,
+        const cta::checksum::ChecksumBlob &checksumBlob,
+        const std::string &vid,
+        const uint64_t &copyNb,
+        const std::string &tapeDrive,
+        const std::string &diskInstance) :
+          m_cat(cat), m_barrier(barrier), m_nbArchiveFiles(nbArchiveFiles), m_batchSize(batchSize), m_storageClass(storageClass), m_archiveFileSize(archiveFileSize),
+          m_checksumBlob(checksumBlob), m_vid(vid), m_copyNb(copyNb), m_tapeDrive(tapeDrive),m_diskInstance(diskInstance) { }
+
+    void run() override {
+      for(uint64_t batch=0;batch< 1 + (m_nbArchiveFiles-1)/m_batchSize;++batch) {
+        uint64_t bs = m_nbArchiveFiles - (m_batchSize*batch);
+        if (bs> m_batchSize) {
+          bs = m_batchSize;
+        }
+        std::set<cta::catalogue::TapeItemWrittenPointer> tapeFilesWritten;
+        for(uint64_t i= 0 ; i < bs; i++) {
+          // calculate this file's archive_file_id and fseq numbers
+          const uint64_t fn_afid = 1 + m_batchSize*batch + i;
+          const uint64_t fn_seq = (m_copyNb == 1) ? fn_afid : 1 + m_batchSize*batch + (bs-i-1);
+          std::ostringstream diskFileId;
+          diskFileId << (12345677 + fn_afid);
+          std::ostringstream diskFilePath;
+          diskFilePath << "/public_dir/public_file_" << fn_afid;
+
+          // Tape this batch to tape
+          auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+          auto & fileWritten = *fileWrittenUP;
+          fileWritten.archiveFileId = fn_afid;
+          fileWritten.diskInstance = m_diskInstance;
+          fileWritten.diskFileId = diskFileId.str();
+
+          fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+          fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+          fileWritten.size = m_archiveFileSize;
+          fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+          fileWritten.storageClassName = m_storageClass.name;
+          fileWritten.vid = m_vid;
+          fileWritten.fSeq = fn_seq;
+          fileWritten.blockId = fn_seq * 100;
+          fileWritten.copyNb = m_copyNb;
+          fileWritten.tapeDrive = m_tapeDrive;
+          tapeFilesWritten.emplace(fileWrittenUP.release());
+        }
+        m_barrier.wait();
+        try {
+          m_cat->TapeFile()->filesWrittenToTape(tapeFilesWritten);
+        } catch(std::exception &) {
+          m_barrier.exit();
+          m_barrier.wait();
+          throw;
+        }
+        m_barrier.wait();
+        if (m_barrier.hasExited()) {
+          return;
+        }
+      }
+    }
+
+    cta::catalogue::Catalogue *const m_cat;
+    Barrier &m_barrier;
+    const uint64_t m_nbArchiveFiles;
+    const uint64_t m_batchSize;
+    const cta::common::dataStructures::StorageClass m_storageClass;
+    const uint64_t m_archiveFileSize;
+    const cta::checksum::ChecksumBlob m_checksumBlob;
+    const std::string m_vid;
+    const uint64_t m_copyNb;
+    const std::string m_tapeDrive;
+    const std::string m_diskInstance;
+  };
+
+  class filesWrittenRunner {
+  public:
+    filesWrittenRunner(filesWrittenThread &th) : m_th(th), m_waited(false) { m_th.start(); }
+    ~filesWrittenRunner() {
+      if (!m_waited) {
+        try {
+          m_th.wait();
+        } catch(...) {
+          // nothing
+        }
+      }
+    }
+    void wait() {
+      m_waited = true;
+      m_th.wait();
+    }
+    filesWrittenThread &m_th;
+    bool m_waited;
+  };
+
+  const std::string vid1 = "VID123";
+  const std::string vid2 = "VID456";
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName2);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName2, pool.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  auto tape2 = m_tape2;
+  tape2.tapePoolName = tapePoolName2;
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName2);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName2, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  {
+    const auto tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const auto vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(vid1);
+      ASSERT_NE(vidToTape.end(), it);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(tape1.vid, tape.vid);
+      ASSERT_EQ(tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(tape1.vendor, tape.vendor);
+      ASSERT_EQ(tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(tapePoolName1, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(vid2);
+      ASSERT_NE(vidToTape.end(), it);
+      const auto &tape = it->second;
+      ASSERT_EQ(tape2.vid, tape.vid);
+      ASSERT_EQ(tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(tape2.vendor, tape.vendor);
+      ASSERT_EQ(tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(tapePoolName2, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const auto creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const auto lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  cta::common::dataStructures::StorageClass storageClass;
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapeDrive1 = "tape_drive1";
+  const std::string tapeDrive2 = "tape_drive2";
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t nbArchiveFiles = 200; // Must be a multiple of batchsize for this test
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  const uint64_t batchsize = 20;
+
+  cta::checksum::ChecksumBlob checksumBlob;
+  checksumBlob.insert(cta::checksum::ADLER32, "9876");
+
+  {
+    Barrier barrier(2);
+    filesWrittenThread a(m_catalogue.get(), barrier, nbArchiveFiles, batchsize, storageClass, archiveFileSize, checksumBlob, vid1, 1, tapeDrive1,diskInstance);
+    filesWrittenThread b(catalogue2.get(), barrier, nbArchiveFiles, batchsize, storageClass, archiveFileSize, checksumBlob, vid2, 2, tapeDrive2,diskInstance);
+
+    filesWrittenRunner r1(a);
+    filesWrittenRunner r2(b);
+    r1.wait();
+    r2.wait();
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName1);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName1, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(2, tapes.size());
+    {
+      auto it = vidToTape.find(tape1.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape1.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+    {
+      auto it = vidToTape.find(tape2.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape2.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(2, pools.size());
+
+    const auto tapePoolMap = CatalogueTestUtils::tapePoolListToMap(pools);
+    auto tapePoolMapItor = tapePoolMap.find(tapePoolName2);
+    ASSERT_NE(tapePoolMapItor, tapePoolMap.end());
+    const auto &pool = tapePoolMapItor->second;
+
+    ASSERT_EQ(tapePoolName2, pool.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, pool.dataBytes);
+    ASSERT_EQ(nbArchiveFiles, pool.nbPhysicalFiles);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(2, tapes.size());
+    {
+      auto it = vidToTape.find(tape1.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape1.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+    {
+      auto it = vidToTape.find(tape2.vid);
+      ASSERT_NE(vidToTape.end(), it);
+      ASSERT_EQ(tape2.vid, it->second.vid);
+      ASSERT_EQ(nbArchiveFiles, it->second.lastFSeq);
+    }
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 1;
+    searchCriteria.diskInstance = diskInstance;
+    std::vector<std::string> diskFileIds;
+    diskFileIds.push_back("12345678");
+    searchCriteria.diskFileIds = diskFileIds;
+    searchCriteria.vid = tape1.vid;
+
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+
+    const auto idAndFile = m.find(1);
+    ASSERT_NE(m.end(), idAndFile);
+    const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+    ASSERT_EQ(searchCriteria.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(searchCriteria.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(searchCriteria.diskFileIds->front(), archiveFile.diskFileId);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    ASSERT_EQ(searchCriteria.vid, archiveFile.tapeFiles.begin()->vid);
+  }
+
+  auto afidToSeq = [](const uint64_t l_nbTot, const uint64_t l_batchsize, const uint64_t l_afid, uint64_t &l_seq1, uint64_t &l_seq2) {
+    l_seq1 = l_afid;
+    uint64_t batch = (l_afid-1)/l_batchsize;
+    uint64_t bidx = (l_afid-1)%l_batchsize;
+    uint64_t bs = l_nbTot - batch*l_batchsize;
+    if (bs>l_batchsize) {
+      bs = l_batchsize;
+    }
+    l_seq2 = batch*l_batchsize + (bs-bidx);
+  };
+
+  {
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    std::map<uint64_t, cta::common::dataStructures::ArchiveFile> m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      uint64_t seq1,seq2;
+      afidToSeq(nbArchiveFiles, batchsize, i, seq1, seq2);
+
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten1;
+      fileWritten1.archiveFileId = i;
+      fileWritten1.diskInstance = diskInstance;
+      fileWritten1.diskFileId = diskFileId.str();
+
+      fileWritten1.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten1.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten1.size = archiveFileSize;
+      fileWritten1.checksumBlob.insert(cta::checksum::ADLER32, "2468");
+      fileWritten1.storageClassName = storageClass.name;
+      fileWritten1.vid = tape1.vid;
+      fileWritten1.fSeq = seq1;
+      fileWritten1.blockId = seq1 * 100;
+      fileWritten1.copyNb = 1;
+
+      cta::catalogue::TapeFileWritten fileWritten2 = fileWritten1;
+      fileWritten2.vid = tape2.vid;
+      fileWritten2.fSeq = seq2;
+      fileWritten2.blockId = seq2 * 100;
+      fileWritten2.copyNb = 2;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten1.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten1.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten1.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten1.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten1.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten1.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten1.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten1.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(storageClass.nbCopies, archiveFile.tapeFiles.size());
+
+      // Tape copy 1
+      {
+        const auto it = archiveFile.tapeFiles.find(1);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten1.vid, it->vid);
+        ASSERT_EQ(fileWritten1.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten1.blockId, it->blockId);
+        ASSERT_EQ(fileWritten1.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten1.copyNb, it->copyNb);
+      }
+
+      // Tape copy 2
+      {
+        const auto it = archiveFile.tapeFiles.find(2);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten2.vid, it->vid);
+        ASSERT_EQ(fileWritten2.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten2.blockId, it->blockId);
+        ASSERT_EQ(fileWritten2.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten2.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  {
+    const uint64_t startFseq = 1;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesForRepackItor(tape1.vid, startFseq);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      uint64_t seq1,seq2;
+      afidToSeq(nbArchiveFiles, batchsize, i, seq1, seq2);
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten1;
+      fileWritten1.archiveFileId = i;
+      fileWritten1.diskInstance = diskInstance;
+      fileWritten1.diskFileId = diskFileId.str();
+
+      fileWritten1.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten1.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten1.size = archiveFileSize;
+      fileWritten1.checksumBlob.insert(cta::checksum::ADLER32, "2468");
+      fileWritten1.storageClassName = storageClass.name;
+      fileWritten1.vid = tape1.vid;
+      fileWritten1.fSeq = seq1;
+      fileWritten1.blockId = seq1 * 100;
+      fileWritten1.copyNb = 1;
+
+      cta::catalogue::TapeFileWritten fileWritten2 = fileWritten1;
+      fileWritten2.vid = tape2.vid;
+      fileWritten2.fSeq = seq2;
+      fileWritten2.blockId = seq2 * 100;
+      fileWritten2.copyNb = 2;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten1.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten1.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten1.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten1.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten1.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten1.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten1.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten1.storageClassName, archiveFile.storageClass);
+      ASSERT_EQ(storageClass.nbCopies, archiveFile.tapeFiles.size());
+
+      // Tape copy 1
+      {
+        const auto it = archiveFile.tapeFiles.find(1);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten1.vid, it->vid);
+        ASSERT_EQ(fileWritten1.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten1.blockId, it->blockId);
+        ASSERT_EQ(fileWritten1.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten1.copyNb, it->copyNb);
+      }
+
+      // Tape copy 2
+      {
+        const auto it = archiveFile.tapeFiles.find(2);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten2.vid, it->vid);
+        ASSERT_EQ(fileWritten2.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten2.blockId, it->blockId);
+        ASSERT_EQ(fileWritten2.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten2.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  for(uint64_t copyNb = 1; copyNb <= 2; copyNb++) {
+    const std::string vid = copyNb == 1 ? tape1.vid : tape2.vid;
+    const uint64_t startFseq = 1;
+    const uint64_t maxNbFiles = nbArchiveFiles;
+    const auto archiveFiles = m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFseq, maxNbFiles);
+    const auto m = CatalogueTestUtils::archiveFileListToMap(archiveFiles);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+      uint64_t seq1,seq2;
+      afidToSeq(nbArchiveFiles, batchsize, i, seq1, seq2);
+      uint64_t seq = (copyNb==1) ? seq1 : seq2;
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten;
+      fileWritten.archiveFileId = i;
+      fileWritten.diskInstance = diskInstance;
+      fileWritten.diskFileId = diskFileId.str();
+
+      fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten.size = archiveFileSize;
+      fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten.storageClassName = storageClass.name;
+      fileWritten.vid = vid;
+      fileWritten.fSeq = seq;
+      fileWritten.blockId = seq * 100;
+      fileWritten.copyNb = copyNb;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten.storageClassName, archiveFile.storageClass);
+
+      // There is only one tape copy because repack only want the tape file on a
+      // single tape
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      {
+        const auto it = archiveFile.tapeFiles.find(copyNb);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten.vid, it->vid);
+        ASSERT_EQ(fileWritten.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten.blockId, it->blockId);
+        ASSERT_EQ(fileWritten.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  for(uint64_t copyNb = 1; copyNb <= 2; copyNb++) {
+    const std::string vid = copyNb == 1 ? tape1.vid : tape2.vid;
+    const uint64_t startFseq = 1;
+    const uint64_t maxNbFiles = nbArchiveFiles / 2;
+    const auto archiveFiles = m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFseq, maxNbFiles);
+    const auto m = CatalogueTestUtils::archiveFileListToMap(archiveFiles);
+    ASSERT_EQ(nbArchiveFiles / 2, m.size());
+
+    for(uint64_t i = 1; i <= nbArchiveFiles / 2; i++) {
+      uint64_t seq1,seq2;
+      afidToSeq(nbArchiveFiles, batchsize, i, seq1, seq2);
+      uint64_t seq = (copyNb==1) ? seq1 : seq2;
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten;
+      fileWritten.archiveFileId = i;
+      fileWritten.diskInstance = diskInstance;
+      fileWritten.diskFileId = diskFileId.str();
+
+      fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten.size = archiveFileSize;
+      fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten.storageClassName = storageClass.name;
+      fileWritten.vid = vid;
+      fileWritten.fSeq = seq;
+      fileWritten.blockId = seq * 100;
+      fileWritten.copyNb = copyNb;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten.storageClassName, archiveFile.storageClass);
+
+      // There is only one tape copy because repack only want the tape file on a
+      // single tape
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      {
+        const auto it = archiveFile.tapeFiles.find(copyNb);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten.vid, it->vid);
+        ASSERT_EQ(fileWritten.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten.blockId, it->blockId);
+        ASSERT_EQ(fileWritten.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  for(uint64_t copyNb = 1; copyNb <= 2; copyNb++) {
+    const std::string vid = copyNb == 1 ? tape1.vid : tape2.vid;
+    const uint64_t startFseq = nbArchiveFiles / 2 + 1;
+    const uint64_t maxNbFiles = nbArchiveFiles / 2;
+    const auto archiveFiles = m_catalogue->ArchiveFile()->getFilesForRepack(vid, startFseq, maxNbFiles);
+    const auto m = CatalogueTestUtils::archiveFileListToMap(archiveFiles);
+    ASSERT_EQ(nbArchiveFiles / 2, m.size());
+
+    for(uint64_t i = nbArchiveFiles / 2 + 1; i <= nbArchiveFiles; i++) {
+      uint64_t seq1,seq2;
+      afidToSeq(nbArchiveFiles, batchsize, i, seq1, seq2);
+      uint64_t seq = (copyNb==1) ? seq1 : seq2;
+      std::ostringstream diskFileId;
+      diskFileId << (12345677 + i);
+      std::ostringstream diskFilePath;
+      diskFilePath << "/public_dir/public_file_" << i;
+
+      cta::catalogue::TapeFileWritten fileWritten;
+      fileWritten.archiveFileId = i;
+      fileWritten.diskInstance = diskInstance;
+      fileWritten.diskFileId = diskFileId.str();
+
+      fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+      fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+      fileWritten.size = archiveFileSize;
+      fileWritten.checksumBlob.insert(cta::checksum::ADLER32, "1357");
+      fileWritten.storageClassName = storageClass.name;
+      fileWritten.vid = vid;
+      fileWritten.fSeq = seq;
+      fileWritten.blockId = seq * 100;
+      fileWritten.copyNb = copyNb;
+
+      const auto idAndFile = m.find(i);
+      ASSERT_NE(m.end(), idAndFile);
+      const cta::common::dataStructures::ArchiveFile archiveFile = idAndFile->second;
+      ASSERT_EQ(fileWritten.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(fileWritten.diskInstance, archiveFile.diskInstance);
+      ASSERT_EQ(fileWritten.diskFileId, archiveFile.diskFileId);
+
+      ASSERT_EQ(fileWritten.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(fileWritten.diskFileGid, archiveFile.diskFileInfo.gid);
+      ASSERT_EQ(fileWritten.size, archiveFile.fileSize);
+      ASSERT_EQ(fileWritten.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(fileWritten.storageClassName, archiveFile.storageClass);
+
+      // There is only one tape copy because repack only want the tape file on a
+      // single tape
+      ASSERT_EQ(1, archiveFile.tapeFiles.size());
+
+      {
+        const auto it = archiveFile.tapeFiles.find(copyNb);
+        ASSERT_NE(archiveFile.tapeFiles.end(), it);
+        ASSERT_EQ(fileWritten.vid, it->vid);
+        ASSERT_EQ(fileWritten.fSeq, it->fSeq);
+        ASSERT_EQ(fileWritten.blockId, it->blockId);
+        ASSERT_EQ(fileWritten.checksumBlob, it->checksumBlob);
+        ASSERT_EQ(fileWritten.copyNb, it->copyNb);
+      }
+    }
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 10;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    ASSERT_EQ(10, m.begin()->first);
+    ASSERT_EQ(10, m.begin()->second.archiveFileID);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(storageClass.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(storageClass.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.diskInstance = diskInstance;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(nbArchiveFiles * storageClass.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(nbArchiveFiles * storageClass.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.diskInstance = diskInstance;
+    std::vector<std::string> diskFileIds;
+    diskFileIds.push_back("12345687");
+    searchCriteria.diskFileIds = diskFileIds;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    ASSERT_EQ("12345687", m.begin()->second.diskFileId);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(storageClass.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(storageClass.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.diskInstance = diskInstance;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    ASSERT_EQ("/public_dir/public_file_10", m.begin()->second.diskFileInfo.path);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(storageClass.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(storageClass.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(nbArchiveFiles * storageClass.nbCopies * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(nbArchiveFiles * storageClass.nbCopies, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.vid = tape1.vid;
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(nbArchiveFiles, m.size());
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(nbArchiveFiles * archiveFileSize, summary.totalBytes);
+    ASSERT_EQ(nbArchiveFiles, summary.totalFiles);
+  }
+
+  {
+    cta::catalogue::TapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = nbArchiveFiles + 1234;
+    ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria), cta::exception::UserError);
+
+    const cta::common::dataStructures::ArchiveFileSummary summary = m_catalogue->ArchiveFile()->getTapeFileSummary(searchCriteria);
+    ASSERT_EQ(0, summary.totalBytes);
+    ASSERT_EQ(0, summary.totalFiles);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_1_tape_copy) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_1_tape_copy_deleteStorageClass) {
+  const std::string diskInstance = m_diskInstance.name;
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name), cta::catalogue::UserSpecifiedStorageClassUsedByArchiveFiles);
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_file_recycle_log_deleteStorageClass) {
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  const std::string diskInstance = m_diskInstance.name;
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    ASSERT_EQ(1, tapes.size());
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    cta::common::dataStructures::DeleteArchiveRequest deletedArchiveReq;
+    deletedArchiveReq.archiveFile = archiveFile;
+    deletedArchiveReq.diskInstance = diskInstance;
+    deletedArchiveReq.archiveFileID = archiveFileId;
+    deletedArchiveReq.diskFileId = file1Written.diskFileId;
+    deletedArchiveReq.recycleTime = time(nullptr);
+    deletedArchiveReq.requester = cta::common::dataStructures::RequesterIdentity(m_admin.username,"group");
+    deletedArchiveReq.diskFilePath = "/path/";
+    m_catalogue->ArchiveFile()->moveArchiveFileToRecycleLog(deletedArchiveReq,dummyLc);
+  }
+
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name), cta::catalogue::UserSpecifiedStorageClassUsedByFileRecycleLogs);
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name), cta::exception::UserError);
+
+  {
+    //reclaim the tape to delete the files from the recycle log and delete the storage class
+    m_catalogue->Tape()->setTapeFull(m_admin,m_tape1.vid,true);
+    m_catalogue->Tape()->reclaimTape(m_admin,m_tape1.vid,dummyLc);
+  }
+
+  ASSERT_NO_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name));
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    ASSERT_EQ(2, m_catalogue->Tape()->getTapes().size());
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file2Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies_same_copy_number) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 1;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    ASSERT_EQ(2, m_catalogue->Tape()->getTapes().size());
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file2Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    const auto &tapeFile = *archiveFile.tapeFiles.begin();
+
+    ASSERT_TRUE(file1Written.vid == tapeFile.vid || file2Written.vid == tapeFile.vid);
+
+    {
+      const auto &fileWritten = file1Written.vid == tapeFile.vid ? file1Written : file2Written;
+
+      ASSERT_EQ(fileWritten.vid, tapeFile.vid);
+      ASSERT_EQ(fileWritten.fSeq, tapeFile.fSeq);
+      ASSERT_EQ(fileWritten.blockId, tapeFile.blockId);
+      ASSERT_EQ(fileWritten.checksumBlob, tapeFile.checksumBlob);
+      ASSERT_EQ(fileWritten.copyNb, tapeFile.copyNb);
+    }
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies_same_copy_number_same_tape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape1.vid;
+  file2Written.fSeq                 = 2;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 1;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    ASSERT_EQ(1, m_catalogue->Tape()->getTapes().size());
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file2Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(2, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    const auto &tapeFile = *archiveFile.tapeFiles.begin();
+
+    ASSERT_TRUE(file1Written.fSeq == tapeFile.fSeq || file2Written.fSeq == tapeFile.fSeq);
+
+    {
+      const auto &fileWritten = file1Written.fSeq == tapeFile.fSeq ? file1Written : file2Written;
+
+      ASSERT_EQ(fileWritten.vid, tapeFile.vid);
+      ASSERT_EQ(fileWritten.fSeq, tapeFile.fSeq);
+      ASSERT_EQ(fileWritten.blockId, tapeFile.blockId);
+      ASSERT_EQ(fileWritten.checksumBlob, tapeFile.checksumBlob);
+      ASSERT_EQ(fileWritten.copyNb, tapeFile.copyNb);
+    }
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies_same_fseq_same_tape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassDualCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassDualCopy.name;
+  file2Written.vid                  = m_tape1.vid;
+  file2Written.fSeq                 = file1Written.fSeq;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  ASSERT_THROW(m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet), cta::exception::TapeFseqMismatch);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies_different_sizes) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize1 = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize1;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassDualCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  const uint64_t archiveFileSize2 = 2;
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize2;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassDualCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  ASSERT_THROW(m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet), cta::catalogue::FileSizeMismatch);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies_different_checksum_types) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassDualCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob.insert(cta::checksum::CRC32, "1234");
+  file2Written.storageClassName     = m_storageClassDualCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  ASSERT_THROW(m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet), cta::exception::ChecksumTypeMismatch);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, filesWrittenToTape_1_archive_file_2_tape_copies_different_checksum_values) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog =
+        tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassDualCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob.insert(cta::checksum::ADLER32, "5678");
+  file2Written.storageClassName     = m_storageClassDualCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  ASSERT_THROW(m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet), cta::exception::ChecksumValueMismatch);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, deleteArchiveFile) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(0, tape.readMountCount);
+      ASSERT_EQ(0, tape.writeMountCount);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassDualCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    auto mItor = m.find(file1Written.archiveFileId);
+    ASSERT_NE(m.end(), mItor);
+
+    const cta::common::dataStructures::ArchiveFile archiveFile = mItor->second;
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassDualCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    ASSERT_EQ(2, m_catalogue->Tape()->getTapes().size());
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file2Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+
+    {
+      auto mItor = m.find(file1Written.archiveFileId);
+      ASSERT_NE(m.end(), mItor);
+
+      const cta::common::dataStructures::ArchiveFile archiveFile = mItor->second;
+
+      ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+      ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+      ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+      ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+      ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+      ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+      auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+      ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+      const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+      ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+      ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+      ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+      ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+      ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+      auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+      ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+      const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+      ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+      ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+      ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+      ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+      ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+    }
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(diskInstance, archiveFileId, dummyLc);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, deleteArchiveFile_by_archive_file_id_of_another_disk_instance) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(2, tapes.size());
+
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    {
+      auto it = vidToTape.find(m_tape1.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape1.vid, tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+    {
+      auto it = vidToTape.find(m_tape2.vid);
+      const cta::common::dataStructures::Tape &tape = it->second;
+      ASSERT_EQ(m_tape2.vid, tape.vid);
+      ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape2.vendor, tape.vendor);
+      ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape2.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape2.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstance;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassDualCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file1Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+    auto mItor = m.find(file1Written.archiveFileId);
+    ASSERT_NE(m.end(), mItor);
+
+    const cta::common::dataStructures::ArchiveFile archiveFile = mItor->second;
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file2Written.storageClassName     = m_storageClassDualCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    ASSERT_EQ(2, m_catalogue->Tape()->getTapes().size());
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = file2Written.vid;
+    std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const cta::common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    auto archiveFileItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    const auto m = CatalogueTestUtils::archiveFileItorToMap(archiveFileItor);
+    ASSERT_EQ(1, m.size());
+
+    {
+      auto mItor = m.find(file1Written.archiveFileId);
+      ASSERT_NE(m.end(), mItor);
+
+      const cta::common::dataStructures::ArchiveFile archiveFile = mItor->second;
+
+      ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+      ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+      ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+      ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+      ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+      ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+      ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+      ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+      ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+      auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+      ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+      const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+      ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+      ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+      ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+      ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+      ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+      auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+      ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+      const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+      ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+      ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+      ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+      ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+      ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+    }
+  }
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  ASSERT_THROW(m_catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE("another_disk_instance", archiveFileId, dummyLc), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveFileTest, deleteArchiveFile_by_archive_file_id_non_existent) {
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE("disk_instance", 12345678, dummyLc);
+}
+
+
+} // namespace unitTests
diff --git a/catalogue/tests/modules/ArchiveFileCatalogueTest.hpp b/catalogue/tests/modules/ArchiveFileCatalogueTest.hpp
new file mode 100644
index 0000000000..da6516c647
--- /dev/null
+++ b/catalogue/tests/modules/ArchiveFileCatalogueTest.hpp
@@ -0,0 +1,57 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/MediaType.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_ArchiveFileTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_ArchiveFileTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+  const cta::catalogue::CreateTapeAttributes m_tape2;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::StorageClass m_storageClassDualCopy;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/ArchiveRouteCatalogueTest.cpp b/catalogue/tests/modules/ArchiveRouteCatalogueTest.cpp
new file mode 100644
index 0000000000..31785650e6
--- /dev/null
+++ b/catalogue/tests/modules/ArchiveRouteCatalogueTest.cpp
@@ -0,0 +1,483 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp"
+#include "common/dataStructures/ArchiveRoute.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_ArchiveRouteTest::cta_catalogue_ArchiveRouteTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_tape1(CatalogueTestUtils::getTape1()),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass())  {
+}
+
+void cta_catalogue_ArchiveRouteTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_ArchiveRouteTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, comment);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes(m_storageClassSingleCopy.name, tapePoolName);
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_emptyStringStorageClassName) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+
+  cta::common::dataStructures::StorageClass storageClass;
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const std::string storageClassName = "";
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, storageClassName, copyNb,
+   tapePoolName, comment), cta::catalogue::UserSpecifiedAnEmptyStringStorageClassName);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_zeroCopyNb) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 0;
+  const std::string comment = "Create archive route";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb,
+    m_tape1.tapePoolName, comment), cta::catalogue::UserSpecifiedAZeroCopyNb);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_emptyStringTapePoolName) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "";
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, comment), cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_emptyStringComment) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb,
+    m_tape1.tapePoolName, comment), cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_non_existent_storage_class) {
+  const std::string storageClassName = "storage_class";
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, storageClassName, copyNb, m_tape1.tapePoolName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_non_existent_tape_pool) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "non_existent_tape_pool";
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_same_twice) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment);
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_two_routes_same_pool) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb1 = 1;
+  const std::string comment1 = "Create archive route for copy 1";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb1, m_tape1.tapePoolName, comment1);
+
+  const uint32_t copyNb2 = 2;
+  const std::string comment2 = "Create archive route for copy 2";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb2, m_tape1.tapePoolName, comment2), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, deleteArchiveRoute) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(m_tape1.tapePoolName, route.tapePoolName);
+  ASSERT_EQ(comment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->ArchiveRoute()->deleteArchiveRoute(m_storageClassSingleCopy.name, copyNb);
+
+  ASSERT_TRUE(m_catalogue->ArchiveRoute()->getArchiveRoutes().empty());
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, deleteArchiveRoute_non_existent) {
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->deleteArchiveRoute("non_existent_storage_class", 1234), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, createArchiveRoute_deleteStorageClass) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment);
+
+  const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const cta::common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(m_tape1.tapePoolName, route.tapePoolName);
+  ASSERT_EQ(comment, route.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name), cta::catalogue::UserSpecifiedStorageClassUsedByArchiveRoutes);
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, modifyArchiveRouteTapePoolName) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const std::string anotherTapePoolName = "another_tape_pool";
+  m_catalogue->TapePool()->createTapePool(m_admin, anotherTapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create another tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(m_tape1.tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->ArchiveRoute()->modifyArchiveRouteTapePoolName(m_admin, m_storageClassSingleCopy.name, copyNb, anotherTapePoolName);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(anotherTapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, modifyArchiveRouteTapePoolName_nonExistentTapePool) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const std::string anotherTapePoolName = "another_tape_pool";
+  m_catalogue->TapePool()->createTapePool(m_admin, anotherTapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create another tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(m_tape1.tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->modifyArchiveRouteTapePoolName(m_admin, m_storageClassSingleCopy.name, copyNb, "non_existent_tape_pool"), cta::catalogue::UserSpecifiedANonExistentTapePool);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, modifyArchiveRouteTapePoolName_nonExistentArchiveRoute) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->modifyArchiveRouteTapePoolName(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName), cta::catalogue::UserSpecifiedANonExistentArchiveRoute);
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, modifyArchiveRouteComment) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, m_tape1.tapePoolName, comment);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(m_tape1.tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->ArchiveRoute()->modifyArchiveRouteComment(m_admin, m_storageClassSingleCopy.name, copyNb, modifiedComment);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(m_tape1.tapePoolName, route.tapePoolName);
+    ASSERT_EQ(modifiedComment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_ArchiveRouteTest, modifyArchiveRouteComment_nonExistentArchiveRoute) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Comment";
+  ASSERT_THROW(m_catalogue->ArchiveRoute()->modifyArchiveRouteComment(m_admin, m_storageClassSingleCopy.name, copyNb, comment), cta::exception::UserError);
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp b/catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp
new file mode 100644
index 0000000000..c35e2adb1d
--- /dev/null
+++ b/catalogue/tests/modules/ArchiveRouteCatalogueTest.hpp
@@ -0,0 +1,53 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_ArchiveRouteTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_ArchiveRouteTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DiskInstanceCatalogueTest.cpp b/catalogue/tests/modules/DiskInstanceCatalogueTest.cpp
new file mode 100644
index 0000000000..cc3a3027ce
--- /dev/null
+++ b/catalogue/tests/modules/DiskInstanceCatalogueTest.cpp
@@ -0,0 +1,230 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/DiskInstanceCatalogueTest.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_DiskInstanceTest::cta_catalogue_DiskInstanceTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()) {
+}
+
+void cta_catalogue_DiskInstanceTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_DiskInstanceTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, getAllDiskInstances_empty) {
+  ASSERT_TRUE(m_catalogue->DiskInstance()->getAllDiskInstances().empty());
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, createDiskInstance) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment);
+
+  const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+  ASSERT_EQ(1, diskInstanceList.size());
+
+  const auto &diskInstance = diskInstanceList.front();
+  ASSERT_EQ(diskInstance.name, name);
+  ASSERT_EQ(diskInstance.comment, comment);
+
+  const auto creationLog = diskInstance.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const auto lastModificationLog = diskInstance.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, createDiskInstance_twice) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment);
+
+  const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+  ASSERT_EQ(1, diskInstanceList.size());
+
+  const auto &diskInstance = diskInstanceList.front();
+  ASSERT_EQ(diskInstance.name, name);
+  ASSERT_EQ(diskInstance.comment, comment);
+
+  const auto creationLog = diskInstance.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const auto lastModificationLog = diskInstance.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  ASSERT_THROW(m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, createDiskInstance_emptyName) {
+  const std::string comment = "disk_instance_comment";
+  ASSERT_THROW(m_catalogue->DiskInstance()->createDiskInstance(m_admin, "", comment),
+    cta::catalogue::UserSpecifiedAnEmptyStringDiskInstanceName);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, createDiskInstance_emptyComment) {
+  const std::string name = "disk_instance_name";
+  ASSERT_THROW(m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, ""),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, deleteDiskInstance) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment);
+
+  const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+  ASSERT_EQ(1, diskInstanceList.size());
+
+  const auto &diskInstance = diskInstanceList.front();
+  ASSERT_EQ(diskInstance.name, name);
+  ASSERT_EQ(diskInstance.comment, comment);
+
+  const auto creationLog = diskInstance.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const auto lastModificationLog = diskInstance.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->DiskInstance()->deleteDiskInstance(name);
+  ASSERT_TRUE(m_catalogue->DiskInstance()->getAllDiskInstances().empty());
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, deleteDiskInstance_nonExisting) {
+  const std::string name = "disk_instance_name";
+  ASSERT_THROW(m_catalogue->DiskInstance()->deleteDiskInstance(name), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, modifyDiskInstanceComment) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment);
+  {
+    const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+    ASSERT_EQ(1, diskInstanceList.size());
+
+    const auto &diskInstance = diskInstanceList.front();
+    ASSERT_EQ(diskInstance.name, name);
+    ASSERT_EQ(diskInstance.comment, comment);
+
+    const auto creationLog = diskInstance.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstance.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "modified_disk_instance_comment";
+  m_catalogue->DiskInstance()->modifyDiskInstanceComment(m_admin, name, modifiedComment);
+
+  {
+    const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+    ASSERT_EQ(1, diskInstanceList.size());
+
+    const auto &diskInstance = diskInstanceList.front();
+    ASSERT_EQ(diskInstance.name, name);
+    ASSERT_EQ(diskInstance.comment, modifiedComment);
+
+    const auto creationLog = diskInstance.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, modifyDiskInstanceComment_emptyName) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment);
+  {
+    const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+    ASSERT_EQ(1, diskInstanceList.size());
+
+    const auto &diskInstance = diskInstanceList.front();
+    ASSERT_EQ(diskInstance.name, name);
+    ASSERT_EQ(diskInstance.comment, comment);
+
+    const auto creationLog = diskInstance.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstance.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_THROW(m_catalogue->DiskInstance()->modifyDiskInstanceComment(m_admin, "", comment),
+    cta::catalogue::UserSpecifiedAnEmptyStringDiskInstanceName);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, modifyDiskInstanceComment_emptyComment) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, name, comment);
+  {
+    const auto diskInstanceList = m_catalogue->DiskInstance()->getAllDiskInstances();
+    ASSERT_EQ(1, diskInstanceList.size());
+
+    const auto &diskInstance = diskInstanceList.front();
+    ASSERT_EQ(diskInstance.name, name);
+    ASSERT_EQ(diskInstance.comment, comment);
+
+    const auto creationLog = diskInstance.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstance.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_THROW(m_catalogue->DiskInstance()->modifyDiskInstanceComment(m_admin, name, ""),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_DiskInstanceTest, modifyDiskInstanceComment_nonExisting) {
+  const std::string name = "disk_instance_name";
+  const std::string comment = "disk_instance_comment";
+
+  ASSERT_THROW(m_catalogue->DiskInstance()->modifyDiskInstanceComment(m_admin, name, comment),
+    cta::catalogue::UserSpecifiedANonExistentDiskInstance);
+}
+
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/DiskInstanceCatalogueTest.hpp b/catalogue/tests/modules/DiskInstanceCatalogueTest.hpp
new file mode 100644
index 0000000000..3119f05039
--- /dev/null
+++ b/catalogue/tests/modules/DiskInstanceCatalogueTest.hpp
@@ -0,0 +1,45 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DiskInstanceTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DiskInstanceTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.cpp b/catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.cpp
new file mode 100644
index 0000000000..40f5ac0b2a
--- /dev/null
+++ b/catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.cpp
@@ -0,0 +1,620 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/DiskInstanceSpace.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_DiskInstanceSpaceTest::cta_catalogue_DiskInstanceSpaceTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()) {
+}
+
+void cta_catalogue_DiskInstanceSpaceTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_DiskInstanceSpaceTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+  ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+  const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+  ASSERT_EQ(diskInstanceSpace.name, name);
+  ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+  ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+  ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+  ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+  ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+  ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+  const auto creationLog = diskInstanceSpace.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace_twice) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+  ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+  const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+  ASSERT_EQ(diskInstanceSpace.name, name);
+  ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+  ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+  ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+  ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+  ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+  ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+  const auto creationLog = diskInstanceSpace.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace_nonExistantDiskInstance) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace_emptyName) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, "", diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment), cta::catalogue::UserSpecifiedAnEmptyStringDiskInstanceSpaceName);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace_emptyComment) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, ""), cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace_emptyFreeSpaceQueryURL) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, "",
+    refreshInterval, comment), cta::catalogue::UserSpecifiedAnEmptyStringFreeSpaceQueryURL);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, createDiskInstanceSpace_zeroRefreshInterval) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const std::string comment = "disk_instance_space_comment";
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    0, comment), cta::catalogue::UserSpecifiedAZeroRefreshInterval);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceComment) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+
+  const std::string newDiskInstanceSpaceComment = "disk_instance_comment_2";
+  m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceComment(m_admin, name, diskInstance,
+    newDiskInstanceSpaceComment);
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, newDiskInstanceSpaceComment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceComment_empty) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceComment(m_admin, name, diskInstance, ""),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceComment_nonExistingSpace) {
+  const std::string name = "disk_instance_space_name";
+  const std::string diskInstance = "disk_instance_name";
+  const std::string comment = "disk_instance_space_comment";
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceComment(m_admin, name, diskInstance, comment),
+    cta::catalogue::UserSpecifiedANonExistentDiskInstanceSpace);
+}
+
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceQueryURL) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+
+  const std::string newFreeSpaceQueryURL = "new_free_space_query_URL";
+  m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceQueryURL(m_admin, name, diskInstance, newFreeSpaceQueryURL);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, newFreeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceQueryURL_empty) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceQueryURL(m_admin, name, diskInstance, ""),
+    cta::catalogue::UserSpecifiedAnEmptyStringFreeSpaceQueryURL);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceQueryURL_nonExistingSpace) {
+  const std::string name = "disk_instance_space_name";
+  const std::string diskInstance = "disk_instance_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceQueryURL(m_admin, name, diskInstance,
+    freeSpaceQueryURL), cta::catalogue::UserSpecifiedANonExistentDiskInstanceSpace);
+}
+
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceRefreshInterval) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+
+  const uint64_t newRefreshInterval = 35;
+  m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceRefreshInterval(m_admin, name, diskInstance,
+    newRefreshInterval);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, newRefreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceRefreshInterval_zeroInterval) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceRefreshInterval(m_admin, name, diskInstance, 0),
+    cta::catalogue::UserSpecifiedAZeroRefreshInterval);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceRefreshInterval_nonExistingSpace) {
+  const std::string name = "disk_instance_space_name";
+  const std::string diskInstance = "disk_instance_name";
+  const uint64_t refreshInterval = 32;
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceRefreshInterval(m_admin, name, diskInstance,
+    refreshInterval), cta::catalogue::UserSpecifiedANonExistentDiskInstanceSpace);
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, modifyDiskInstanceSpaceFreeSpace) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+
+  }
+
+  const uint64_t newFreeSpace = 300;
+  m_catalogue->DiskInstanceSpace()->modifyDiskInstanceSpaceFreeSpace(name, diskInstance, newFreeSpace);
+
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_NE(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, newFreeSpace);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, deleteDiskInstanceSpace) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string diskInstanceComment = "disk_instance_comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, diskInstanceComment);
+
+  const std::string name = "disk_instance_space_name";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const std::string comment = "disk_instance_space_comment";
+
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, name, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+  {
+    const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+    ASSERT_EQ(1, diskInstanceSpaceList.size());
+
+    const auto &diskInstanceSpace = diskInstanceSpaceList.front();
+    ASSERT_EQ(diskInstanceSpace.name, name);
+    ASSERT_EQ(diskInstanceSpace.diskInstance, diskInstance);
+    ASSERT_EQ(diskInstanceSpace.freeSpaceQueryURL, freeSpaceQueryURL);
+    ASSERT_EQ(diskInstanceSpace.refreshInterval, refreshInterval);
+    ASSERT_EQ(diskInstanceSpace.lastRefreshTime, 0);
+    ASSERT_EQ(diskInstanceSpace.freeSpace, 0);
+
+    ASSERT_EQ(diskInstanceSpace.comment, comment);
+
+    const auto creationLog = diskInstanceSpace.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskInstanceSpace.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  m_catalogue->DiskInstanceSpace()->deleteDiskInstanceSpace(name, diskInstance);
+  const auto diskInstanceSpaceList = m_catalogue->DiskInstanceSpace()->getAllDiskInstanceSpaces();
+  ASSERT_EQ(0, diskInstanceSpaceList.size());
+}
+
+TEST_P(cta_catalogue_DiskInstanceSpaceTest, deleteDiskInstanceSpace_notExisting) {
+  const std::string diskInstance = "disk_instance_name";
+  const std::string name = "disk_instance_space_name";
+
+  ASSERT_THROW(m_catalogue->DiskInstanceSpace()->deleteDiskInstanceSpace(name, diskInstance),
+    cta::catalogue::UserSpecifiedANonExistentDiskInstanceSpace);
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp b/catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp
new file mode 100644
index 0000000000..c9882342b9
--- /dev/null
+++ b/catalogue/tests/modules/DiskInstanceSpaceCatalogueTest.hpp
@@ -0,0 +1,45 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DiskInstanceSpaceTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DiskInstanceSpaceTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DiskSystemCatalogueTest.cpp b/catalogue/tests/modules/DiskSystemCatalogueTest.cpp
new file mode 100644
index 0000000000..e673ab83a4
--- /dev/null
+++ b/catalogue/tests/modules/DiskSystemCatalogueTest.cpp
@@ -0,0 +1,714 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/DiskSystemCatalogueTest.hpp"
+#include "common/log/LogContext.hpp"
+#include "disk/DiskSystem.hpp"
+
+namespace unitTests {
+
+cta_catalogue_DiskSystemTest::cta_catalogue_DiskSystemTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()) {
+}
+
+void cta_catalogue_DiskSystemTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_DiskSystemTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, getAllDiskSystems_no_systems) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, getAllDiskSystems_many_diskSystems) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+
+  const uint32_t nbDiskSystems = 16;
+
+  std::string diskInstanceName = "DiskInstanceName";
+  std::string diskInstanceComment = "Comment";
+  std::string diskInstanceSpaceName = "DiskInstanceSpace";
+  std::string diskInstanceSpaceComment = "Comment";
+
+   // create disk instance
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstanceName, diskInstanceComment);
+  // create disk instance space
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpaceName, diskInstanceName,
+    freeSpaceQueryURL, refreshInterval, diskInstanceSpaceComment);
+
+
+  for(uint32_t i = 0; i < nbDiskSystems; i++) {
+    std::ostringstream name;
+    name << "DiskSystem" << std::setfill('0') << std::setw(5) << i;
+    const std::string diskSystemComment = "Create disk system " + name.str();
+        m_catalogue->DiskSystem()->createDiskSystem(m_admin, name.str(), diskInstanceName, diskInstanceSpaceName,
+      fileRegexp, targetedFreeSpace + i, sleepTime + i, diskSystemComment);
+
+  }
+
+  auto diskSystemsList = m_catalogue->DiskSystem()->getAllDiskSystems();
+  ASSERT_EQ(nbDiskSystems, diskSystemsList.size());
+
+  for(size_t i = 0; i < nbDiskSystems; i++) {
+    std::ostringstream name;
+    name << "DiskSystem" << std::setfill('0') << std::setw(5) << i;
+    const std::string diskSystemComment = "Create disk system " + name.str();
+    ASSERT_NO_THROW(diskSystemsList.at(name.str()));
+    const auto diskSystem = diskSystemsList.at(name.str());
+
+    ASSERT_EQ(name.str(), diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval );
+
+    ASSERT_EQ(targetedFreeSpace + i, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(sleepTime + i, diskSystem.sleepTime);
+    ASSERT_EQ(diskSystemComment, diskSystem.comment);
+  }
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, diskSystemExists_emptyString) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "";
+
+  ASSERT_THROW(m_catalogue->DiskSystem()->diskSystemExists(name), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_emptyStringDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "Create disk system";
+
+  ASSERT_THROW(m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace,
+    fileRegexp, targetedFreeSpace, sleepTime, comment), cta::catalogue::UserSpecifiedAnEmptyStringDiskSystemName);
+
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_emptyStringFileRegexp) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "";
+  const std::string freeSpaceQueryURL = "free_space_query_URL";
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "Create disk system";
+
+  ASSERT_THROW(m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace,
+    fileRegexp, targetedFreeSpace, sleepTime, comment), cta::catalogue::UserSpecifiedAnEmptyStringFileRegexp);
+
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_zeroTargetedFreeSpace) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t targetedFreeSpace = 0;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "Create disk system";
+
+  ASSERT_THROW(m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace,
+    fileRegexp, targetedFreeSpace, sleepTime, comment), cta::catalogue::UserSpecifiedAZeroTargetedFreeSpace);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_emptyStringComment) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "";
+
+  ASSERT_THROW(m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace,
+    fileRegexp, targetedFreeSpace, sleepTime, comment), cta::catalogue::UserSpecifiedAnEmptyStringComment);
+
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_9_exabytes_targetedFreeSpace) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 9L * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+
+  ASSERT_EQ(1, diskSystemList.size());
+
+  {
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(diskInstance, diskSystem.diskInstanceSpace.diskInstance);
+    ASSERT_EQ(diskInstanceSpace, diskSystem.diskInstanceSpace.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(sleepTime, diskSystem.sleepTime);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_sleepTimeHandling) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 0;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+  ASSERT_THROW(m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment), cta::catalogue::UserSpecifiedAZeroSleepTime);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, std::numeric_limits<int64_t>::max(), comment);
+
+  const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+
+  ASSERT_EQ(1, diskSystemList.size());
+
+  {
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(std::numeric_limits<int64_t>::max(), diskSystem.sleepTime);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+}
+
+
+TEST_P(cta_catalogue_DiskSystemTest, createDiskSystem_same_twice) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+    const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+
+  ASSERT_EQ(1, diskSystemList.size());
+  ASSERT_THROW(m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, deleteDiskSystem) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+
+  ASSERT_EQ(1, diskSystemList.size());
+
+  const auto &diskSystem = diskSystemList.front();
+  ASSERT_EQ(name, diskSystem.name);
+  ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+  ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+  ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+  ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+  ASSERT_EQ(comment, diskSystem.comment);
+
+  const auto creationLog = diskSystem.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const auto lastModificationLog = diskSystem.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->DiskSystem()->deleteDiskSystem(diskSystem.name);
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, deleteDiskSystem_non_existent) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+  ASSERT_THROW(m_catalogue->DiskSystem()->deleteDiskSystem("non_existent_disk_system"),
+    cta::catalogue::UserSpecifiedANonExistentDiskSystem);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemFileRegexp) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance, freeSpaceQueryURL,
+    refreshInterval, comment);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedFileRegexp = "modified_fileRegexp";
+  m_catalogue->DiskSystem()->modifyDiskSystemFileRegexp(m_admin, name, modifiedFileRegexp);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(modifiedFileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemFileRegexp_emptyStringDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string diskSystemName = "";
+  const std::string modifiedFileRegexp = "modified_fileRegexp";
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemFileRegexp(m_admin, diskSystemName, modifiedFileRegexp),
+    cta::catalogue::UserSpecifiedAnEmptyStringDiskSystemName);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemFileRegexp_nonExistentDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string diskSystemName = "dummyDiskSystemName";
+  const std::string modifiedFileRegexp = "modified_fileRegexp";
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemFileRegexp(m_admin, diskSystemName, modifiedFileRegexp),
+    cta::catalogue::UserSpecifiedANonExistentDiskSystem);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemFileRegexp_emptyStringFileRegexp) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance,
+    freeSpaceQueryURL, refreshInterval, comment);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedFileRegexp = "";
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemFileRegexp(m_admin, name, modifiedFileRegexp),
+    cta::catalogue::UserSpecifiedAnEmptyStringFileRegexp);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemTargetedFreeSpace) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance,
+    freeSpaceQueryURL, refreshInterval, comment);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedTargetedFreeSpace = 128;
+  m_catalogue->DiskSystem()->modifyDiskSystemTargetedFreeSpace(m_admin, name, modifiedTargetedFreeSpace);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(modifiedTargetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemTargetedFreeSpace_emptyStringDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string diskSystemName = "";
+  const uint64_t modifiedTargetedFreeSpace = 128;
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemTargetedFreeSpace(m_admin, diskSystemName,
+    modifiedTargetedFreeSpace), cta::catalogue::UserSpecifiedAnEmptyStringDiskSystemName);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemTargetedFreeSpace_nonExistentDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string diskSystemName = "dummyDiskSystemName";
+  const uint64_t modifiedTargetedFreeSpace = 128;
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemTargetedFreeSpace(m_admin, diskSystemName,
+    modifiedTargetedFreeSpace), cta::catalogue::UserSpecifiedANonExistentDiskSystem);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemTargetedFreeSpace_zeroTargetedFreeSpace) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance,
+    freeSpaceQueryURL, refreshInterval, comment);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedTargetedFreeSpace = 0;
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemTargetedFreeSpace(m_admin, name, modifiedTargetedFreeSpace),
+    cta::catalogue::UserSpecifiedAZeroTargetedFreeSpace);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemComment) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance,
+    freeSpaceQueryURL, refreshInterval, comment);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "modified_comment";
+  m_catalogue->DiskSystem()->modifyDiskSystemComment(m_admin, name, modifiedComment);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(modifiedComment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemComment_emptyStringDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string diskSystemName = "";
+  const std::string modifiedComment = "modified_comment";
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemComment(m_admin, diskSystemName, modifiedComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringDiskSystemName);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemComment_nonExistentDiskSystemName) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string diskSystemName = "dummyDiskSystemName";
+  const std::string modifiedComment = "modified_comment";
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemComment(m_admin, diskSystemName, modifiedComment),
+    cta::catalogue::UserSpecifiedANonExistentDiskSystem);
+}
+
+TEST_P(cta_catalogue_DiskSystemTest, modifyDiskSystemCommentL_emptyStringComment) {
+  ASSERT_TRUE(m_catalogue->DiskSystem()->getAllDiskSystems().empty());
+
+  const std::string name = "disk_system_name";
+  const std::string diskInstance = "disk_instance";
+  const std::string diskInstanceSpace = "disk_instance_space";
+  const std::string fileRegexp = "file_regexp";
+  const std::string freeSpaceQueryURL = "free_space_query_url";
+  const uint64_t refreshInterval = 32;
+  const uint64_t targetedFreeSpace = 64;
+  const uint64_t sleepTime = 15*60;
+  const std::string comment = "disk system comment";
+
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstance, comment);
+  m_catalogue->DiskInstanceSpace()->createDiskInstanceSpace(m_admin, diskInstanceSpace, diskInstance,
+    freeSpaceQueryURL, refreshInterval, comment);
+
+  m_catalogue->DiskSystem()->createDiskSystem(m_admin, name, diskInstance, diskInstanceSpace, fileRegexp,
+    targetedFreeSpace, sleepTime, comment);
+
+  {
+    const auto diskSystemList = m_catalogue->DiskSystem()->getAllDiskSystems();
+    ASSERT_EQ(1, diskSystemList.size());
+
+    const auto &diskSystem = diskSystemList.front();
+    ASSERT_EQ(name, diskSystem.name);
+    ASSERT_EQ(fileRegexp, diskSystem.fileRegexp);
+    ASSERT_EQ(freeSpaceQueryURL, diskSystem.diskInstanceSpace.freeSpaceQueryURL);
+    ASSERT_EQ(refreshInterval, diskSystem.diskInstanceSpace.refreshInterval);
+    ASSERT_EQ(targetedFreeSpace, diskSystem.targetedFreeSpace);
+    ASSERT_EQ(comment, diskSystem.comment);
+
+    const auto creationLog = diskSystem.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = diskSystem.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "";
+  ASSERT_THROW(m_catalogue->DiskSystem()->modifyDiskSystemComment(m_admin, name, modifiedComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/DiskSystemCatalogueTest.hpp b/catalogue/tests/modules/DiskSystemCatalogueTest.hpp
new file mode 100644
index 0000000000..e9939918b4
--- /dev/null
+++ b/catalogue/tests/modules/DiskSystemCatalogueTest.hpp
@@ -0,0 +1,45 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DiskSystemTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DiskSystemTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DriveConfigCatalogueTest.cpp b/catalogue/tests/modules/DriveConfigCatalogueTest.cpp
new file mode 100644
index 0000000000..5f2e4090e6
--- /dev/null
+++ b/catalogue/tests/modules/DriveConfigCatalogueTest.cpp
@@ -0,0 +1,266 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/DriveConfigCatalogueTest.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/SourcedParameter.hpp"
+
+namespace unitTests {
+
+cta_catalogue_DriveConfigTest::cta_catalogue_DriveConfigTest()
+  : m_dummyLog("dummy", "dummy") {
+}
+
+void cta_catalogue_DriveConfigTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_DriveConfigTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, getTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig.value();
+  ASSERT_EQ(daemonUserName.category(), category);
+  ASSERT_EQ(daemonUserName.value(), value);
+  ASSERT_EQ(daemonUserName.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, getAllDrivesConfigs) {
+  std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> tapeDriveConfigs;
+  // Create 100 tape drives
+  for (size_t i = 0; i < 100; i++) {
+    std::stringstream ss;
+    ss << "VDSTK" << std::setw(5) << std::setfill('0') << i;
+
+    cta::SourcedParameter<std::string> daemonUserName {
+      "taped", "DaemonUserName", "cta", "Compile time default"};
+    m_catalogue->DriveConfig()->createTapeDriveConfig(ss.str(), daemonUserName.category(), daemonUserName.key(),
+      daemonUserName.value(), daemonUserName.source());
+    tapeDriveConfigs.push_back({ss.str(), daemonUserName.category(), daemonUserName.key(), daemonUserName.value(),
+      daemonUserName.source()});
+    cta::SourcedParameter<std::string> defaultConfig {
+      "taped", "defaultConfig", "cta", "Random Default Config for Testing"};
+    m_catalogue->DriveConfig()->createTapeDriveConfig(ss.str(), defaultConfig.category(), defaultConfig.key(),
+      defaultConfig.value(), defaultConfig.source());
+    tapeDriveConfigs.push_back({ss.str(), defaultConfig.category(), defaultConfig.key(), defaultConfig.value(),
+      defaultConfig.source()});
+  }
+  const auto drivesConfigs = m_catalogue->DriveConfig()->getTapeDriveConfigs();
+  ASSERT_EQ(tapeDriveConfigs.size(), drivesConfigs.size());
+  for (const auto& dc : drivesConfigs) {
+    m_catalogue->DriveConfig()->deleteTapeDriveConfig(dc.tapeDriveName, dc.keyName);
+  }
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, setSourcedParameterWithEmptyValue) {
+  const std::string tapeDriveName = "VDSTK11";
+
+  cta::SourcedParameter<std::string> raoLtoOptions {
+    "taped", "RAOLTOAlgorithmOptions", "", "Compile time default"
+  };
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, raoLtoOptions.category(), raoLtoOptions.key(),
+    raoLtoOptions.value(), raoLtoOptions.source());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, raoLtoOptions.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig.value();
+  ASSERT_EQ(raoLtoOptions.category(), category);
+  ASSERT_EQ("", value);
+  ASSERT_EQ(raoLtoOptions.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, raoLtoOptions.key());
+
+  cta::SourcedParameter<std::string> backendPath{
+    "ObjectStore", "BackendPath"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, backendPath.category(), backendPath.key(),
+    backendPath.value(), backendPath.source());
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, backendPath.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  std::tie(category, value, source) = driveConfig.value();
+  ASSERT_EQ(backendPath.category(), category);
+  ASSERT_EQ("", value);
+  ASSERT_EQ("", source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, backendPath.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, failTogetTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const std::string wrongKey = "wrongKey";
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(wrongName, daemonUserName.key());
+  ASSERT_FALSE(driveConfig);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, wrongKey);
+  ASSERT_FALSE(driveConfig);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(wrongName, wrongKey);
+  ASSERT_FALSE(driveConfig);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, failTodeleteTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const std::string wrongKey = "wrongKey";
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(wrongName, daemonUserName.key());
+  auto driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, wrongKey);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(wrongName, wrongKey);
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig));
+  // Good deletion
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  driveConfig = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName.key());
+  ASSERT_FALSE(driveConfig);
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, multipleDriveConfig) {
+  const std::string tapeDriveName1 = "VDSTK11";
+  const std::string tapeDriveName2 = "VDSTK12";
+
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+  cta::SourcedParameter<std::string> daemonGroupName {
+    "taped", "DaemonGroupName", "tape", "Compile time default"};
+
+  // Combinations of tapeDriveName1/2 and daemonUserName and daemonGroupName
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+  auto driveConfig1UserName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName1, daemonUserName.key());
+  auto driveConfig2UserName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName2, daemonUserName.key());
+  auto driveConfig1GroupName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName1, daemonGroupName.key());
+  auto driveConfig2GroupName = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName2, daemonGroupName.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig1UserName));
+  ASSERT_TRUE(static_cast<bool>(driveConfig2UserName));
+  ASSERT_TRUE(static_cast<bool>(driveConfig1GroupName));
+  ASSERT_TRUE(static_cast<bool>(driveConfig2GroupName));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig1UserName.value();
+  ASSERT_EQ(daemonUserName.category(), category);
+  ASSERT_EQ(daemonUserName.value(), value);
+  ASSERT_EQ(daemonUserName.source(), source);
+  std::tie(category, value, source) = driveConfig2UserName.value();
+  ASSERT_EQ(daemonUserName.category(), category);
+  ASSERT_EQ(daemonUserName.value(), value);
+  ASSERT_EQ(daemonUserName.source(), source);
+  std::tie(category, value, source) = driveConfig1GroupName.value();
+  ASSERT_EQ(daemonGroupName.category(), category);
+  ASSERT_EQ(daemonGroupName.value(), value);
+  ASSERT_EQ(daemonGroupName.source(), source);
+  std::tie(category, value, source) = driveConfig2GroupName.value();
+  ASSERT_EQ(daemonGroupName.category(), category);
+  ASSERT_EQ(daemonGroupName.value(), value);
+  ASSERT_EQ(daemonGroupName.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName1, daemonUserName.key());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName1, daemonGroupName.key());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName2, daemonUserName.key());
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName2, daemonGroupName.key());
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, getNamesAndKeysOfMultipleDriveConfig) {
+  const std::string tapeDriveName1 = "VDSTK11";
+  const std::string tapeDriveName2 = "VDSTK12";
+
+  cta::SourcedParameter<std::string> daemonUserName {
+    "taped", "DaemonUserName", "cta", "Compile time default"};
+  cta::SourcedParameter<std::string> daemonGroupName {
+    "taped", "DaemonGroupName", "tape", "Compile time default"};
+
+  // Combinations of tapeDriveName1/2 and daemonUserName and daemonGroupName
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName1, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonUserName.category(), daemonUserName.key(),
+    daemonUserName.value(), daemonUserName.source());
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName2, daemonGroupName.category(), daemonGroupName.key(),
+    daemonGroupName.value(), daemonGroupName.source());
+
+  const auto configurationTapeNamesAndKeys = m_catalogue->DriveConfig()->getTapeDriveConfigNamesAndKeys();
+
+  for (const auto& nameAndKey : configurationTapeNamesAndKeys) {
+    m_catalogue->DriveConfig()->deleteTapeDriveConfig(nameAndKey.first, nameAndKey.second);
+  }
+}
+
+TEST_P(cta_catalogue_DriveConfigTest, modifyTapeDriveConfig) {
+  const std::string tapeDriveName = "VDSTK11";
+  // Both share same key
+  cta::SourcedParameter<std::string> daemonUserName1 {
+    "taped1", "DaemonUserName", "cta1", "Compile time1 default"};
+  cta::SourcedParameter<std::string> daemonUserName2 {
+    "taped2", "DaemonUserName", "cta2", "Compile time2 default"};
+
+  m_catalogue->DriveConfig()->createTapeDriveConfig(tapeDriveName, daemonUserName1.category(), daemonUserName1.key(),
+    daemonUserName1.value(), daemonUserName1.source());
+  const auto driveConfig1 = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName1.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig1));
+  std::string category, value, source;
+  std::tie(category, value, source) = driveConfig1.value();
+  ASSERT_NE(daemonUserName2.category(), category);
+  ASSERT_NE(daemonUserName2.value(), value);
+  ASSERT_NE(daemonUserName2.source(), source);
+  m_catalogue->DriveConfig()->modifyTapeDriveConfig(tapeDriveName, daemonUserName2.category(), daemonUserName2.key(),
+    daemonUserName2.value(), daemonUserName2.source());
+  const auto driveConfig2 = m_catalogue->DriveConfig()->getTapeDriveConfig(tapeDriveName, daemonUserName1.key());
+  ASSERT_TRUE(static_cast<bool>(driveConfig2));
+  std::tie(category, value, source) = driveConfig2.value();
+  ASSERT_EQ(daemonUserName2.category(), category);
+  ASSERT_EQ(daemonUserName2.value(), value);
+  ASSERT_EQ(daemonUserName2.source(), source);
+  m_catalogue->DriveConfig()->deleteTapeDriveConfig(tapeDriveName, daemonUserName1.key());
+}
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DriveConfigCatalogueTest.hpp b/catalogue/tests/modules/DriveConfigCatalogueTest.hpp
new file mode 100644
index 0000000000..c03102f6c9
--- /dev/null
+++ b/catalogue/tests/modules/DriveConfigCatalogueTest.hpp
@@ -0,0 +1,42 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DriveConfigTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DriveConfigTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DriveStateCatalogueTest.cpp b/catalogue/tests/modules/DriveStateCatalogueTest.cpp
new file mode 100644
index 0000000000..2d1343aa25
--- /dev/null
+++ b/catalogue/tests/modules/DriveStateCatalogueTest.cpp
@@ -0,0 +1,1526 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/TapeDrivesCatalogueState.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/DriveStateCatalogueTest.hpp"
+#include "common/dataStructures/DesiredDriveState.hpp"
+#include "common/dataStructures/DriveInfo.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/TapeDrive.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/SourcedParameter.hpp"
+
+namespace unitTests {
+
+namespace {
+cta::common::dataStructures::SecurityIdentity getAdmin() {
+  cta::common::dataStructures::SecurityIdentity admin;
+  admin.username = "admin_user_name";
+  admin.host = "admin_host";
+  return admin;
+}
+
+cta::common::dataStructures::TapeDrive getTapeDriveWithMandatoryElements(const std::string &driveName) {
+  cta::common::dataStructures::TapeDrive tapeDrive;
+  tapeDrive.driveName = driveName;
+  tapeDrive.host = "admin_host";
+  tapeDrive.logicalLibrary = "VLSTK10";
+  tapeDrive.mountType = cta::common::dataStructures::MountType::NoMount;
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  tapeDrive.desiredUp = false;
+  tapeDrive.desiredForceDown = false;
+  return tapeDrive;
+}
+
+cta::common::dataStructures::TapeDrive getTapeDriveWithAllElements(const std::string &driveName) {
+  cta::common::dataStructures::TapeDrive tapeDrive;
+  tapeDrive.driveName = driveName;
+  tapeDrive.host = "admin_host";
+  tapeDrive.logicalLibrary = "VLSTK10";
+  tapeDrive.mountType = cta::common::dataStructures::MountType::NoMount;
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  tapeDrive.desiredUp = false;
+  tapeDrive.desiredForceDown = false;
+  tapeDrive.diskSystemName = "dummyDiskSystemName";
+  tapeDrive.reservedBytes = 694498291384;
+  tapeDrive.reservationSessionId = 0;
+
+  tapeDrive.sessionStartTime = 1001;
+  tapeDrive.mountStartTime = 1002;
+  tapeDrive.transferStartTime = 1003;
+  tapeDrive.unloadStartTime = 1004;
+  tapeDrive.unmountStartTime = 1005;
+  tapeDrive.drainingStartTime = 1006;
+  tapeDrive.downOrUpStartTime = 1007;
+  tapeDrive.probeStartTime = 1008;
+  tapeDrive.cleanupStartTime = 1009;
+  tapeDrive.startStartTime = 1010;
+  tapeDrive.shutdownTime = 1011;
+
+  tapeDrive.reasonUpDown = "Random Reason";
+
+  tapeDrive.currentVid = "VIDONE";
+  tapeDrive.ctaVersion = "v1.0.0";
+  tapeDrive.currentPriority = 3;
+  tapeDrive.currentActivity = "Activity1";
+  tapeDrive.currentTapePool = "tape_pool_0";
+  tapeDrive.nextMountType = cta::common::dataStructures::MountType::Retrieve;
+  tapeDrive.nextVid = "VIDTWO";
+  tapeDrive.nextTapePool = "tape_pool_1";
+  tapeDrive.nextPriority = 1;
+  tapeDrive.nextActivity = "Activity2";
+
+  tapeDrive.devFileName = "fileName";
+  tapeDrive.rawLibrarySlot = "librarySlot1";
+
+  tapeDrive.currentVo = "VO_ONE";
+  tapeDrive.nextVo = "VO_TWO";
+
+  tapeDrive.userComment = "Random comment";
+  tapeDrive.creationLog = cta::common::dataStructures::EntryLog("user_name_1", "host_1", 100002);
+  tapeDrive.lastModificationLog = cta::common::dataStructures::EntryLog("user_name_2", "host_2", 10032131);
+
+  return tapeDrive;
+}
+}  // namespace
+
+cta_catalogue_DriveStateTest::cta_catalogue_DriveStateTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(getAdmin()) {
+}
+
+void cta_catalogue_DriveStateTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_DriveStateTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveNames) {
+  const std::list<std::string> tapeDriveNames = {"VDSTK11", "VDSTK12", "VDSTK21", "VDSTK22"};
+  for (const auto& name : tapeDriveNames) {
+    const auto tapeDrive = getTapeDriveWithMandatoryElements(name);
+    m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  }
+  const auto storedTapeDriveNames = m_catalogue->DriveState()->getTapeDriveNames();
+  ASSERT_EQ(tapeDriveNames, storedTapeDriveNames);
+  for (const auto& name : tapeDriveNames) {
+    m_catalogue->DriveState()->deleteTapeDrive(name);
+  }
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getAllTapeDrives) {
+  std::list<std::string> tapeDriveNames;
+  // Create 100 tape drives
+  for (size_t i = 0; i < 100; i++) {
+    std::stringstream ss;
+    ss << "VDSTK" << std::setw(5) << std::setfill('0') << i;
+    tapeDriveNames.push_back(ss.str());
+  }
+  std::list<cta::common::dataStructures::TapeDrive> tapeDrives;
+  for (const auto& name : tapeDriveNames) {
+    const auto tapeDrive = getTapeDriveWithMandatoryElements(name);
+    m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+    tapeDrives.push_back(tapeDrive);
+  }
+  auto storedTapeDrives = m_catalogue->DriveState()->getTapeDrives();
+  ASSERT_EQ(tapeDriveNames.size(), storedTapeDrives.size());
+  while (!storedTapeDrives.empty()) {
+    const auto storedTapeDrive = storedTapeDrives.front();
+    const auto tapeDrive = tapeDrives.front();
+    storedTapeDrives.pop_front();
+    tapeDrives.pop_front();
+    ASSERT_EQ(tapeDrive, storedTapeDrive);
+  }
+  for (const auto& name : tapeDriveNames) {
+    m_catalogue->DriveState()->deleteTapeDrive(name);
+  }
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDrive) {
+  const std::string tapeDriveName = "VDSTK11";
+  const auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(tapeDrive, storedTapeDrive);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithEmptyEntryLog) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.creationLog = cta::common::dataStructures::EntryLog("", "", 0);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().creationLog);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithNonExistingLogicalLibrary) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().logicalLibraryDisabled);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithDisabledLogicalLibrary) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, tapeDrive.logicalLibrary, true, "comment");
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(storedTapeDrive.value().logicalLibraryDisabled);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+  m_catalogue->LogicalLibrary()->deleteLogicalLibrary(tapeDrive.logicalLibrary);
+}
+
+
+TEST_P(cta_catalogue_DriveStateTest, failToGetTapeDrive) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(wrongName);
+  ASSERT_FALSE(storedTapeDrive);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDriveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, failToDeleteTapeDrive) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string wrongName = "VDSTK56";
+  const auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  m_catalogue->DriveState()->deleteTapeDrive(wrongName);
+  auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDriveName);
+  storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, getTapeDriveWithAllElements) {
+  const std::string tapeDriveName = "VDSTK11";
+  const auto tapeDrive = getTapeDriveWithAllElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(tapeDrive, storedTapeDrive.value());
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, multipleTapeDrives) {
+  const std::string tapeDriveName1 = "VDSTK11";
+  const std::string tapeDriveName2 = "VDSTK12";
+  const auto tapeDrive1 = getTapeDriveWithMandatoryElements(tapeDriveName1);
+  const auto tapeDrive2 = getTapeDriveWithAllElements(tapeDriveName2);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive1);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive2);
+  const auto storedTapeDrive1 = m_catalogue->DriveState()->getTapeDrive(tapeDrive1.driveName);
+  const auto storedTapeDrive2 = m_catalogue->DriveState()->getTapeDrive(tapeDrive2.driveName);
+  ASSERT_EQ(tapeDrive1, storedTapeDrive1);
+  ASSERT_EQ(tapeDrive2, storedTapeDrive2);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive1.driveName);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive2.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateEmpty) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.reasonUpDown = "Previous reason";
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  {
+    cta::common::dataStructures::DesiredDriveState desiredState;
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reasonUpDown));
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value(), tapeDrive.reasonUpDown.value());
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateWithEmptyReason) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  {
+    cta::common::dataStructures::DesiredDriveState desiredState;
+    desiredState.reason = "";
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  // SqlLite (InMemory) returns an empty string and Oracle returns a std::nullopt
+  if (storedTapeDrive.value().reasonUpDown) {
+    ASSERT_TRUE(storedTapeDrive.value().reasonUpDown.value().empty());
+  } else {
+    ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reasonUpDown));
+  }
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredState) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::common::dataStructures::DesiredDriveState desiredState;
+  desiredState.up = false;
+  desiredState.forceDown = true;
+  desiredState.reason = "reason";
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp , desiredState.up);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown , desiredState.forceDown);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value() , desiredState.reason);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateComment) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  // It should keep this Desired Status
+  tapeDrive.desiredUp = true;
+  tapeDrive.desiredForceDown = false;
+  tapeDrive.reasonUpDown = "reason";
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::common::dataStructures::DesiredDriveState desiredState;
+  // It should update only the comment
+  const std::string comment = "New Comment";
+  desiredState.up = false;
+  desiredState.forceDown = true;
+  desiredState.reason = "reason2";
+  desiredState.comment = comment;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp , tapeDrive.desiredUp);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown , tapeDrive.desiredForceDown);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value() , tapeDrive.reasonUpDown);
+  ASSERT_EQ(storedTapeDrive.value().userComment.value() , comment);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setDesiredStateEmptyComment) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  // It should keep this Desired Status
+  tapeDrive.desiredUp = true;
+  tapeDrive.desiredForceDown = false;
+  tapeDrive.reasonUpDown = "reason";
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::common::dataStructures::DesiredDriveState desiredState;
+  // It should update only the comment
+  const std::string comment = "";
+  desiredState.up = false;
+  desiredState.forceDown = true;
+  desiredState.reason = "reason2";
+  desiredState.comment = comment;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->setDesiredDriveState(tapeDriveName, desiredState, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp , tapeDrive.desiredUp);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown , tapeDrive.desiredForceDown);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value() , tapeDrive.reasonUpDown);
+  // SqlLite (InMemory) returns an empty string and Oracle returns a std::nullopt
+  if (storedTapeDrive.value().userComment) {
+    ASSERT_TRUE(storedTapeDrive.value().userComment.value().empty());
+  } else {
+    ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().userComment));
+  }
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setTapeDriveStatistics) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Transferring;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatsInputs inputs;
+  inputs.reportTime = time(nullptr);
+  inputs.bytesTransferred = 123456789;
+  inputs.filesTransferred = 987654321;
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatistics(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(storedTapeDrive.value().bytesTransferedInSession.value(), inputs.bytesTransferred);
+  ASSERT_EQ(storedTapeDrive.value().filesTransferedInSession.value(), inputs.filesTransferred);
+  const auto lastModificationLog = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host,
+    inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value() , lastModificationLog);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, setTapeDriveStatisticsInNoTransferingStatus) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  cta::ReportDriveStatsInputs inputs;
+  inputs.reportTime = time(nullptr);
+  inputs.bytesTransferred = 123456789;
+  inputs.filesTransferred = 987654321;
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatistics(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().bytesTransferedInSession);
+  ASSERT_FALSE(storedTapeDrive.value().filesTransferedInSession);
+  ASSERT_FALSE(storedTapeDrive.value().lastModificationLog);
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusSameAsPrevious) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  // We update keeping the same status, so it has to update only the lastModificationLog
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = tapeDrive.driveStatus;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 123456;
+  inputs.filesTransferred = 987654;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(driveInfo.driveName, storedTapeDrive.value().driveName);
+  ASSERT_EQ(inputs.status, storedTapeDrive.value().driveStatus);
+  ASSERT_NE(inputs.mountType, storedTapeDrive.value().mountType);  // Not update this value
+  ASSERT_EQ(driveInfo.host, storedTapeDrive.value().host);
+  ASSERT_EQ(driveInfo.logicalLibrary, storedTapeDrive.value().logicalLibrary);
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(log, storedTapeDrive.value().lastModificationLog.value());
+  ASSERT_FALSE(storedTapeDrive.value().bytesTransferedInSession);
+  ASSERT_FALSE(storedTapeDrive.value().filesTransferedInSession);
+  // Disk reservations are not updated by updateTapeDriveStatus()
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusSameTransferingAsPrevious) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Transferring;
+  tapeDrive.sessionStartTime = time(nullptr);
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+  const auto test = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_EQ(tapeDrive.sessionStartTime, test.value().sessionStartTime.value());
+  // We update keeping the same status, so it has to update only the lastModificationLog
+  const uint64_t elapsedTime = 1000;
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = tapeDrive.driveStatus;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = tapeDrive.sessionStartTime.value() + elapsedTime;
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 123456;
+  inputs.filesTransferred = 987654;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(driveInfo.driveName, storedTapeDrive.value().driveName);
+  ASSERT_EQ(inputs.status, storedTapeDrive.value().driveStatus);
+  ASSERT_NE(inputs.mountType, storedTapeDrive.value().mountType);  // Not update this value
+  ASSERT_EQ(driveInfo.host, storedTapeDrive.value().host);
+  ASSERT_EQ(driveInfo.logicalLibrary, storedTapeDrive.value().logicalLibrary);
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(log, storedTapeDrive.value().lastModificationLog.value());
+  ASSERT_EQ(inputs.byteTransferred, storedTapeDrive.value().bytesTransferedInSession.value());
+  ASSERT_EQ(inputs.filesTransferred, storedTapeDrive.value().filesTransferedInSession.value());
+  // It will keep names and bytes, because it isn't in state UP
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  // Check elapsed time
+  ASSERT_EQ(storedTapeDrive.value().sessionElapsedTime.value(), inputs.reportTime - tapeDrive.sessionStartTime.value());
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusDown) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Up;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Down;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_EQ(storedTapeDrive.value().downOrUpStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, cta::common::dataStructures::MountType::NoMount);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, cta::common::dataStructures::DriveStatus::Down);
+  ASSERT_EQ(storedTapeDrive.value().desiredUp, false);
+  ASSERT_EQ(storedTapeDrive.value().desiredForceDown, false);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVid));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentTapePool));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVo));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value(), inputs.reason);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUp) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.desiredUp = true;
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_EQ(storedTapeDrive.value().downOrUpStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, cta::common::dataStructures::MountType::NoMount);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, cta::common::dataStructures::DriveStatus::Up);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVid));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentTapePool));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVo));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reasonUpDown));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUpButDesiredIsDown) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::DrainingToDisk;  // To force a change of state
+  tapeDrive.desiredUp = false;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, cta::common::dataStructures::DriveStatus::Down);
+  ASSERT_EQ(storedTapeDrive.value().reasonUpDown.value(), inputs.reason);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUpCleanSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  tapeDrive.diskSystemName = "DISK_SYSTEM_NAME";
+  tapeDrive.reservedBytes = 123456789;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUpDontCleanSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  tapeDrive.diskSystemName = std::nullopt;
+  tapeDrive.reservedBytes = std::nullopt;
+  tapeDrive.reservationSessionId = std::nullopt;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Up;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.reason = "testing";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(storedTapeDrive.value().diskSystemName);
+  ASSERT_FALSE(storedTapeDrive.value().reservedBytes);
+  ASSERT_FALSE(storedTapeDrive.value().reservationSessionId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusProbing) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Probing;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::NoMount;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 0;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_EQ(storedTapeDrive.value().probeStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVid));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentTapePool));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentVo));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusStarting) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Starting;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_EQ(storedTapeDrive.value().sessionStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_EQ(storedTapeDrive.value().currentActivity.value(), inputs.activity);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusMounting) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Mounting;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 0;
+  inputs.filesTransferred = 0;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_EQ(storedTapeDrive.value().mountStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusTransfering) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Transferring;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_EQ(storedTapeDrive.value().bytesTransferedInSession.value(), inputs.byteTransferred);
+  ASSERT_EQ(storedTapeDrive.value().filesTransferedInSession.value(), inputs.filesTransferred);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_EQ(storedTapeDrive.value().sessionElapsedTime.value(), 0);  // Because it's starting
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_EQ(storedTapeDrive.value().transferStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUnloading) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Unloading;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_EQ(storedTapeDrive.value().unloadStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusUnmounting) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Unmounting;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_EQ(storedTapeDrive.value().unmountStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusDrainingToDisk) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::DrainingToDisk;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_EQ(storedTapeDrive.value().drainingStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusCleaningUp) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::CleaningUp;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_EQ(storedTapeDrive.value().sessionId.value(), inputs.mountSessionId);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_EQ(storedTapeDrive.value().cleanupStartTime.value(), inputs.reportTime);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().shutdownTime));
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, updateTapeDriveStatusShutdown) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.driveStatus = cta::common::dataStructures::DriveStatus::Down;  // To force a change of state
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::ReportDriveStatusInputs inputs;
+  inputs.status = cta::common::dataStructures::DriveStatus::Shutdown;
+  // We use a different MountType to check it doesn't update this value in the database
+  inputs.mountType = cta::common::dataStructures::MountType::ArchiveForUser;
+  inputs.reportTime = time(nullptr);
+  inputs.mountSessionId = 123456;
+  inputs.byteTransferred = 987654;
+  inputs.filesTransferred = 456;
+  inputs.vid = "vid";
+  inputs.tapepool = "tapepool";
+  inputs.vo = "vo";
+  inputs.activity = "activity";
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName = tapeDrive.driveName;
+  driveInfo.host = tapeDrive.host;
+  driveInfo.logicalLibrary = tapeDrive.logicalLibrary;
+  {
+    cta::log::LogContext dummyLc(m_dummyLog);
+    auto tapeDrivesState = std::make_unique<cta::TapeDrivesCatalogueState>(*m_catalogue);
+    tapeDrivesState->updateDriveStatus(driveInfo, inputs, dummyLc);
+  }
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionId));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().bytesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().filesTransferedInSession));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().sessionElapsedTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().mountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().transferStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unloadStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().unmountStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().drainingStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().downOrUpStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().probeStartTime));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().cleanupStartTime));
+  ASSERT_EQ(storedTapeDrive.value().shutdownTime.value(), inputs.reportTime);
+  const auto log = cta::common::dataStructures::EntryLog("NO_USER", driveInfo.host, inputs.reportTime);
+  ASSERT_EQ(storedTapeDrive.value().lastModificationLog.value(), log);
+  ASSERT_EQ(storedTapeDrive.value().mountType, inputs.mountType);
+  ASSERT_EQ(storedTapeDrive.value().driveStatus, inputs.status);
+  ASSERT_EQ(storedTapeDrive.value().currentVid.value(), inputs.vid);
+  ASSERT_EQ(storedTapeDrive.value().currentTapePool.value(), inputs.tapepool);
+  ASSERT_EQ(storedTapeDrive.value().currentVo.value(), inputs.vo);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().currentActivity));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+
+TEST_P(cta_catalogue_DriveStateTest, addDiskSpaceReservationWhenItsNull) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = std::nullopt;
+  tapeDrive.reservedBytes = std::nullopt;
+  tapeDrive.reservationSessionId = std::nullopt;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = "space1";
+  const uint64_t reservedBytes = 987654;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 123;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, incrementAnExistingDiskSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "existing_space";
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = tapeDrive.diskSystemName.value();
+  const uint64_t reservedBytes = 852;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = tapeDrive.reservationSessionId.value();
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes + tapeDrive.reservedBytes.value());
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, decrementANonExistingDiskSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = std::nullopt;
+  tapeDrive.reservedBytes = std::nullopt;
+  tapeDrive.reservationSessionId = std::nullopt;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = "space1";
+  const uint64_t reservedBytes = 852;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 123;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_FALSE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, decrementAExistingDiskSpaceReservation) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "existing_space";
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest request1;
+  const std::string spaceName = tapeDrive.diskSystemName.value();
+  const uint64_t reservedBytes = 852;
+  request1.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = tapeDrive.reservationSessionId.value();
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, request1, dummyLc);
+
+  const auto storedTapeDrive1 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive1.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive1.value().reservedBytes.value(), tapeDrive.reservedBytes.value() - reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive1.value().reservationSessionId.value(), mountId);
+
+  cta::DiskSpaceReservationRequest request2;
+  request2.addRequest(tapeDrive.diskSystemName.value(), tapeDrive.reservedBytes.value() - reservedBytes);
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, request2, dummyLc);
+
+  const auto storedTapeDrive2 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive2.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive2.value().reservedBytes.value(), 0);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive2.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, incrementAnExistingDiskSpaceReservationAndThenLargerDecrement) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "existing_space";
+  tapeDrive.reservedBytes = 10;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  cta::DiskSpaceReservationRequest increaseRequest;
+  const std::string spaceName = tapeDrive.diskSystemName.value();
+  const uint64_t reservedBytes = 20;
+  increaseRequest.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = tapeDrive.reservationSessionId.value();
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, increaseRequest, dummyLc);
+
+  const auto storedTapeDrive1 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive1.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive1.value().reservedBytes.value(), reservedBytes + tapeDrive.reservedBytes.value());
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive1.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive1.value().reservationSessionId.value(), mountId);
+
+  cta::DiskSpaceReservationRequest decreaseRequest;
+  decreaseRequest.addRequest(tapeDrive.diskSystemName.value(), 100000);  // Decrease a bigger number of reserved bytes
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, decreaseRequest, dummyLc);
+
+  const auto storedTapeDrive2 = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive2.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive2.value().reservedBytes.value(), 0);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive2.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive2.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, failToIncrementAnOldDiskSystem) {
+  const std::string tapeDriveName = "VDSTK11";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = "old_space";
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  // New Disk Space
+  cta::DiskSpaceReservationRequest newRequest;
+  const std::string spaceName = "new_space";
+  const uint64_t reservedBytes = 345;
+  newRequest.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 3;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, newRequest, dummyLc);
+
+  // Decrease Old Space
+  cta::DiskSpaceReservationRequest oldRequest;
+  oldRequest.addRequest(tapeDrive.diskSystemName.value(), tapeDrive.reservedBytes.value());
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, tapeDrive.reservationSessionId.value(), oldRequest,
+    dummyLc);
+
+  // Check it keeps the new disk space system values
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), spaceName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, sameSystemNameButDifferentMountID) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string diskSystemName = "space_name";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = diskSystemName;
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  // New Disk Space
+  cta::DiskSpaceReservationRequest request;
+  const std::string spaceName = diskSystemName;
+  const uint64_t reservedBytes = 345;
+  request.addRequest(spaceName, reservedBytes);
+  const uint64_t mountId = 3;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, request, dummyLc);
+
+  // Check it keeps the new disk space system values
+  const auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), diskSystemName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+TEST_P(cta_catalogue_DriveStateTest, failToDecrementAnOldMountIDAndDecrementNewAgain) {
+  const std::string tapeDriveName = "VDSTK11";
+  const std::string diskSystemName = "space_name";
+  auto tapeDrive = getTapeDriveWithMandatoryElements(tapeDriveName);
+  tapeDrive.diskSystemName = diskSystemName;
+  tapeDrive.reservedBytes = 1234;
+  tapeDrive.reservationSessionId = 9;
+  m_catalogue->DriveState()->createTapeDrive(tapeDrive);
+
+  // New Disk Space
+  cta::DiskSpaceReservationRequest newRequest;
+  const uint64_t reservedBytes = 345;
+  newRequest.addRequest(diskSystemName, reservedBytes);
+  const uint64_t mountId = 3;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->DriveState()->reserveDiskSpace(tapeDriveName, mountId, newRequest, dummyLc);
+
+  // Decrease Old Space
+  cta::DiskSpaceReservationRequest oldRequest;
+  oldRequest.addRequest(diskSystemName, tapeDrive.reservedBytes.value());
+
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, tapeDrive.reservationSessionId.value(), oldRequest, dummyLc);
+
+  // Check it keeps the new disk space system values
+  auto storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), diskSystemName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  // Decrease New Space
+  cta::DiskSpaceReservationRequest decreaseRequest;
+  const uint64_t decreasedBytes = 10;
+  decreaseRequest.addRequest(diskSystemName, decreasedBytes);
+  m_catalogue->DriveState()->releaseDiskSpace(tapeDriveName, mountId, decreaseRequest, dummyLc);
+
+  // Check it keeps the new disk space system values
+  storedTapeDrive = m_catalogue->DriveState()->getTapeDrive(tapeDrive.driveName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().diskSystemName));
+  ASSERT_EQ(storedTapeDrive.value().diskSystemName.value(), diskSystemName);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservedBytes));
+  ASSERT_EQ(storedTapeDrive.value().reservedBytes.value(), reservedBytes - decreasedBytes);
+  ASSERT_TRUE(static_cast<bool>(storedTapeDrive.value().reservationSessionId));
+  ASSERT_EQ(storedTapeDrive.value().reservationSessionId.value(), mountId);
+
+  m_catalogue->DriveState()->deleteTapeDrive(tapeDrive.driveName);
+}
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/DriveStateCatalogueTest.hpp b/catalogue/tests/modules/DriveStateCatalogueTest.hpp
new file mode 100644
index 0000000000..1d9f1aa16e
--- /dev/null
+++ b/catalogue/tests/modules/DriveStateCatalogueTest.hpp
@@ -0,0 +1,45 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_DriveStateTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_DriveStateTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/FileRecycleLogCatalogueTest.cpp b/catalogue/tests/modules/FileRecycleLogCatalogueTest.cpp
new file mode 100644
index 0000000000..2beffac555
--- /dev/null
+++ b/catalogue/tests/modules/FileRecycleLogCatalogueTest.cpp
@@ -0,0 +1,1122 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2021-2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <list>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/InsertFileRecycleLog.hpp"
+#include "catalogue/interfaces/FileRecycleLogCatalogue.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "catalogue/rdbms/RdbmsCatalogue.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/DummyLogger.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_FileRecycleLogTest::cta_catalogue_FileRecycleLogTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_tape1(CatalogueTestUtils::getTape1()),
+    m_tape2(CatalogueTestUtils::getTape2()),
+    m_tape3(CatalogueTestUtils::getTape3()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_storageClassDualCopy(CatalogueTestUtils::getStorageClassDualCopy()),
+    m_storageClassTripleCopy(CatalogueTestUtils::getStorageClassTripleCopy()) {
+}
+
+void cta_catalogue_FileRecycleLogTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_FileRecycleLogTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, reclaimTapeRemovesFilesFromRecycleLog) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapeDrive = "tape_drive";
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+  auto tape2 = m_tape2;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t nbArchiveFiles = 10; // Must be a multiple of 2 for this test
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+    std::ostringstream diskFileId;
+    diskFileId << (12345677 + i);
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file"<<i;
+
+    // Tape copy 1 written to tape
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = i;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassSingleCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = i;
+    fileWritten.blockId = i * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+  }
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  {
+    ASSERT_TRUE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  }
+  log::LogContext dummyLc(m_dummyLog);
+  for (auto & tapeItemWritten: tapeFilesWrittenCopy1){
+    auto tapeItem = static_cast<cta::catalogue::TapeFileWritten *>(tapeItemWritten.get());
+    cta::common::dataStructures::DeleteArchiveRequest req;
+    req.archiveFileID = tapeItem->archiveFileId;
+    req.diskFileId = tapeItem->diskFileId;
+    req.diskFilePath = tapeItem->diskFilePath;
+    req.diskInstance = tapeItem->diskInstance;
+    req.archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(tapeItem->archiveFileId);
+    ASSERT_NO_THROW(m_catalogue->ArchiveFile()->moveArchiveFileToRecycleLog(req,dummyLc));
+  }
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  std::vector<common::dataStructures::FileRecycleLog> deletedArchiveFiles;
+  {
+    auto itor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    while(itor.hasMore()){
+      deletedArchiveFiles.push_back(itor.next());
+    }
+  }
+
+  //And test that these files are in the recycle log
+  ASSERT_EQ(nbArchiveFiles,deletedArchiveFiles.size());
+
+  ASSERT_TRUE(m_catalogue->FileRecycleLog()->getFileRecycleLogItor().hasMore());
+  //Reclaim the tape
+  m_catalogue->Tape()->setTapeFull(m_admin, tape1.vid, true);
+  m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc);
+  {
+    auto itor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_FALSE(itor.hasMore());
+  }
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, emptyFileRecycleLogItorTest) {
+  using namespace cta;
+  auto itor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+  ASSERT_FALSE(itor.hasMore());
+  ASSERT_THROW(itor.next(),cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, getFileRecycleLogItorVidNotExists) {
+  using namespace cta;
+  auto itor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+  ASSERT_FALSE(m_catalogue->FileRecycleLog()->getFileRecycleLogItor().hasMore());
+
+  catalogue::RecycleTapeFileSearchCriteria criteria;
+  criteria.vid = "NOT_EXISTS";
+
+  ASSERT_THROW(m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria),exception::UserError);
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, filesArePutInTheFileRecycleLogInsteadOfBeingSuperseded) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+  auto tape2 = m_tape2;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t nbArchiveFiles = 10; // Must be a multiple of 2 for this test
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+    std::ostringstream diskFileId;
+    diskFileId << (12345677 + i);
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file"<<i;
+
+    // Tape copy 1 written to tape
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = i;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassSingleCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = i;
+    fileWritten.blockId = i * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+  }
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  {
+    ASSERT_TRUE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+    ASSERT_FALSE(m_catalogue->FileRecycleLog()->getFileRecycleLogItor().hasMore());
+  }
+  log::LogContext dummyLc(m_dummyLog);
+  //Archive the same files but in a new tape
+  for(auto & tapeItemWritten: tapeFilesWrittenCopy1){
+    auto tapeItem = static_cast<cta::catalogue::TapeFileWritten *>(tapeItemWritten.get());
+    tapeItem->vid = tape2.vid;
+  }
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  //Change the vid back to the first one to test the content of the file recycle log afterwards
+  for(auto & tapeItemWritten: tapeFilesWrittenCopy1){
+    auto tapeItem = static_cast<cta::catalogue::TapeFileWritten *>(tapeItemWritten.get());
+    tapeItem->vid = tape1.vid;
+  }
+  //Check that the new files written exist on the catalogue
+  {
+    auto archiveFilesItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    bool hasArchiveFilesItor = false;
+    while(archiveFilesItor.hasMore()){
+      hasArchiveFilesItor = true;
+      //The vid is the destination one
+      ASSERT_EQ(tape2.vid, archiveFilesItor.next().tapeFiles.at(1).vid);
+    }
+    ASSERT_TRUE(hasArchiveFilesItor);
+  }
+  //Check that the old files are in the file recycle logs
+  std::list<common::dataStructures::FileRecycleLog> fileRecycleLogs;
+  {
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    while(fileRecycleLogItor.hasMore()){
+      fileRecycleLogs.push_back(fileRecycleLogItor.next());
+    }
+    ASSERT_FALSE(fileRecycleLogs.empty());
+    //Now we check the consistency of what is returned by the file recycle log
+    for(const auto& fileRecycleLog: fileRecycleLogs){
+      //First, get the correct file written to tape associated to the current fileRecycleLog
+      auto tapeFilesWrittenCopy1Itor = std::find_if(tapeFilesWrittenCopy1.begin(), tapeFilesWrittenCopy1.end(),
+        [&fileRecycleLog](const cta::catalogue::TapeItemWrittenPointer & item) {
+          auto fileWrittenPtr = static_cast<cta::catalogue::TapeFileWritten *>(item.get());
+          return fileWrittenPtr->archiveFileId == fileRecycleLog.archiveFileId;
+      });
+
+      ASSERT_NE(tapeFilesWrittenCopy1.end(), tapeFilesWrittenCopy1Itor);
+      auto fileWrittenPtr = static_cast<cta::catalogue::TapeFileWritten *>(tapeFilesWrittenCopy1Itor->get());
+      ASSERT_EQ(fileRecycleLog.vid,tape1.vid);
+      ASSERT_EQ(fileRecycleLog.fSeq,fileWrittenPtr->fSeq);
+      ASSERT_EQ(fileRecycleLog.blockId,fileWrittenPtr->blockId);
+      ASSERT_EQ(fileRecycleLog.copyNb,fileWrittenPtr->copyNb);
+      ASSERT_EQ(fileRecycleLog.archiveFileId,fileWrittenPtr->archiveFileId);
+      ASSERT_EQ(fileRecycleLog.diskInstanceName,fileWrittenPtr->diskInstance);
+      ASSERT_EQ(fileRecycleLog.diskFileId,fileWrittenPtr->diskFileId);
+      ASSERT_EQ(fileRecycleLog.diskFileIdWhenDeleted,fileWrittenPtr->diskFileId);
+      ASSERT_EQ(fileRecycleLog.diskFileUid,fileWrittenPtr->diskFileOwnerUid);
+      ASSERT_EQ(fileRecycleLog.diskFileGid,fileWrittenPtr->diskFileGid);
+      ASSERT_EQ(fileRecycleLog.sizeInBytes,fileWrittenPtr->size);
+      ASSERT_EQ(fileRecycleLog.checksumBlob,fileWrittenPtr->checksumBlob);
+      ASSERT_EQ(fileRecycleLog.storageClassName,fileWrittenPtr->storageClassName);
+      ASSERT_EQ(fileRecycleLog.reconciliationTime,fileRecycleLog.archiveFileCreationTime);
+      ASSERT_EQ(std::nullopt, fileRecycleLog.collocationHint);
+      ASSERT_EQ(std::nullopt, fileRecycleLog.diskFilePath);
+      ASSERT_EQ(cta::catalogue::InsertFileRecycleLog::getRepackReasonLog(),fileRecycleLog.reasonLog);
+    }
+  }
+  {
+    //Check the vid search criteria
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.vid = tape1.vid;
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    int nbFileRecycleLogs = 0;
+    while(fileRecycleLogItor.hasMore()){
+      nbFileRecycleLogs++;
+      fileRecycleLogItor.next();
+    }
+    ASSERT_EQ(nbArchiveFiles,nbFileRecycleLogs);
+  }
+  {
+    //Check the diskFileId search criteria
+    std::string diskFileId = "12345678";
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.diskFileIds = std::vector<std::string>();
+    criteria.diskFileIds->push_back(diskFileId);
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    auto fileRecycleLog = fileRecycleLogItor.next();
+    ASSERT_EQ(diskFileId,fileRecycleLog.diskFileId);
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+  }
+  {
+    //Check the non existing diskFileId search criteria
+    std::string diskFileId = "DOES_NOT_EXIST";
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.diskFileIds = std::vector<std::string>();
+    criteria.diskFileIds->push_back(diskFileId);
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+  }
+  {
+    //Check the archiveID search criteria
+    uint64_t archiveFileId = 1;
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.archiveFileId = archiveFileId;
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    auto fileRecycleLog = fileRecycleLogItor.next();
+    ASSERT_EQ(archiveFileId,fileRecycleLog.archiveFileId);
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+  }
+  {
+    //Check the non existing archiveFileId search criteria
+    uint64_t archiveFileId = -1;
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.archiveFileId = archiveFileId;
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+  }
+  {
+    //Check the copynb search criteria
+    uint64_t copynb = 1;
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.copynb = copynb;
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    int nbFileRecycleLogs = 0;
+    while(fileRecycleLogItor.hasMore()){
+      nbFileRecycleLogs++;
+      fileRecycleLogItor.next();
+    }
+    ASSERT_EQ(nbArchiveFiles,nbFileRecycleLogs);
+  }
+  {
+    //Check the disk instance search criteria
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.diskInstance = diskInstance;
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+    int nbFileRecycleLogs = 0;
+    while(fileRecycleLogItor.hasMore()){
+      nbFileRecycleLogs++;
+      fileRecycleLogItor.next();
+    }
+    ASSERT_EQ(nbArchiveFiles,nbFileRecycleLogs);
+  }
+  {
+    //Check multiple search criteria together
+    uint64_t copynb = 1;
+    uint64_t archiveFileId = 1;
+    std::string diskFileId = "12345678";
+    catalogue::RecycleTapeFileSearchCriteria criteria;
+    criteria.diskInstance = diskInstance;
+    criteria.copynb = copynb;
+    criteria.archiveFileId = archiveFileId;
+    criteria.diskFileIds = std::vector<std::string>();
+    criteria.diskFileIds->push_back(diskFileId);
+    criteria.vid = tape1.vid;
+
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor(criteria);
+
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    auto fileRecycleLog = fileRecycleLogItor.next();
+    ASSERT_EQ(archiveFileId, fileRecycleLog.archiveFileId);
+    ASSERT_EQ(diskFileId, fileRecycleLog.diskFileId);
+    ASSERT_EQ(copynb, fileRecycleLog.copyNb);
+    ASSERT_EQ(tape1.vid, fileRecycleLog.vid);
+    ASSERT_EQ(diskInstance, fileRecycleLog.diskInstanceName);
+
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+  }
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, sameFileWrittenToSameTapePutThePreviousCopyOnTheFileRecycleLog) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+  std::ostringstream diskFileId;
+  diskFileId << 12345677;
+
+  std::ostringstream diskFilePath;
+  diskFilePath << "/test/file1";
+
+  // Two files same archiveFileId and CopyNb on the same tape
+  auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & fileWritten = *fileWrittenUP;
+  fileWritten.archiveFileId = 1;
+  fileWritten.diskInstance = diskInstance;
+  fileWritten.diskFileId = diskFileId.str();
+  fileWritten.diskFilePath = diskFilePath.str();
+  fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+  fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+  fileWritten.size = archiveFileSize;
+  fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+  fileWritten.storageClassName = m_storageClassSingleCopy.name;
+  fileWritten.vid = tape1.vid;
+  fileWritten.fSeq = 1;
+  fileWritten.blockId = 1 * 100;
+  fileWritten.copyNb = 1;
+  fileWritten.tapeDrive = tapeDrive;
+  tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+
+  auto & tapeItemWritten =  *(tapeFilesWrittenCopy1.begin());
+  auto tapeItem = static_cast<cta::catalogue::TapeFileWritten *>(tapeItemWritten.get());
+  tapeItem->fSeq = 2;
+  tapeItem->blockId = 2 *100 + 1;
+
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  {
+    auto archiveFilesItor = m_catalogue->ArchiveFile()->getArchiveFilesItor();
+    ASSERT_TRUE(archiveFilesItor.hasMore());
+    //The file with fseq 2 is on the active archive files of CTA
+    ASSERT_EQ(2,archiveFilesItor.next().tapeFiles.at(1).fSeq);
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    //The previous file (fSeq = 1) is on the recycle log
+    ASSERT_EQ(1,fileRecycleLogItor.next().fSeq);
+  }
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, RestoreTapeFileCopy) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  // Write a file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a second copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+  {
+    //Assert both copies written
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete copy of file on tape1
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape1.vid;
+    criteria.diskInstance = diskInstance;
+    criteria.diskFileIds = std::vector<std::string>();
+    auto fid = std::to_string(strtol("BC614D", nullptr, 16));
+    criteria.diskFileIds.value().push_back(fid);
+    auto archiveFileForDeletion = m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria);
+    archiveFileForDeletion.diskFileInfo.path = "/test/file1";
+    m_catalogue->TapeFile()->deleteTapeFileCopy(archiveFileForDeletion, reason);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+  }
+
+
+  {
+    // restore copy of file on tape1
+    catalogue::RecycleTapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 1;
+    searchCriteria.vid = tape1.vid;
+
+    // new FID does not matter because archive file still exists in catalogue
+    m_catalogue->FileRecycleLog()->restoreFileInRecycleLog(searchCriteria, "0");
+
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    //assert both copies present
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    //assert recycle log is empty
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+
+  }
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, RestoreRewrittenTapeFileCopyFails) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+
+  // Write a file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a second copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+  {
+    //Assert both copies written
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete copy of file on tape1
+
+    //delete copy of file on tape1
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape1.vid;
+    criteria.diskInstance = diskInstance;
+    criteria.diskFileIds = std::vector<std::string>();
+    auto fid = std::to_string(strtol("BC614D", nullptr, 16));
+    criteria.diskFileIds.value().push_back(fid);
+    auto archiveFileForDeletion = m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria);
+    archiveFileForDeletion.diskFileInfo.path = "/test/file1";
+    m_catalogue->TapeFile()->deleteTapeFileCopy(archiveFileForDeletion, reason);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+  }
+
+    // Rewrite deleted copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 2;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  {
+    //restore copy of file on tape1
+    catalogue::RecycleTapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 1;
+    searchCriteria.vid = tape1.vid;
+
+    ASSERT_THROW(m_catalogue->FileRecycleLog()->restoreFileInRecycleLog(searchCriteria, "0"),
+      catalogue::UserSpecifiedExistingDeletedFileCopy);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    //assert only two copies present
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    //assert recycle log still contains deleted copy
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+  }
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, RestoreVariousDeletedTapeFileCopies) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const std::string tapePoolName3 = "tape_pool_name_3";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName3, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassTripleCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  auto tape3 = m_tape3;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+  tape3.tapePoolName = tapePoolName3;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+  m_catalogue->Tape()->createTape(m_admin, tape3);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+
+  // Write a file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassTripleCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a second copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassTripleCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a third copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassTripleCopy.name;
+    fileWritten.vid = tape3.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 3;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+  {
+    //Assert all copies written
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(3, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete copy of file on tape1
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape1.vid;
+    criteria.diskInstance = diskInstance;
+    criteria.diskFileIds = std::vector<std::string>();
+    auto fid = std::to_string(strtol("BC614D", nullptr, 16));
+    criteria.diskFileIds.value().push_back(fid);
+    auto archiveFileForDeletion = m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria);
+    archiveFileForDeletion.diskFileInfo.path = "/test/file1";
+    m_catalogue->TapeFile()->deleteTapeFileCopy(archiveFileForDeletion, reason);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete copy of file on tape2
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape2.vid;
+    criteria.diskInstance = diskInstance;
+    criteria.diskFileIds = std::vector<std::string>();
+    auto fid = std::to_string(strtol("BC614D", nullptr, 16));
+    criteria.diskFileIds.value().push_back(fid);
+    auto archiveFileForDeletion = m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria);
+    archiveFileForDeletion.diskFileInfo.path = "/test/file1";
+    m_catalogue->TapeFile()->deleteTapeFileCopy(archiveFileForDeletion, reason);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+  }
+
+
+  {
+    //try to restore all deleted copies should give an error
+    catalogue::RecycleTapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 1;
+
+    ASSERT_THROW(m_catalogue->FileRecycleLog()->restoreFileInRecycleLog(searchCriteria, "0"),
+      cta::exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_FileRecycleLogTest, RestoreArchiveFileAndCopy) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+
+  // Write a file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a second copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+  {
+    //Assert both copies written
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete archive file
+    common::dataStructures::DeleteArchiveRequest deleteRequest;
+    deleteRequest.archiveFileID = 1;
+    deleteRequest.archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    deleteRequest.diskInstance = diskInstance;
+    deleteRequest.diskFileId = std::to_string(12345677);
+    deleteRequest.diskFilePath = "/test/file1";
+
+    log::LogContext dummyLc(m_dummyLog);
+    m_catalogue->ArchiveFile()->moveArchiveFileToRecycleLog(deleteRequest, dummyLc);
+    ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(1), cta::exception::Exception);
+  }
+
+
+  {
+    //restore copy of file on tape1
+    catalogue::RecycleTapeFileSearchCriteria searchCriteria;
+    searchCriteria.archiveFileId = 1;
+    searchCriteria.vid = tape1.vid;
+
+    m_catalogue->FileRecycleLog()->restoreFileInRecycleLog(searchCriteria, std::to_string(12345678)); //previous fid + 1
+
+    //assert archive file has been restored in the catalogue
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    ASSERT_EQ(archiveFile.diskFileId, std::to_string(12345678));
+    ASSERT_EQ(archiveFile.diskInstance, diskInstance);
+    ASSERT_EQ(archiveFile.storageClass, m_storageClassDualCopy.name);
+
+    //assert recycle log has the other tape file copy
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    auto fileRecycleLog = fileRecycleLogItor.next();
+    ASSERT_FALSE(fileRecycleLogItor.hasMore());
+  }
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp b/catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp
new file mode 100644
index 0000000000..461daafe47
--- /dev/null
+++ b/catalogue/tests/modules/FileRecycleLogCatalogueTest.hpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/log/DummyLogger.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_FileRecycleLogTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_FileRecycleLogTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+  const cta::catalogue::CreateTapeAttributes m_tape2;
+  const cta::catalogue::CreateTapeAttributes m_tape3;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::StorageClass m_storageClassDualCopy;
+  const cta::common::dataStructures::StorageClass m_storageClassTripleCopy;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/LogicalLibraryCatalogueTest.cpp b/catalogue/tests/modules/LogicalLibraryCatalogueTest.cpp
new file mode 100644
index 0000000000..d2db960d3c
--- /dev/null
+++ b/catalogue/tests/modules/LogicalLibraryCatalogueTest.cpp
@@ -0,0 +1,751 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/TapePool.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/EntryLog.hpp"
+#include "common/dataStructures/LogicalLibrary.hpp"
+#include "common/dataStructures/Tape.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_LogicalLibraryTest::cta_catalogue_LogicalLibraryTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin("admin", "admin", "admin"),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_tape1(CatalogueTestUtils::getTape1()) {
+}
+
+void cta_catalogue_LogicalLibraryTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_LogicalLibraryTest::TearDown() {
+  m_catalogue.reset();
+}
+
+std::map<std::string, cta::common::dataStructures::LogicalLibrary>
+  cta_catalogue_LogicalLibraryTest::logicalLibraryListToMap(
+  const std::list<cta::common::dataStructures::LogicalLibrary> &listOfLibs) const {
+  try {
+    std::map<std::string, cta::common::dataStructures::LogicalLibrary> nameToLib;
+
+    for (auto &lib: listOfLibs) {
+      if(nameToLib.end() != nameToLib.find(lib.name)) {
+        throw cta::exception::Exception(std::string("Duplicate logical library: value=") + lib.name);
+      }
+      nameToLib[lib.name] = lib;
+    }
+
+    return nameToLib;
+  } catch(cta::exception::Exception &ex) {
+    throw cta::exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, createLogicalLibrary) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+
+  const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+  ASSERT_EQ(1, libs.size());
+
+  const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+  ASSERT_EQ(logicalLibraryName, lib.name);
+  ASSERT_FALSE(lib.isDisabled);
+  ASSERT_EQ(comment, lib.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, createLogicalLibrary_disabled_true) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled(true);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+
+  const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+  ASSERT_EQ(1, libs.size());
+
+  const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+  ASSERT_EQ(logicalLibraryName, lib.name);
+  ASSERT_TRUE(lib.isDisabled);
+  ASSERT_EQ(comment, lib.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, createLogicalLibrary_disabled_false) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled(false);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+
+  const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+  ASSERT_EQ(1, libs.size());
+
+  const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+  ASSERT_EQ(logicalLibraryName, lib.name);
+  ASSERT_FALSE(lib.isDisabled);
+  ASSERT_EQ(comment, lib.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog =
+    lib.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, createLogicalLibrary_same_twice) {
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName,
+    logicalLibraryIsDisabled, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, setLogicalLibraryDisabled_true) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+
+  {
+    const std::list<cta::common::dataStructures::LogicalLibrary> libs =
+      m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_FALSE(lib.isDisabled);
+    ASSERT_EQ(comment, lib.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      lib.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const bool modifiedLogicalLibraryIsDisabled= true;
+  m_catalogue->LogicalLibrary()->setLogicalLibraryDisabled(m_admin, logicalLibraryName,
+    modifiedLogicalLibraryIsDisabled);
+
+  {
+    const std::list<cta::common::dataStructures::LogicalLibrary> libs =
+      m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(modifiedLogicalLibraryIsDisabled, lib.isDisabled);
+    ASSERT_EQ(comment, lib.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, setLogicalLibraryDisabled_false) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+
+  {
+    const std::list<cta::common::dataStructures::LogicalLibrary> libs =
+      m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_FALSE(lib.isDisabled);
+    ASSERT_EQ(comment, lib.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      lib.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const bool modifiedLogicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->setLogicalLibraryDisabled(m_admin, logicalLibraryName,
+    modifiedLogicalLibraryIsDisabled);
+
+  {
+    const std::list<cta::common::dataStructures::LogicalLibrary> libs =
+      m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(modifiedLogicalLibraryIsDisabled, lib.isDisabled);
+    ASSERT_EQ(comment, lib.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, deleteLogicalLibrary) {
+  const bool libNotToDeleteIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string libNotToDeleteComment = "Create logical library to NOT be deleted";
+
+  // Create a tape and a logical library that are not the ones to be deleted
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, libNotToDeleteIsDisabled,
+    libNotToDeleteComment);
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+    ASSERT_EQ(1, libs.size());
+    const auto lib = libs.front();
+    ASSERT_EQ(m_tape1.logicalLibraryName, lib.name);
+    ASSERT_EQ(libNotToDeleteIsDisabled, lib.isDisabled);
+    ASSERT_EQ(libNotToDeleteComment, lib.comment);
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  ASSERT_TRUE(m_catalogue->Tape()->tapeExists(m_tape1.vid));
+  {
+    const auto tapes = m_catalogue->Tape()->getTapes();
+    ASSERT_EQ(1, tapes.size());
+
+    const auto tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.state,tape.state);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const auto creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  // Create the logical library to be deleted
+  const std::string libToDeleteName = "lib_to_delete";
+  const bool libToDeleteIsDisabled = false;
+  const std::string libToDeleteComment = "Create logical library to be deleted";
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, libToDeleteName, libToDeleteIsDisabled,
+    libToDeleteComment);
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+    ASSERT_EQ(2, libs.size());
+    const auto nameToLib = logicalLibraryListToMap(libs);
+    ASSERT_EQ(2, nameToLib.size());
+
+    {
+      const auto nameToLibItor = nameToLib.find(m_tape1.logicalLibraryName);
+      ASSERT_NE(nameToLib.end(), nameToLibItor);
+      const auto &lib = nameToLibItor->second;
+      ASSERT_EQ(m_tape1.logicalLibraryName, lib.name);
+      ASSERT_EQ(libNotToDeleteIsDisabled, lib.isDisabled);
+      ASSERT_EQ(libNotToDeleteComment, lib.comment);
+      const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+      const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+
+    {
+      const auto nameToLibItor = nameToLib.find(libToDeleteName);
+      ASSERT_NE(nameToLib.end(), nameToLibItor);
+      const auto &lib = nameToLibItor->second;
+      ASSERT_EQ(libToDeleteName, lib.name);
+      ASSERT_EQ(libToDeleteIsDisabled, lib.isDisabled);
+      ASSERT_EQ(libToDeleteComment, lib.comment);
+      const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+      const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  m_catalogue->LogicalLibrary()->deleteLogicalLibrary(libToDeleteName);
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+    ASSERT_EQ(1, libs.size());
+    const auto lib = libs.front();
+    ASSERT_EQ(m_tape1.logicalLibraryName, lib.name);
+    ASSERT_EQ(libNotToDeleteIsDisabled, lib.isDisabled);
+    ASSERT_EQ(libNotToDeleteComment, lib.comment);
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, deleteLogicalLibrary_non_existent) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->deleteLogicalLibrary("non_existent_logical_library"),
+    cta::catalogue::UserSpecifiedANonExistentLogicalLibrary);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, deleteLogicalLibrary_non_empty) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+  ASSERT_EQ(1, tapes.size());
+
+  const cta::common::dataStructures::Tape tape = tapes.front();
+  ASSERT_EQ(m_tape1.vid, tape.vid);
+  ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+  ASSERT_EQ(m_tape1.vendor, tape.vendor);
+  ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+  ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+  ASSERT_EQ(m_vo.name, tape.vo);
+  ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+  ASSERT_EQ(m_tape1.state,tape.state);
+  ASSERT_EQ(m_tape1.full, tape.full);
+
+  ASSERT_FALSE(tape.isFromCastor);
+  ASSERT_EQ(m_tape1.comment, tape.comment);
+  ASSERT_FALSE(tape.labelLog);
+  ASSERT_FALSE(tape.lastReadLog);
+  ASSERT_FALSE(tape.lastWriteLog);
+
+  const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog =
+    tape.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->deleteLogicalLibrary(m_tape1.logicalLibraryName),
+    cta::catalogue::UserSpecifiedANonEmptyLogicalLibrary);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryName) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string libraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool libraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, libraryName, libraryIsDisabled, comment);
+
+  {
+    const auto libraries = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libraries.size());
+
+    const auto &library = libraries.front();
+    ASSERT_EQ(libraryName, library.name);
+    ASSERT_FALSE(library.isDisabled);
+    ASSERT_EQ(comment, library.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = library.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = library.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string newLibraryName = "new_logical_library";
+  m_catalogue->LogicalLibrary()->modifyLogicalLibraryName(m_admin, libraryName, newLibraryName);
+
+  {
+    const auto libraries = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libraries.size());
+
+    const auto &library = libraries.front();
+    ASSERT_EQ(newLibraryName, library.name);
+    ASSERT_FALSE(library.isDisabled);
+    ASSERT_EQ(comment, library.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = library.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryName_emptyStringCurrentLogicalLibraryName) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string libraryName = "logical_library";
+  const bool libraryIsDisabled = false;
+  const std::string comment = "Create logical library";
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, libraryName, libraryIsDisabled, comment);
+
+  const std::string newLibraryName = "new_logical_library";
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->modifyLogicalLibraryName(m_admin, "", newLibraryName),
+    cta::catalogue::UserSpecifiedAnEmptyStringLogicalLibraryName);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryName_emptyStringNewLogicalLibraryName) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string libraryName = "logical_library";
+  const bool libraryIsDisabled = false;
+  const std::string comment = "Create logical library";
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, libraryName, libraryIsDisabled, comment);
+
+  {
+    const auto libraries = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libraries.size());
+
+    const auto library = libraries.front();
+    ASSERT_EQ(libraryName, library.name);
+    ASSERT_FALSE(library.isDisabled);
+    ASSERT_EQ(comment, library.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = library.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = library.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string newLibraryName = "";
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->modifyLogicalLibraryName(m_admin, libraryName, newLibraryName),
+    cta::catalogue::UserSpecifiedAnEmptyStringLogicalLibraryName);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryComment) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(comment, lib.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->LogicalLibrary()->modifyLogicalLibraryComment(m_admin, logicalLibraryName, modifiedComment);
+
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(modifiedComment, lib.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryComment_nonExisentLogicalLibrary) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->modifyLogicalLibraryComment(m_admin, logicalLibraryName, comment),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryDisabledReason) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "Create logical library";
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    comment);
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(comment, lib.comment);
+    ASSERT_FALSE(lib.disabledReason);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedDisabledReason = "Modified disabled reason";
+  m_catalogue->LogicalLibrary()->modifyLogicalLibraryDisabledReason(m_admin, logicalLibraryName,
+    modifiedDisabledReason);
+
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(comment, lib.comment);
+    ASSERT_EQ(modifiedDisabledReason, lib.disabledReason.value());
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+  }
+
+  //setting empty reason should delete it from the DB
+  m_catalogue->LogicalLibrary()->modifyLogicalLibraryDisabledReason(m_admin, logicalLibraryName, "");
+  {
+    const auto libs = m_catalogue->LogicalLibrary()->getLogicalLibraries();
+
+    ASSERT_EQ(1, libs.size());
+
+    const cta::common::dataStructures::LogicalLibrary lib = libs.front();
+    ASSERT_EQ(logicalLibraryName, lib.name);
+    ASSERT_EQ(comment, lib.comment);
+    ASSERT_FALSE(lib.disabledReason);
+
+    const cta::common::dataStructures::EntryLog creationLog = lib.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = lib.lastModificationLog;
+  }
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, modifyLogicalLibraryDisabledReason_nonExisentLogicalLibrary) {
+  ASSERT_TRUE(m_catalogue->LogicalLibrary()->getLogicalLibraries().empty());
+
+  const std::string logicalLibraryName = "logical_library";
+  const std::string disabledReason = "Create logical library";
+  ASSERT_THROW(m_catalogue->LogicalLibrary()->modifyLogicalLibraryDisabledReason(m_admin, logicalLibraryName,
+    disabledReason), cta::exception::UserError);
+}
+
+
+TEST_P(cta_catalogue_LogicalLibraryTest, tapeExists_emptyString) {
+  const std::string vid = "";
+  ASSERT_THROW(m_catalogue->Tape()->tapeExists(vid), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_LogicalLibraryTest, createTape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  ASSERT_TRUE(m_catalogue->Tape()->tapeExists(m_tape1.vid));
+
+  const auto tapes = m_catalogue->Tape()->getTapes();
+
+  ASSERT_EQ(1, tapes.size());
+
+  {
+    const auto tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.state,tape.state);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const auto creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+}
+
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp b/catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp
new file mode 100644
index 0000000000..ff23e3fb3a
--- /dev/null
+++ b/catalogue/tests/modules/LogicalLibraryCatalogueTest.hpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/MediaType.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_LogicalLibraryTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_LogicalLibraryTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+
+  std::map<std::string, cta::common::dataStructures::LogicalLibrary> logicalLibraryListToMap(
+    const std::list<cta::common::dataStructures::LogicalLibrary> &listOfLibs) const;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/MediaTypeCatalogueTest.cpp b/catalogue/tests/modules/MediaTypeCatalogueTest.cpp
new file mode 100644
index 0000000000..10f17d1269
--- /dev/null
+++ b/catalogue/tests/modules/MediaTypeCatalogueTest.cpp
@@ -0,0 +1,788 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <optional>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/MediaTypeCatalogueTest.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_MediaTypeTest::cta_catalogue_MediaTypeTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_tape1(CatalogueTestUtils::getTape1()) {
+}
+
+void cta_catalogue_MediaTypeTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_MediaTypeTest::TearDown() {
+  m_catalogue.reset();
+}
+
+std::map<std::string, cta::catalogue::MediaTypeWithLogs> cta_catalogue_MediaTypeTest::mediaTypeWithLogsListToMap(
+  const std::list<cta::catalogue::MediaTypeWithLogs> &listOfMediaTypes) {
+  try {
+    std::map<std::string, cta::catalogue::MediaTypeWithLogs> m;
+
+    for(auto &mediaType: listOfMediaTypes) {
+      if(m.end() != m.find(mediaType.name)) {
+        cta::exception::Exception ex;
+        ex.getMessage() << "Media type " << mediaType.name << " is a duplicate";
+        throw ex;
+      }
+      m[mediaType.name] = mediaType;
+    }
+    return m;
+  } catch(cta::exception::Exception &ex) {
+    throw cta::exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, createMediaType) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+  ASSERT_EQ(1, mediaTypes.size());
+
+  ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+  ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+  ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+  ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+  ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+  ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+  ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+  ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+  ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, createMediaType_same_twice) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  ASSERT_THROW(m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, createMediaType_emptyStringMediaTypeName) {
+  auto mediaType = m_mediaType;
+  mediaType.name = "";
+  ASSERT_THROW(m_catalogue->MediaType()->createMediaType(m_admin, mediaType),
+    cta::catalogue::UserSpecifiedAnEmptyStringMediaTypeName);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, createMediaType_emptyStringComment) {
+  auto mediaType = m_mediaType;
+  mediaType.comment = "";
+  ASSERT_THROW(m_catalogue->MediaType()->createMediaType(m_admin, mediaType),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, createMediaType_emptyStringCartridge) {
+  auto mediaType = m_mediaType;
+  mediaType.cartridge = "";
+  ASSERT_THROW(m_catalogue->MediaType()->createMediaType(m_admin, mediaType),
+    cta::catalogue::UserSpecifiedAnEmptyStringCartridge);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, createMediaType_zeroCapacity) {
+  auto mediaType = m_mediaType;
+  mediaType.capacityInBytes = 0;
+  ASSERT_THROW(m_catalogue->MediaType()->createMediaType(m_admin, mediaType),
+    cta::catalogue::UserSpecifiedAZeroCapacity);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, deleteMediaType) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+  ASSERT_EQ(1, mediaTypes.size());
+
+  ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+  ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+  ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+  ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+  ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+  ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+  ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+  ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+  ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->MediaType()->deleteMediaType(m_mediaType.name);
+
+  ASSERT_TRUE(m_catalogue->MediaType()->getMediaTypes().empty());
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, deleteMediaType_nonExistentMediaType) {
+  ASSERT_THROW(m_catalogue->MediaType()->deleteMediaType("media_type"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, deleteMediaType_usedByTapes) {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  const bool logicalLibraryIsDisabled = false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  //Media type is used by at least one tape, deleting it should throw an exception
+  ASSERT_THROW(m_catalogue->MediaType()->deleteMediaType(m_tape1.mediaType), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeName) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string newMediaTypeName = "new_media_type";
+  m_catalogue->MediaType()->modifyMediaTypeName(m_admin, m_mediaType.name, newMediaTypeName);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(newMediaTypeName, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeName_nonExistentMediaType) {
+  const std::string currentName = "media_type";
+  const std::string newName = "new_media_type";
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeName(m_admin, currentName, newName), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeName_newNameAlreadyExists) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  auto mediaType2 = m_mediaType;
+  mediaType2.name = "media_type_2";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, mediaType2);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(2, mediaTypes.size());
+
+    const auto mediaTypeMap = mediaTypeWithLogsListToMap(mediaTypes);
+
+    ASSERT_EQ(2, mediaTypeMap.size());
+
+    auto mediaType1Itor = mediaTypeMap.find(m_mediaType.name);
+    ASSERT_TRUE(mediaType1Itor != mediaTypeMap.end());
+
+    ASSERT_EQ(m_mediaType.name, mediaType1Itor->second.name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaType1Itor->second.cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaType1Itor->second.capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaType1Itor->second.primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaType1Itor->second.secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaType1Itor->second.nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaType1Itor->second.minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaType1Itor->second.maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaType1Itor->second.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog1 = mediaType1Itor->second.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog1.username);
+    ASSERT_EQ(m_admin.host, creationLog1.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog1 = mediaType1Itor->second.lastModificationLog;
+    ASSERT_EQ(creationLog1, lastModificationLog1);
+
+    auto mediaType2Itor = mediaTypeMap.find(mediaType2.name);
+    ASSERT_TRUE(mediaType2Itor != mediaTypeMap.end());
+
+    ASSERT_EQ(mediaType2.name, mediaType2Itor->second.name);
+    ASSERT_EQ(mediaType2.cartridge, mediaType2Itor->second.cartridge);
+    ASSERT_EQ(mediaType2.capacityInBytes, mediaType2Itor->second.capacityInBytes);
+    ASSERT_EQ(mediaType2.primaryDensityCode, mediaType2Itor->second.primaryDensityCode);
+    ASSERT_EQ(mediaType2.secondaryDensityCode, mediaType2Itor->second.secondaryDensityCode);
+    ASSERT_EQ(mediaType2.nbWraps, mediaType2Itor->second.nbWraps);
+    ASSERT_EQ(mediaType2.minLPos, mediaType2Itor->second.minLPos);
+    ASSERT_EQ(mediaType2.maxLPos, mediaType2Itor->second.maxLPos);
+    ASSERT_EQ(mediaType2.comment, mediaType2Itor->second.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog2 = mediaType2Itor->second.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog2.username);
+    ASSERT_EQ(m_admin.host, creationLog2.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog2 = mediaType2Itor->second.lastModificationLog;
+    ASSERT_EQ(creationLog2, lastModificationLog2);
+  }
+
+  // Try to rename the first media type with the name of the second one
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeName(m_admin, m_mediaType.name, mediaType2.name),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeCartridge) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedCartridge = "new_cartridge";
+  m_catalogue->MediaType()->modifyMediaTypeCartridge(m_admin, m_mediaType.name, modifiedCartridge);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(modifiedCartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeCartridge_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const std::string cartridge = "cartride";
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeCartridge(m_admin, name, cartridge), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeCapacityInBytes) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedCapacityInBytes = m_mediaType.capacityInBytes + 7;
+  m_catalogue->MediaType()->modifyMediaTypeCapacityInBytes(m_admin, m_mediaType.name, modifiedCapacityInBytes);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(modifiedCapacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeCapacityInBytes_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const uint64_t capacityInBytes = 1;
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeCapacityInBytes(m_admin, name, capacityInBytes),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypePrimaryDensityCode) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint8_t modifiedPrimaryDensityCode = 7;
+  m_catalogue->MediaType()->modifyMediaTypePrimaryDensityCode(m_admin, m_mediaType.name, modifiedPrimaryDensityCode);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(modifiedPrimaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypePrimaryDensityCode_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const uint8_t primaryDensityCode = 1;
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypePrimaryDensityCode(m_admin, name, primaryDensityCode),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeSecondaryDensityCode) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint8_t modifiedSecondaryDensityCode = 7;
+  m_catalogue->MediaType()->modifyMediaTypeSecondaryDensityCode(m_admin, m_mediaType.name, modifiedSecondaryDensityCode);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(modifiedSecondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeSecondaryDensityCode_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const uint8_t secondaryDensityCode = 1;
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeSecondaryDensityCode(m_admin, name, secondaryDensityCode),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeNbWraps) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint32_t modifiedNbWraps = 7;
+  m_catalogue->MediaType()->modifyMediaTypeNbWraps(m_admin, m_mediaType.name, modifiedNbWraps);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(modifiedNbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeNbWraps_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const uint32_t nbWraps = 1;
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeNbWraps(m_admin, name, nbWraps), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeMinLPos) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedMinLPos = 7;
+  m_catalogue->MediaType()->modifyMediaTypeMinLPos(m_admin, m_mediaType.name, modifiedMinLPos);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(modifiedMinLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeMinLPos_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const uint64_t minLPos = 1;
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeMinLPos(m_admin, name, minLPos), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeMaxLPos) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedMaxLPos = 7;
+  m_catalogue->MediaType()->modifyMediaTypeMaxLPos(m_admin, m_mediaType.name, modifiedMaxLPos);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(modifiedMaxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeMaxLPos_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const uint64_t maxLPos = 1;
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeMaxLPos(m_admin, name, maxLPos), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeComment) {
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(m_mediaType.comment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = mediaTypes.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->MediaType()->modifyMediaTypeComment(m_admin, m_mediaType.name, modifiedComment);
+
+  {
+    const auto mediaTypes = m_catalogue->MediaType()->getMediaTypes();
+
+    ASSERT_EQ(1, mediaTypes.size());
+
+    ASSERT_EQ(m_mediaType.name, mediaTypes.front().name);
+    ASSERT_EQ(m_mediaType.cartridge, mediaTypes.front().cartridge);
+    ASSERT_EQ(m_mediaType.capacityInBytes, mediaTypes.front().capacityInBytes);
+    ASSERT_EQ(m_mediaType.primaryDensityCode, mediaTypes.front().primaryDensityCode);
+    ASSERT_EQ(m_mediaType.secondaryDensityCode, mediaTypes.front().secondaryDensityCode);
+    ASSERT_EQ(m_mediaType.nbWraps, mediaTypes.front().nbWraps);
+    ASSERT_EQ(m_mediaType.minLPos, mediaTypes.front().minLPos);
+    ASSERT_EQ(m_mediaType.maxLPos, mediaTypes.front().maxLPos);
+    ASSERT_EQ(modifiedComment, mediaTypes.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mediaTypes.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, modifyMediaTypeComment_nonExistentMediaType) {
+  const std::string name = "media_type";
+  const std::string comment = "Comment";
+  ASSERT_THROW(m_catalogue->MediaType()->modifyMediaTypeComment(m_admin, name, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, getMediaTypeByVid_nonExistentTape) {
+  ASSERT_THROW(m_catalogue->MediaType()->getMediaTypeByVid("DOES_NOT_EXIST"), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_MediaTypeTest, getMediaTypeByVid) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  auto tapeMediaType = m_catalogue->MediaType()->getMediaTypeByVid(m_tape1.vid);
+  ASSERT_EQ(m_mediaType.name, tapeMediaType.name);
+  ASSERT_EQ(m_mediaType.capacityInBytes, tapeMediaType.capacityInBytes);
+  ASSERT_EQ(m_mediaType.cartridge, tapeMediaType.cartridge);
+  ASSERT_EQ(m_mediaType.comment, tapeMediaType.comment);
+  ASSERT_EQ(m_mediaType.maxLPos, tapeMediaType.maxLPos);
+  ASSERT_EQ(m_mediaType.minLPos, tapeMediaType.minLPos);
+  ASSERT_EQ(m_mediaType.nbWraps, tapeMediaType.nbWraps);
+  ASSERT_EQ(m_mediaType.primaryDensityCode, tapeMediaType.primaryDensityCode);
+  ASSERT_EQ(m_mediaType.secondaryDensityCode, tapeMediaType.secondaryDensityCode);
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/MediaTypeCatalogueTest.hpp b/catalogue/tests/modules/MediaTypeCatalogueTest.hpp
new file mode 100644
index 0000000000..a56c414a57
--- /dev/null
+++ b/catalogue/tests/modules/MediaTypeCatalogueTest.hpp
@@ -0,0 +1,65 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <map>
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_MediaTypeTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_MediaTypeTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+
+  /**
+   * Creates a map from tape meida type name to tape media type from the
+   * specified list of tape media types.
+   *
+   * @param listOfMediaTypes The list of tape media types.
+   * @return Map from tape media type name to tape media type.
+   */
+  std::map<std::string, cta::catalogue::MediaTypeWithLogs> mediaTypeWithLogsListToMap(
+    const std::list<cta::catalogue::MediaTypeWithLogs> &listOfMediaTypes);
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/MountPolicyCatalogueTest.cpp b/catalogue/tests/modules/MountPolicyCatalogueTest.cpp
new file mode 100644
index 0000000000..06fd8f3182
--- /dev/null
+++ b/catalogue/tests/modules/MountPolicyCatalogueTest.cpp
@@ -0,0 +1,318 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/MountPolicyCatalogueTest.hpp"
+#include "common/dataStructures/MountPolicy.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_MountPolicyTest::cta_catalogue_MountPolicyTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin("admin", "host") {
+}
+
+void cta_catalogue_MountPolicyTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_MountPolicyTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, createMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  cta::catalogue::CreateMountPolicyAttributes mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin, mountPolicyToAdd);
+
+  const std::list<cta::common::dataStructures::MountPolicy> mountPolicies =
+    m_catalogue->MountPolicy()->getMountPolicies();
+
+  ASSERT_EQ(1, mountPolicies.size());
+
+  const cta::common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+  ASSERT_EQ(mountPolicyName, mountPolicy.name);
+
+  ASSERT_EQ(mountPolicyToAdd.archivePriority, mountPolicy.archivePriority);
+  ASSERT_EQ(mountPolicyToAdd.minArchiveRequestAge, mountPolicy.archiveMinRequestAge);
+
+  ASSERT_EQ(mountPolicyToAdd.retrievePriority, mountPolicy.retrievePriority);
+  ASSERT_EQ(mountPolicyToAdd.minRetrieveRequestAge, mountPolicy.retrieveMinRequestAge);
+
+  ASSERT_EQ(mountPolicyToAdd.comment, mountPolicy.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = mountPolicy.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog =
+    mountPolicy.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, createMountPolicy_same_twice) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicy =CatalogueTestUtils::getMountPolicy1();
+
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicy);
+
+  ASSERT_THROW(m_catalogue->MountPolicy()->createMountPolicy(m_admin, mountPolicy),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, deleteMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const auto mountPolicies = m_catalogue->MountPolicy()->getMountPolicies();
+
+  ASSERT_EQ(1, mountPolicies.size());
+
+  m_catalogue->MountPolicy()->deleteMountPolicy(mountPolicyName);
+
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, deleteMountPolicy_non_existent) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+  ASSERT_THROW(m_catalogue->MountPolicy()->deleteMountPolicy("non_existent_mount_policy"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, getMountPolicyByName) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  cta::catalogue::CreateMountPolicyAttributes mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin, mountPolicyToAdd);
+  {
+    const std::optional<cta::common::dataStructures::MountPolicy> mountPolicyOpt =
+      m_catalogue->MountPolicy()->getMountPolicy(mountPolicyName);
+
+    ASSERT_TRUE(static_cast<bool>(mountPolicyOpt));
+
+    const cta::common::dataStructures::MountPolicy mountPolicy = *mountPolicyOpt;
+
+    ASSERT_EQ(mountPolicyName, mountPolicy.name);
+
+    ASSERT_EQ(mountPolicyToAdd.archivePriority, mountPolicy.archivePriority);
+    ASSERT_EQ(mountPolicyToAdd.minArchiveRequestAge, mountPolicy.archiveMinRequestAge);
+
+    ASSERT_EQ(mountPolicyToAdd.retrievePriority, mountPolicy.retrievePriority);
+    ASSERT_EQ(mountPolicyToAdd.minRetrieveRequestAge, mountPolicy.retrieveMinRequestAge);
+
+    ASSERT_EQ(mountPolicyToAdd.comment, mountPolicy.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = mountPolicy.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      mountPolicy.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    //non existant name
+    const std::optional<cta::common::dataStructures::MountPolicy> mountPolicyOpt =
+      m_catalogue->MountPolicy()->getMountPolicy("non existant mount policy");
+    ASSERT_FALSE(static_cast<bool>(mountPolicyOpt));
+  }
+}
+
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyArchivePriority) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const uint64_t modifiedArchivePriority = mountPolicyToAdd.archivePriority + 10;
+  m_catalogue->MountPolicy()->modifyMountPolicyArchivePriority(m_admin, mountPolicyToAdd.name, modifiedArchivePriority);
+
+  {
+    const auto mountPolicies = m_catalogue->MountPolicy()->getMountPolicies();
+    ASSERT_EQ(1, mountPolicies.size());
+
+    const cta::common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+    ASSERT_EQ(modifiedArchivePriority, mountPolicy.archivePriority);
+
+    const cta::common::dataStructures::EntryLog modificationLog = mountPolicy.lastModificationLog;
+    ASSERT_EQ(m_admin.username, modificationLog.username);
+    ASSERT_EQ(m_admin.host, modificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyArchivePriority_nonExistentMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  const std::string name = "mount_policy";
+  const uint64_t archivePriority = 1;
+
+  ASSERT_THROW(m_catalogue->MountPolicy()->modifyMountPolicyArchivePriority(m_admin, name, archivePriority),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyArchiveMinRequestAge) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const uint64_t modifiedMinArchiveRequestAge = mountPolicyToAdd.minArchiveRequestAge + 10;
+  m_catalogue->MountPolicy()->modifyMountPolicyArchiveMinRequestAge(m_admin, mountPolicyToAdd.name,
+    modifiedMinArchiveRequestAge);
+
+  {
+    const auto mountPolicies = m_catalogue->MountPolicy()->getMountPolicies();
+    ASSERT_EQ(1, mountPolicies.size());
+
+    const cta::common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+    ASSERT_EQ(modifiedMinArchiveRequestAge, mountPolicy.archiveMinRequestAge);
+
+    const cta::common::dataStructures::EntryLog modificationLog = mountPolicy.lastModificationLog;
+    ASSERT_EQ(m_admin.username, modificationLog.username);
+    ASSERT_EQ(m_admin.host, modificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyArchiveMinRequestAge_nonExistentMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  const std::string name = "mount_policy";
+  const uint64_t minArchiveRequestAge = 2;
+
+  ASSERT_THROW(m_catalogue->MountPolicy()->modifyMountPolicyArchiveMinRequestAge(m_admin, name, minArchiveRequestAge),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyRetrievePriority) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const uint64_t modifiedRetrievePriority = mountPolicyToAdd.retrievePriority + 10;
+  m_catalogue->MountPolicy()->modifyMountPolicyRetrievePriority(m_admin, mountPolicyToAdd.name,
+    modifiedRetrievePriority);
+
+  {
+    const auto mountPolicies = m_catalogue->MountPolicy()->getMountPolicies();
+    ASSERT_EQ(1, mountPolicies.size());
+
+    const cta::common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+    ASSERT_EQ(modifiedRetrievePriority, mountPolicy.retrievePriority);
+
+    const cta::common::dataStructures::EntryLog modificationLog = mountPolicy.lastModificationLog;
+    ASSERT_EQ(m_admin.username, modificationLog.username);
+    ASSERT_EQ(m_admin.host, modificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyRetrievePriority_nonExistentMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  const std::string name = "mount_policy";
+  const uint64_t retrievePriority = 1;
+
+  ASSERT_THROW(m_catalogue->MountPolicy()->modifyMountPolicyRetrievePriority(m_admin, name, retrievePriority),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyRetrieveMinRequestAge) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const uint64_t modifiedMinRetrieveRequestAge = mountPolicyToAdd.minRetrieveRequestAge + 10;
+  m_catalogue->MountPolicy()->modifyMountPolicyRetrieveMinRequestAge(m_admin, mountPolicyToAdd.name,
+    modifiedMinRetrieveRequestAge);
+
+  {
+    const auto mountPolicies = m_catalogue->MountPolicy()->getMountPolicies();
+    ASSERT_EQ(1, mountPolicies.size());
+
+    const cta::common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+    ASSERT_EQ(modifiedMinRetrieveRequestAge, mountPolicy.retrieveMinRequestAge);
+
+    const cta::common::dataStructures::EntryLog modificationLog = mountPolicy.lastModificationLog;
+    ASSERT_EQ(m_admin.username, modificationLog.username);
+    ASSERT_EQ(m_admin.host, modificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyRetrieveMinRequestAge_nonExistentMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  const std::string name = "mount_policy";
+  const uint64_t minRetrieveRequestAge = 2;
+
+  ASSERT_THROW(m_catalogue->MountPolicy()->modifyMountPolicyRetrieveMinRequestAge(m_admin, name, minRetrieveRequestAge),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyComment) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->MountPolicy()->modifyMountPolicyComment(m_admin, mountPolicyToAdd.name, modifiedComment);
+
+  {
+    const auto mountPolicies = m_catalogue->MountPolicy()->getMountPolicies();
+    ASSERT_EQ(1, mountPolicies.size());
+
+    const cta::common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+    ASSERT_EQ(modifiedComment, mountPolicy.comment);
+
+    const cta::common::dataStructures::EntryLog modificationLog = mountPolicy.lastModificationLog;
+    ASSERT_EQ(m_admin.username, modificationLog.username);
+    ASSERT_EQ(m_admin.host, modificationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_MountPolicyTest, modifyMountPolicyComment_nonExistentMountPolicy) {
+  ASSERT_TRUE(m_catalogue->MountPolicy()->getMountPolicies().empty());
+
+  const std::string name = "mount_policy";
+  const std::string comment = "Comment";
+
+  ASSERT_THROW(m_catalogue->MountPolicy()->modifyMountPolicyComment(m_admin, name, comment), cta::exception::UserError);
+}
+
+} // namespace unitTests
diff --git a/catalogue/tests/modules/MountPolicyCatalogueTest.hpp b/catalogue/tests/modules/MountPolicyCatalogueTest.hpp
new file mode 100644
index 0000000000..3e3d9af4ab
--- /dev/null
+++ b/catalogue/tests/modules/MountPolicyCatalogueTest.hpp
@@ -0,0 +1,45 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_MountPolicyTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_MountPolicyTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/RequesterActivityMountRuleTest.cpp b/catalogue/tests/modules/RequesterActivityMountRuleTest.cpp
new file mode 100644
index 0000000000..87eae6cf8c
--- /dev/null
+++ b/catalogue/tests/modules/RequesterActivityMountRuleTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/RequesterActivityMountRuleTest.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_RequesterActivityMountRuleTest::cta_catalogue_RequesterActivityMountRuleTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()) {
+}
+
+void cta_catalogue_RequesterActivityMountRuleTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_RequesterActivityMountRuleTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, createRequesterActivityMountRule) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity_regex";
+  m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment);
+
+  const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterActivityMountRule rule = rules.front();
+
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(activityRegex, rule.activityRegex);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+  ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, createRequesterActivityMountRule_same_twice) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity_regex";
+  m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment);
+
+  ASSERT_THROW(m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, createRequesterActivityMountRule_non_existent_mount_policy) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string mountPolicyName = "non_existent_mount_policy";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity_regex";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  ASSERT_THROW( m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, createRequesterActivityMountRule_non_existent_disk_instance) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity_regex";
+  ASSERT_THROW( m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment), cta::exception::UserError);
+}
+
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, deleteRequesterActivityMountRule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity_regex";
+  m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment);
+
+  const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  m_catalogue->RequesterActivityMountRule()->deleteRequesterActivityMountRule(m_diskInstance.name, requesterName,
+    activityRegex);
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, deleteRequesterActivityMountRule_non_existent) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+  ASSERT_THROW(m_catalogue->RequesterActivityMountRule()->deleteRequesterActivityMountRule("non_existent_disk_instance",
+    "non_existent_requester", "non_existrnt_activity"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, modifyRequesterActivityMountRulePolicy) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string anotherMountPolicyName = "another_mount_policy";
+
+  auto anotherMountPolicy = CatalogueTestUtils::getMountPolicy1();
+  anotherMountPolicy.name = anotherMountPolicyName;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,anotherMountPolicy);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity";
+  m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment);
+
+  {
+    const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterActivityMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(activityRegex, rule.activityRegex);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+
+  m_catalogue->RequesterActivityMountRule()->modifyRequesterActivityMountRulePolicy(m_admin, m_diskInstance.name,
+    requesterName, activityRegex, anotherMountPolicyName);
+
+  {
+    const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterActivityMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(anotherMountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(activityRegex, rule.activityRegex);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, modifyRequesterActivityMountRulePolicy_nonExistentRequesterActivity) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity";
+
+  ASSERT_THROW(m_catalogue->RequesterActivityMountRule()->modifyRequesterActivityMountRulePolicy(m_admin,
+    m_diskInstance.name, requesterName, activityRegex, mountPolicyName), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, modifyRequesterActivityMountRuleComment) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  const std::string activityRegex = "activity";
+  m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, activityRegex, comment);
+
+  {
+    const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterActivityMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(activityRegex, rule.activityRegex);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->RequesterActivityMountRule()->modifyRequesterActivityMountRuleComment(m_admin, m_diskInstance.name,
+    requesterName, activityRegex, modifiedComment);
+
+  {
+    const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterActivityMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(modifiedComment, rule.comment);
+    ASSERT_EQ(activityRegex, rule.activityRegex);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+}
+
+TEST_P(cta_catalogue_RequesterActivityMountRuleTest, modifyRequesterMountRuleComment_nonExistentRequesterActivity) {
+  ASSERT_TRUE(m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules().empty());
+
+  const std::string diskInstanceName = "disk_instance";
+  const std::string requesterName = "requester_name";
+  const std::string comment = "Comment";
+  const std::string activityRegex = "activity";
+  ASSERT_THROW(m_catalogue->RequesterActivityMountRule()->modifyRequesterActivityMountRuleComment(m_admin,
+    diskInstanceName, requesterName, activityRegex, comment), cta::exception::UserError);
+}
+
+
+} // namespace unitTests
diff --git a/catalogue/tests/modules/RequesterActivityMountRuleTest.hpp b/catalogue/tests/modules/RequesterActivityMountRuleTest.hpp
new file mode 100644
index 0000000000..6416242223
--- /dev/null
+++ b/catalogue/tests/modules/RequesterActivityMountRuleTest.hpp
@@ -0,0 +1,47 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_RequesterActivityMountRuleTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_RequesterActivityMountRuleTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.cpp b/catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.cpp
new file mode 100644
index 0000000000..288c0c9a2b
--- /dev/null
+++ b/catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.cpp
@@ -0,0 +1,288 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp"
+#include "common/dataStructures/RequesterGroupMountRule.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_RequesterGroupMountRuleTest::cta_catalogue_RequesterGroupMountRuleTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()) {
+}
+
+void cta_catalogue_RequesterGroupMountRuleTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_RequesterGroupMountRuleTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, modifyRequesterGroupMountRulePolicy) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  std::string mountPolicyName = mountPolicyToAdd.name;
+
+  const std::string anotherMountPolicyName = "another_mount_policy";
+
+  auto anotherMountPolicy = CatalogueTestUtils::getMountPolicy1();
+  anotherMountPolicy.name = anotherMountPolicyName;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,anotherMountPolicy);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group_name";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  {
+    const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterGroupName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+    ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  }
+
+  m_catalogue->RequesterGroupMountRule()->modifyRequesterGroupMountRulePolicy(m_admin, diskInstanceName,
+    requesterGroupName, anotherMountPolicyName);
+
+  {
+    const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterGroupName, rule.name);
+    ASSERT_EQ(anotherMountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  }
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, modifyRequesterGroupMountRulePolicy_nonExistentRequester) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group_name";
+
+  ASSERT_THROW(m_catalogue->RequesterGroupMountRule()->modifyRequesterGroupMountRulePolicy(m_admin, diskInstanceName,
+    requesterGroupName, mountPolicyName), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, modifyRequesterGroupMountRuleComment) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group_name";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  {
+    const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterGroupName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+    ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  }
+
+  const std::string modifiedComment = "ModifiedComment";
+  m_catalogue->RequesterGroupMountRule()->modifyRequesterGroupMountRuleComment(m_admin, diskInstanceName,
+    requesterGroupName, modifiedComment);
+
+  {
+    const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterGroupName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(modifiedComment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  }
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, modifyRequesterGroupMountRuleComment_nonExistentRequester) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  const std::string diskInstanceName = "disk_instance";
+  const std::string requesterGroupName = "requester_group_name";
+  const std::string comment  = "Comment";
+
+  ASSERT_THROW(m_catalogue->RequesterGroupMountRule()->modifyRequesterGroupMountRuleComment(m_admin, diskInstanceName,
+    requesterGroupName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, createRequesterGroupMountRule) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  const auto rules =
+    m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+  ASSERT_EQ(requesterGroupName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, createRequesterGroupMountRule_same_twice) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+  ASSERT_THROW(m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName,
+    diskInstanceName, requesterGroupName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, createRequesterGroupMountRule_non_existent_mount_policy) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string mountPolicyName = "non_existent_mount_policy";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  ASSERT_THROW(m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName,
+    diskInstanceName, requesterGroupName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, createRequesterGroupMountRule_non_existent_disk_instance) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+  
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+
+  ASSERT_THROW(m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName,
+    diskInstanceName, requesterGroupName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, deleteRequesterGroupMountRule) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester group";
+  const std::string diskInstanceName = m_diskInstance.name;
+  const std::string requesterGroupName = "requester_group";
+  m_catalogue->RequesterGroupMountRule()->createRequesterGroupMountRule(m_admin, mountPolicyName, diskInstanceName,
+    requesterGroupName, comment);
+
+  const auto rules = m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterGroupMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+  ASSERT_EQ(requesterGroupName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+  ASSERT_EQ(diskInstanceName, rule.diskInstance);
+
+  m_catalogue->RequesterGroupMountRule()->deleteRequesterGroupMountRule(diskInstanceName, requesterGroupName);
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+}
+
+TEST_P(cta_catalogue_RequesterGroupMountRuleTest, deleteRequesterGroupMountRule_non_existent) {
+  ASSERT_TRUE(m_catalogue->RequesterGroupMountRule()->getRequesterGroupMountRules().empty());
+  ASSERT_THROW(m_catalogue->RequesterGroupMountRule()->deleteRequesterGroupMountRule("non_existent_disk_isntance",
+    "non_existent_requester_group"), cta::exception::UserError);
+}
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp b/catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp
new file mode 100644
index 0000000000..dd38240cb2
--- /dev/null
+++ b/catalogue/tests/modules/RequesterGroupMountRuleCatalogueTest.hpp
@@ -0,0 +1,47 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_RequesterGroupMountRuleTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_RequesterGroupMountRuleTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/RequesterMountRuleTest.cpp b/catalogue/tests/modules/RequesterMountRuleTest.cpp
new file mode 100644
index 0000000000..93621f13a9
--- /dev/null
+++ b/catalogue/tests/modules/RequesterMountRuleTest.cpp
@@ -0,0 +1,267 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/RequesterMountRuleTest.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_RequesterMountRuleTest::cta_catalogue_RequesterMountRuleTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()) {
+}
+
+void cta_catalogue_RequesterMountRuleTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_RequesterMountRuleTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, createRequesterMountRule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, m_diskInstance.name,
+    requesterName, comment);
+
+  const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+  ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, createRequesterMountRule_same_twice) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, m_diskInstance.name,
+    requesterName, comment);
+  ASSERT_THROW(m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyToAdd.name,
+    m_diskInstance.name, requesterName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, createRequesterMountRule_non_existent_mount_policy) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string mountPolicyName = "non_existent_mount_policy";
+  const std::string requesterName = "requester_name";
+  ASSERT_THROW(m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, createRequesterMountRule_non_existent_disk_instance) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  ASSERT_THROW(m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName,
+    m_diskInstance.name, requesterName, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, deleteRequesterMountRule) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName,  m_diskInstance.name,
+    requesterName, comment);
+
+  const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  m_catalogue->RequesterMountRule()->deleteRequesterMountRule(m_diskInstance.name, requesterName);
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, deleteRequesterMountRule_non_existent) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+  ASSERT_THROW(m_catalogue->RequesterMountRule()->deleteRequesterMountRule("non_existent_disk_instance",
+    "non_existent_requester"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, modifyRequesterMountRulePolicy) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string anotherMountPolicyName = "another_mount_policy";
+
+  auto anotherMountPolicy =CatalogueTestUtils::getMountPolicy1();
+  anotherMountPolicy.name = anotherMountPolicyName;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,anotherMountPolicy);
+
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, m_diskInstance.name,
+    requesterName, comment);
+
+  {
+    const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+
+  m_catalogue->RequesterMountRule()->modifyRequesterMountRulePolicy(m_admin, m_diskInstance.name, requesterName,
+    anotherMountPolicyName);
+
+  {
+    const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(anotherMountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, modifyRequesterMountRulePolicy_nonExistentRequester) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string requesterName = "requester_name";
+
+  ASSERT_THROW(m_catalogue->RequesterMountRule()->modifyRequesterMountRulePolicy(m_admin, m_diskInstance.name,
+    requesterName, mountPolicyName), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, modifyRequesteMountRuleComment) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  auto mountPolicyToAdd =CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, m_diskInstance.name,
+    requesterName, comment);
+
+  {
+    const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(comment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->RequesterMountRule()->modifyRequesteMountRuleComment(m_admin, m_diskInstance.name, requesterName,
+    modifiedComment);
+
+  {
+    const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+    ASSERT_EQ(1, rules.size());
+
+    const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+    ASSERT_EQ(requesterName, rule.name);
+    ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+    ASSERT_EQ(modifiedComment, rule.comment);
+    ASSERT_EQ(m_admin.username, rule.creationLog.username);
+    ASSERT_EQ(m_admin.host, rule.creationLog.host);
+    ASSERT_EQ(m_diskInstance.name, rule.diskInstance);
+  }
+}
+
+TEST_P(cta_catalogue_RequesterMountRuleTest, modifyRequesteMountRuleComment_nonExistentRequester) {
+  ASSERT_TRUE(m_catalogue->RequesterMountRule()->getRequesterMountRules().empty());
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  const std::string requesterName = "requester_name";
+  const std::string comment = "Comment";
+
+  ASSERT_THROW(m_catalogue->RequesterMountRule()->modifyRequesteMountRuleComment(m_admin,  m_diskInstance.name,
+    requesterName, comment), cta::exception::UserError);
+}
+
+} // namespace unitTests
diff --git a/catalogue/tests/modules/RequesterMountRuleTest.hpp b/catalogue/tests/modules/RequesterMountRuleTest.hpp
new file mode 100644
index 0000000000..eaced6e452
--- /dev/null
+++ b/catalogue/tests/modules/RequesterMountRuleTest.hpp
@@ -0,0 +1,47 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2023 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_RequesterMountRuleTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_RequesterMountRuleTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/SchemaCatalogueTest.cpp b/catalogue/tests/modules/SchemaCatalogueTest.cpp
new file mode 100644
index 0000000000..58e5cf9468
--- /dev/null
+++ b/catalogue/tests/modules/SchemaCatalogueTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/SchemaVersion.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/SchemaCatalogueTest.hpp"
+#include "common/Constants.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_SchemaTest::cta_catalogue_SchemaTest()
+  : m_dummyLog("dummy", "dummy") {
+}
+
+void cta_catalogue_SchemaTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_SchemaTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_SchemaTest, getSchemaVersion) {
+  const auto schemaDbVersion = m_catalogue->Schema()->getSchemaVersion();
+  ASSERT_EQ(static_cast<uint64_t>(CTA_CATALOGUE_SCHEMA_VERSION_MAJOR),
+    schemaDbVersion.getSchemaVersion<cta::catalogue::SchemaVersion::MajorMinor>().first);
+  ASSERT_EQ(static_cast<uint64_t>(CTA_CATALOGUE_SCHEMA_VERSION_MINOR),
+    schemaDbVersion.getSchemaVersion<cta::catalogue::SchemaVersion::MajorMinor>().second);
+}
+
+TEST_P(cta_catalogue_SchemaTest, ping) {
+  m_catalogue->Schema()->ping();
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/SchemaCatalogueTest.hpp b/catalogue/tests/modules/SchemaCatalogueTest.hpp
new file mode 100644
index 0000000000..3f005f30f7
--- /dev/null
+++ b/catalogue/tests/modules/SchemaCatalogueTest.hpp
@@ -0,0 +1,41 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_SchemaTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_SchemaTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/StorageClassCatalogueTest.cpp b/catalogue/tests/modules/StorageClassCatalogueTest.cpp
new file mode 100644
index 0000000000..48a1e35267
--- /dev/null
+++ b/catalogue/tests/modules/StorageClassCatalogueTest.cpp
@@ -0,0 +1,337 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/SchemaVersion.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/StorageClassCatalogueTest.hpp"
+#include "common/Constants.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_StorageClassTest::cta_catalogue_StorageClassTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()) {
+}
+
+void cta_catalogue_StorageClassTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_StorageClassTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_StorageClassTest, createStorageClass) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+  ASSERT_EQ(1, storageClasses.size());
+
+  ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+  ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+  ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+  ASSERT_EQ(m_storageClassSingleCopy.vo.name, storageClasses.front().vo.name);
+
+  const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = storageClasses.front().lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, createStorageClass_same_twice) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+  ASSERT_THROW(m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, createStorageClass_emptyStringStorageClassName) {
+  auto storageClass = m_storageClassSingleCopy;
+  storageClass.name = "";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  ASSERT_THROW(m_catalogue->StorageClass()->createStorageClass(m_admin, storageClass),
+    cta::catalogue::UserSpecifiedAnEmptyStringStorageClassName);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, createStorageClass_emptyStringComment) {
+  auto storageClass = m_storageClassSingleCopy;
+  storageClass.comment = "";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  ASSERT_THROW(m_catalogue->StorageClass()->createStorageClass(m_admin, storageClass),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, createStorageClass_emptyStringVo) {
+  auto storageClass = m_storageClassSingleCopy;
+  storageClass.vo.name = "";
+  ASSERT_THROW(m_catalogue->StorageClass()->createStorageClass(m_admin, storageClass),
+    cta::catalogue::UserSpecifiedAnEmptyStringVo);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, createStorageClass_nonExistingVo) {
+  auto storageClass = m_storageClassSingleCopy;
+  storageClass.vo.name = "NonExistingVO";
+  ASSERT_THROW(m_catalogue->StorageClass()->createStorageClass(m_admin, storageClass), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, deleteStorageClass) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+  ASSERT_EQ(1, storageClasses.size());
+
+  ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+  ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+  ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = storageClasses.front().lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->StorageClass()->deleteStorageClass(m_storageClassSingleCopy.name);
+  ASSERT_TRUE(m_catalogue->StorageClass()->getStorageClasses().empty());
+}
+
+TEST_P(cta_catalogue_StorageClassTest, deleteStorageClass_non_existent) {
+  ASSERT_THROW(m_catalogue->StorageClass()->deleteStorageClass("non_existent_storage_class"),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassNbCopies) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+    ASSERT_EQ(1, storageClasses.size());
+
+
+    ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+    ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+    ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = storageClasses.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedNbCopies = 5;
+  m_catalogue->StorageClass()->modifyStorageClassNbCopies(m_admin, m_storageClassSingleCopy.name, modifiedNbCopies);
+
+  {
+    const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+    ASSERT_EQ(1, storageClasses.size());
+
+
+    ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+    ASSERT_EQ(modifiedNbCopies, storageClasses.front().nbCopies);
+    ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassNbCopies_nonExistentStorageClass) {
+  const std::string storageClassName = "storage_class";
+  const uint64_t nbCopies = 5;
+  ASSERT_THROW(m_catalogue->StorageClass()->modifyStorageClassNbCopies(m_admin, storageClassName, nbCopies),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassComment) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+    ASSERT_EQ(1, storageClasses.size());
+
+
+    ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+    ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+    ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = storageClasses.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->StorageClass()->modifyStorageClassComment(m_admin, m_storageClassSingleCopy.name, modifiedComment);
+
+  {
+    const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+    ASSERT_EQ(1, storageClasses.size());
+
+
+    ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+    ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+    ASSERT_EQ(modifiedComment, storageClasses.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassComment_nonExistentStorageClass) {
+  const std::string storageClassName = "storage_class";
+  const std::string comment = "Comment";
+  ASSERT_THROW(m_catalogue->StorageClass()->modifyStorageClassComment(m_admin, storageClassName, comment),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassName) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+    ASSERT_EQ(1, storageClasses.size());
+
+
+    ASSERT_EQ(m_storageClassSingleCopy.name, storageClasses.front().name);
+    ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+    ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = storageClasses.front().lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string newStorageClassName = "new_storage_class_name";
+  m_catalogue->StorageClass()->modifyStorageClassName(m_admin, m_storageClassSingleCopy.name, newStorageClassName);
+
+  {
+    const auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+
+    ASSERT_EQ(1, storageClasses.size());
+
+
+    ASSERT_EQ(newStorageClassName, storageClasses.front().name);
+    ASSERT_EQ(m_storageClassSingleCopy.nbCopies, storageClasses.front().nbCopies);
+    ASSERT_EQ(m_storageClassSingleCopy.comment, storageClasses.front().comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = storageClasses.front().creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassName_nonExistentStorageClass) {
+  const std::string currentStorageClassName = "storage_class";
+  const std::string newStorageClassName = "new_storage_class";
+  ASSERT_THROW(m_catalogue->StorageClass()->modifyStorageClassName(
+    m_admin, currentStorageClassName, newStorageClassName), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassName_newNameAlreadyExists) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto storageClass2 = m_storageClassSingleCopy;
+  storageClass2.name = "storage_class2";
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, storageClass2);
+
+  //Try to rename the first storage class with the name of the second one
+  ASSERT_THROW(m_catalogue->StorageClass()->modifyStorageClassName(
+    m_admin, m_storageClassSingleCopy.name, storageClass2.name), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassVo) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto newVo = m_vo;
+  newVo.name = "newVo";
+  m_catalogue->VO()->createVirtualOrganization(m_admin, newVo);
+
+  m_catalogue->StorageClass()->modifyStorageClassVo(m_admin, m_storageClassSingleCopy.name, newVo.name);
+
+  auto storageClasses = m_catalogue->StorageClass()->getStorageClasses();
+  ASSERT_EQ(newVo.name, storageClasses.front().vo.name);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassEmptyStringVo) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  ASSERT_THROW(m_catalogue->StorageClass()->modifyStorageClassVo(m_admin, m_storageClassSingleCopy.name, ""),
+    cta::catalogue::UserSpecifiedAnEmptyStringVo);
+}
+
+TEST_P(cta_catalogue_StorageClassTest, modifyStorageClassVoDoesNotExist) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  ASSERT_THROW(m_catalogue->StorageClass()->modifyStorageClassVo(
+    m_admin, m_storageClassSingleCopy.name, "DOES_NOT_EXISTS"), cta::exception::UserError);
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/StorageClassCatalogueTest.hpp b/catalogue/tests/modules/StorageClassCatalogueTest.hpp
new file mode 100644
index 0000000000..e397fca119
--- /dev/null
+++ b/catalogue/tests/modules/StorageClassCatalogueTest.hpp
@@ -0,0 +1,50 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_StorageClassTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_StorageClassTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/TapeCatalogueTest.cpp b/catalogue/tests/modules/TapeCatalogueTest.cpp
new file mode 100644
index 0000000000..d635c4da2d
--- /dev/null
+++ b/catalogue/tests/modules/TapeCatalogueTest.cpp
@@ -0,0 +1,4185 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/rdbms/RdbmsCatalogueUtils.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/TapeForWriting.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/TapeCatalogueTest.hpp"
+#include "common/dataStructures/ArchiveFile.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/RequesterIdentity.hpp"
+#include "common/log/DummyLogger.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_TapeTest::cta_catalogue_TapeTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_tape1(CatalogueTestUtils::getTape1()),
+    m_tape2(CatalogueTestUtils::getTape2()),
+    m_tape3(CatalogueTestUtils::getTape3()) {
+}
+
+void cta_catalogue_TapeTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_TapeTest::TearDown() {
+  m_catalogue.reset();
+}
+
+std::map<std::string, cta::common::dataStructures::Tape> cta_catalogue_TapeTest::tapeListToMap(
+  const std::list<cta::common::dataStructures::Tape> &listOfTapes) {
+  try {
+    std::map<std::string, cta::common::dataStructures::Tape> vidToTape;
+
+    for (auto &tape: listOfTapes) {
+      if(vidToTape.end() != vidToTape.find(tape.vid)) {
+        throw cta::exception::Exception(std::string("Duplicate VID: value=") + tape.vid);
+      }
+      vidToTape[tape.vid] = tape;
+    }
+
+    return vidToTape;
+  } catch(cta::exception::Exception &ex) {
+    throw cta::exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_1_tape_with_write_log_1_tape_without) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string diskInstance = m_diskInstance.name;
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const auto tapes = tapeListToMap(m_catalogue->Tape()->getTapes());
+    ASSERT_EQ(1, tapes.size());
+
+    const auto tapeItor = tapes.find(m_tape1.vid);
+    ASSERT_NE(tapes.end(), tapeItor);
+
+    const cta::common::dataStructures::Tape tape = tapeItor->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  const uint64_t fileSize = 1234 * 1000000000UL;
+  const uint64_t archiveFileId = 1234;
+  const std::string diskFileId = "5678";
+  {
+    auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & file1Written = *file1WrittenUP;
+    std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+    file1WrittenSet.insert(file1WrittenUP.release());
+    file1Written.archiveFileId        = archiveFileId;
+    file1Written.diskInstance         = diskInstance;
+    file1Written.diskFileId           = diskFileId;
+    file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+    file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+    file1Written.size                 = fileSize;
+    file1Written.checksumBlob.insert(cta::checksum::ADLER32, 0x1000); // tests checksum with embedded zeros
+    file1Written.storageClassName     = m_storageClassSingleCopy.name;
+    file1Written.vid                  = m_tape1.vid;
+    file1Written.fSeq                 = 1;
+    file1Written.blockId              = 4321;
+    file1Written.copyNb               = 1;
+    file1Written.tapeDrive            = "tape_drive";
+    m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+  }
+
+  {
+    // Check that a lookup of diskFileId 5678 returns 1 tape
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    std::vector<std::string> diskFileIds;
+    diskFileIds.push_back("5678");
+    searchCriteria.diskFileIds = diskFileIds;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+    ASSERT_EQ(m_tape1.vid, vidToTape.begin()->first);
+    ASSERT_EQ(m_tape1.vid, vidToTape.begin()->second.vid);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(fileSize, pool.dataBytes);
+    ASSERT_EQ(1, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  {
+    const auto tapes = tapeListToMap(m_catalogue->Tape()->getTapes());
+    ASSERT_EQ(2, tapes.size());
+
+    const auto tapeItor = tapes.find(m_tape2.vid);
+    ASSERT_NE(tapes.end(), tapeItor);
+
+    const cta::common::dataStructures::Tape tape = tapeItor->second;
+    ASSERT_EQ(m_tape2.vid, tape.vid);
+    ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape2.vendor, tape.vendor);
+    ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape2.state, tape.state);
+    ASSERT_EQ(m_tape2.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape2.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(2, pool.nbTapes);
+    ASSERT_EQ(2*m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(fileSize, pool.dataBytes);
+    ASSERT_EQ(1, pool.nbPhysicalFiles);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, deleteTape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+  ASSERT_EQ(1, tapes.size());
+
+  const cta::common::dataStructures::Tape tape = tapes.front();
+  ASSERT_EQ(m_tape1.vid, tape.vid);
+  ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+  ASSERT_EQ(m_tape1.vendor, tape.vendor);
+  ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+  ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+  ASSERT_EQ(m_vo.name, tape.vo);
+  ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+  ASSERT_EQ(m_tape1.full, tape.full);
+
+  ASSERT_FALSE(tape.isFromCastor);
+  ASSERT_EQ(m_tape1.comment, tape.comment);
+  ASSERT_FALSE(tape.labelLog);
+  ASSERT_FALSE(tape.lastReadLog);
+  ASSERT_FALSE(tape.lastWriteLog);
+
+  const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->Tape()->deleteTape(tape.vid);
+  ASSERT_TRUE(m_catalogue->Tape()->getTapes().empty());
+}
+
+TEST_P(cta_catalogue_TapeTest, writeToTapeAndCheckMasterBytesAndFiles) {
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(0, tape.nbMasterFiles);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  std::set<cta::catalogue::TapeItemWrittenPointer> fileWrittenSet;
+  {
+    auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & file1Written = *file1WrittenUP;
+    fileWrittenSet.insert(file1WrittenUP.release());
+    file1Written.archiveFileId        = 1234;
+    file1Written.diskInstance         = diskInstance;
+    file1Written.diskFileId           = "5678";
+    file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+    file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+    file1Written.size                 =  1234 * 1000000000UL;
+    file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+    file1Written.storageClassName     = m_storageClassSingleCopy.name;
+    file1Written.vid                  = m_tape1.vid;
+    file1Written.fSeq                 = 2;
+    file1Written.blockId              = 4321;
+    file1Written.copyNb               = 1;
+    file1Written.tapeDrive            = "tape_drive";
+  }
+  {
+    auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & file2Written = *file2WrittenUP;
+    fileWrittenSet.insert(file2WrittenUP.release());
+    file2Written.archiveFileId        = 1235;
+    file2Written.diskInstance         = diskInstance;
+    file2Written.diskFileId           = "5679";
+    file2Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+    file2Written.diskFileGid          = PUBLIC_DISK_GROUP;
+    file2Written.size                 =  1234 * 1000000000UL;
+    file2Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+    file2Written.storageClassName     = m_storageClassSingleCopy.name;
+    file2Written.vid                  = m_tape1.vid;
+    file2Written.fSeq                 = 1;
+    file2Written.blockId              = 8642;
+    file2Written.copyNb               = 1;
+    file2Written.tapeDrive            = "tape_drive";
+  }
+
+  m_catalogue->TapeFile()->filesWrittenToTape(fileWrittenSet);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(2 * 1234 * 1000000000UL, tape.dataOnTapeInBytes);
+    ASSERT_EQ(2 * 1234 * 1000000000UL, tape.masterDataInBytes);
+    ASSERT_EQ(2, tape.nbMasterFiles);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(static_cast<bool>(tape.labelLog));
+    ASSERT_FALSE(static_cast<bool>(tape.lastReadLog));
+    ASSERT_TRUE(static_cast<bool>(tape.lastWriteLog));
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+}
+
+
+TEST_P(cta_catalogue_TapeTest, deleteNonEmptyTape) {
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t fileSize = 1234 * 1000000000UL;
+  const uint64_t archiveFileId = 1234;
+  const std::string diskFileId = "5678";
+  {
+    auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & file1Written = *file1WrittenUP;
+    std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+    file1WrittenSet.insert(file1WrittenUP.release());
+    file1Written.archiveFileId        = archiveFileId;
+    file1Written.diskInstance         = diskInstance;
+    file1Written.diskFileId           = diskFileId;
+    file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+    file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+    file1Written.size                 = fileSize;
+    file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+    file1Written.storageClassName     = m_storageClassSingleCopy.name;
+    file1Written.vid                  = m_tape1.vid;
+    file1Written.fSeq                 = 1;
+    file1Written.blockId              = 4321;
+    file1Written.copyNb               = 1;
+    file1Written.tapeDrive            = "tape_drive";
+    m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(fileSize, tape.dataOnTapeInBytes);
+    ASSERT_EQ(fileSize, tape.masterDataInBytes);
+    ASSERT_EQ(1, tape.nbMasterFiles);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_THROW(m_catalogue->Tape()->deleteTape(m_tape1.vid), cta::catalogue::UserSpecifiedANonEmptyTape);
+  ASSERT_FALSE(m_catalogue->Tape()->getTapes().empty());
+
+  //Put the files on the tape on the recycle log
+  cta::common::dataStructures::DeleteArchiveRequest deletedArchiveReq;
+  deletedArchiveReq.archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+  deletedArchiveReq.diskInstance = diskInstance;
+  deletedArchiveReq.archiveFileID = archiveFileId;
+  deletedArchiveReq.diskFileId = diskFileId;
+  deletedArchiveReq.recycleTime = time(nullptr);
+  deletedArchiveReq.requester = cta::common::dataStructures::RequesterIdentity(m_admin.username,"group");
+  deletedArchiveReq.diskFilePath = "/path/";
+  m_catalogue->ArchiveFile()->moveArchiveFileToRecycleLog(deletedArchiveReq,dummyLc);
+
+  //The ArchiveFilesItor should not have any file in it
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  //The tape should not be deleted
+  ASSERT_THROW(m_catalogue->Tape()->deleteTape(m_tape1.vid), cta::catalogue::UserSpecifiedANonEmptyTape);
+  ASSERT_FALSE(m_catalogue->Tape()->getTapes().empty());
+  m_catalogue->Tape()->setTapeFull(m_admin,m_tape1.vid,true);
+  //Reclaim it to delete the files from the recycle log
+  m_catalogue->Tape()->reclaimTape(m_admin,m_tape1.vid,dummyLc);
+  //Deletion should be successful
+  ASSERT_NO_THROW(m_catalogue->Tape()->deleteTape(m_tape1.vid));
+  ASSERT_TRUE(m_catalogue->Tape()->getTapes().empty());
+}
+
+TEST_P(cta_catalogue_TapeTest, deleteTape_non_existent) {
+  ASSERT_THROW(m_catalogue->Tape()->deleteTape("non_existent_tape"), cta::catalogue::UserSpecifiedANonExistentTape);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeMediaType) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  auto anotherMediaType = m_mediaType;
+  anotherMediaType.name = "another_media_type";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, anotherMediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->modifyTapeMediaType(m_admin, m_tape1.vid, anotherMediaType.name);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(anotherMediaType.name, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeMediaType(m_admin, m_tape1.vid, "DOES NOT EXIST"),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeVendor) {
+  const std::string anotherVendor = "another_vendor";
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->modifyTapeVendor(m_admin, m_tape1.vid, anotherVendor);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(anotherVendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeLogicalLibraryName) {
+  const bool logicalLibraryIsDisabled= false;
+  const std::string anotherLogicalLibraryName = "another_logical_library_name";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, anotherLogicalLibraryName, logicalLibraryIsDisabled,
+    "Create another logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->modifyTapeLogicalLibraryName(m_admin, m_tape1.vid, anotherLogicalLibraryName);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(anotherLogicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeLogicalLibraryName_nonExistentTape) {
+  const bool logicalLibraryIsDisabled= false;
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeLogicalLibraryName(m_admin, m_tape1.vid, m_tape1.logicalLibraryName),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeTapePoolName) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string anotherTapePoolName = "another_tape_pool_name";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, anotherTapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create another tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->modifyTapeTapePoolName(m_admin, m_tape1.vid, anotherTapePoolName);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(anotherTapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeTapePoolName_nonExistentTape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeTapePoolName(m_admin, m_tape1.vid, m_tape1.tapePoolName),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeEncryptionKeyName) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedEncryptionKeyName = "modified_encryption_key_name";
+  m_catalogue->Tape()->modifyTapeEncryptionKeyName(m_admin, m_tape1.vid, modifiedEncryptionKeyName);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(modifiedEncryptionKeyName, tape.encryptionKeyName);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeEncryptionKeyName_emptyStringEncryptionKey) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedEncryptionKeyName;
+  m_catalogue->Tape()->modifyTapeEncryptionKeyName(m_admin, m_tape1.vid, modifiedEncryptionKeyName);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_FALSE((bool)tape.encryptionKeyName);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeVerificationStatus) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+    ASSERT_FALSE(tape.verificationStatus);
+  }
+
+  const std::string modifiedVerificationStatus = "verification_status";
+  m_catalogue->Tape()->modifyTapeVerificationStatus(m_admin, m_tape1.vid, modifiedVerificationStatus);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+    ASSERT_EQ(tape.verificationStatus.value(), modifiedVerificationStatus);
+  }
+
+  // Clear verification status
+  m_catalogue->Tape()->modifyTapeVerificationStatus(m_admin, m_tape1.vid, "");
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_FALSE(tape.verificationStatus);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeEncryptionKeyName_nonExistentTape) {
+  const std::string encryptionKeyName = "encryption_key_name";
+
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeEncryptionKeyName(m_admin, m_tape1.vid, encryptionKeyName),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeState_nonExistentTape) {
+  cta::common::dataStructures::Tape::State state = cta::common::dataStructures::Tape::State::ACTIVE;
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, "DOES_NOT_EXIST", state, std::nullopt, std::nullopt),
+    cta::catalogue::UserSpecifiedANonExistentTape);
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeDisabled_nonExistentTape) {
+  ASSERT_THROW(m_catalogue->Tape()->setTapeDisabled(m_admin, m_tape1.vid, "Test"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeState_nonExistentState) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  cta::common::dataStructures::Tape::State state = (cta::common::dataStructures::Tape::State)42;
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid, state, std::nullopt, std::nullopt),
+    cta::catalogue::UserSpecifiedANonExistentTapeState);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeState_nonExistentPrevState) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  cta::common::dataStructures::Tape::State state = cta::common::dataStructures::Tape::State::ACTIVE;
+  cta::common::dataStructures::Tape::State prevState = (cta::common::dataStructures::Tape::State)42;
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid, state, prevState, std::nullopt),
+    cta::catalogue::UserSpecifiedANonExistentTapeState);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeState_wrongPrevState) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  cta::common::dataStructures::Tape::State prevState = cta::common::dataStructures::Tape::State::ACTIVE;
+  cta::common::dataStructures::Tape::State prevStateGuess = cta::common::dataStructures::Tape::State::REPACKING;
+  cta::common::dataStructures::Tape::State nextState = cta::common::dataStructures::Tape::State::DISABLED;
+  std::string reason = "modify for testing";
+  m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid, prevState, std::nullopt, std::nullopt);
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid,nextState,prevStateGuess,reason),
+    cta::catalogue::UserSpecifiedANonExistentTape);
+}
+
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeState_noReasonWhenNotActive) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  std::string reason = "";
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid,
+    cta::common::dataStructures::Tape::State::BROKEN, std::nullopt, reason),
+    cta::catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
+
+  ASSERT_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid,
+    cta::common::dataStructures::Tape::State::DISABLED,std::nullopt,std::nullopt),
+    cta::catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeState) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  std::string reason = "tape broken";
+
+  std::string vid = m_tape1.vid;
+  ASSERT_NO_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, vid, cta::common::dataStructures::Tape::State::BROKEN,
+    std::nullopt,reason));
+
+  {
+    //catalogue getTapesByVid test (single VID)
+    auto vidToTapeMap = m_catalogue->Tape()->getTapesByVid(vid);
+    auto tape = vidToTapeMap.at(vid);
+    ASSERT_EQ(vid,tape.vid);
+    ASSERT_EQ(cta::common::dataStructures::Tape::BROKEN,tape.state);
+    ASSERT_EQ(reason,tape.stateReason);
+    ASSERT_EQ(cta::catalogue::RdbmsCatalogueUtils::generateTapeStateModifiedBy(m_admin),tape.stateModifiedBy);
+    ASSERT_NE(0,tape.stateUpdateTime);
+  }
+
+  {
+    //Get tape by search criteria test
+    cta::catalogue::TapeSearchCriteria criteria;
+    criteria.vid = vid;
+    auto tapes = m_catalogue->Tape()->getTapes(criteria);
+    auto tape = tapes.front();
+    ASSERT_EQ(vid,tape.vid);
+    ASSERT_EQ(cta::common::dataStructures::Tape::BROKEN,tape.state);
+    ASSERT_EQ(reason,tape.stateReason);
+    ASSERT_EQ(cta::catalogue::RdbmsCatalogueUtils::generateTapeStateModifiedBy(m_admin),tape.stateModifiedBy);
+    ASSERT_NE(0,tape.stateUpdateTime);
+  }
+
+  {
+    //catalogue getTapesByVid test (set of VIDs)
+    std::set<std::string> vids = {vid};
+    auto vidToTapeMap = m_catalogue->Tape()->getTapesByVid(vids);
+    auto tape = vidToTapeMap.at(vid);
+    ASSERT_EQ(vid,tape.vid);
+    ASSERT_EQ(cta::common::dataStructures::Tape::BROKEN,tape.state);
+    ASSERT_EQ(reason,tape.stateReason);
+    ASSERT_EQ(cta::catalogue::RdbmsCatalogueUtils::generateTapeStateModifiedBy(m_admin),tape.stateModifiedBy);
+    ASSERT_NE(0,tape.stateUpdateTime);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, modifyTapeStateResetReasonWhenBackToActiveState) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  std::string vid = m_tape1.vid;
+
+  std::string reason = "Broken tape";
+  ASSERT_NO_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, vid, cta::common::dataStructures::Tape::State::BROKEN,
+    std::nullopt,reason));
+
+  ASSERT_NO_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, vid, cta::common::dataStructures::Tape::State::ACTIVE,
+    std::nullopt, std::nullopt));
+
+  {
+    auto vidToTapeMap = m_catalogue->Tape()->getTapesByVid(vid);
+    auto tape = vidToTapeMap.at(vid);
+    ASSERT_EQ(vid,tape.vid);
+    ASSERT_EQ(cta::common::dataStructures::Tape::ACTIVE,tape.state);
+    ASSERT_FALSE(tape.stateReason);
+    ASSERT_EQ(cta::catalogue::RdbmsCatalogueUtils::generateTapeStateModifiedBy(m_admin),tape.stateModifiedBy);
+    ASSERT_NE(0,tape.stateUpdateTime);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesSearchCriteriaByState) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  std::string vidTape1 = m_tape1.vid;
+  std::string vidTape2 = m_tape2.vid;
+
+  {
+    cta::catalogue::TapeSearchCriteria criteria;
+    criteria.state = cta::common::dataStructures::Tape::ACTIVE;
+    auto tapes = m_catalogue->Tape()->getTapes(criteria);
+    ASSERT_EQ(2,tapes.size());
+    auto tape = tapes.front();
+    ASSERT_EQ(vidTape1,tape.vid);
+    ASSERT_EQ(cta::common::dataStructures::Tape::ACTIVE,tape.state);
+    ASSERT_FALSE(tape.stateReason);
+    ASSERT_EQ(m_admin.username + "@" + m_admin.host,tape.stateModifiedBy);
+    ASSERT_NE(0,tape.stateUpdateTime);
+  }
+
+  std::string reason = "Broken tape";
+  ASSERT_NO_THROW(m_catalogue->Tape()->modifyTapeState(m_admin, vidTape1,
+    cta::common::dataStructures::Tape::State::BROKEN, std::nullopt, reason));
+
+  {
+    cta::catalogue::TapeSearchCriteria criteria;
+    criteria.state = cta::common::dataStructures::Tape::ACTIVE;
+    auto tapes = m_catalogue->Tape()->getTapes(criteria);
+    ASSERT_EQ(1,tapes.size());
+    auto tape = tapes.front();
+    //The tape 2 is ACTIVE so this is the one we expect
+    ASSERT_EQ(vidTape2,tape.vid);
+  }
+  {
+    cta::catalogue::TapeSearchCriteria criteria;
+    criteria.state = cta::common::dataStructures::Tape::BROKEN;
+    auto tapes = m_catalogue->Tape()->getTapes(criteria);
+    ASSERT_EQ(1,tapes.size());
+    auto tape = tapes.front();
+    //The tape 2 is ACTIVE so this is the one we expect
+    ASSERT_EQ(vidTape1,tape.vid);
+    ASSERT_EQ(cta::common::dataStructures::Tape::BROKEN,tape.state);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, tapeLabelled) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string labelDrive = "labelling_drive";
+  m_catalogue->Tape()->tapeLabelled(m_tape1.vid, labelDrive);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_TRUE((bool)tape.labelLog);
+    ASSERT_EQ(labelDrive, tape.labelLog.value().drive);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, tapeLabelled_nonExistentTape) {
+  const std::string labelDrive = "drive";
+
+  ASSERT_THROW(m_catalogue->Tape()->tapeLabelled(m_tape1.vid, labelDrive), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, tapeMountedForArchive) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(0, tape.readMountCount);
+    ASSERT_EQ(0, tape.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedDrive = "modified_drive";
+  m_catalogue->Tape()->tapeMountedForArchive(m_tape1.vid, modifiedDrive);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(0, tape.readMountCount);
+    ASSERT_EQ(1, tape.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+    ASSERT_EQ(modifiedDrive, tape.lastWriteLog.value().drive);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  for(int i=1; i<1024; i++) {
+    m_catalogue->Tape()->tapeMountedForArchive(m_tape1.vid, modifiedDrive);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(0, tape.readMountCount);
+    ASSERT_EQ(1024, tape.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+    ASSERT_EQ(modifiedDrive, tape.lastWriteLog.value().drive);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, tapeMountedForArchive_nonExistentTape) {
+  const std::string drive = "drive";
+
+  ASSERT_THROW(m_catalogue->Tape()->tapeMountedForArchive(m_tape1.vid, drive), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, tapeMountedForRetrieve) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(0, tape.readMountCount);
+    ASSERT_EQ(0, tape.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedDrive = "modified_drive";
+  m_catalogue->Tape()->tapeMountedForRetrieve(m_tape1.vid, modifiedDrive);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(1, tape.readMountCount);
+    ASSERT_EQ(0, tape.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_TRUE((bool)tape.lastReadLog);
+    ASSERT_EQ(modifiedDrive, tape.lastReadLog.value().drive);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  for(int i=1; i<1024; i++) {
+    m_catalogue->Tape()->tapeMountedForRetrieve(m_tape1.vid, modifiedDrive);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(1024, tape.readMountCount);
+    ASSERT_EQ(0, tape.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_TRUE((bool)tape.lastReadLog);
+    ASSERT_EQ(modifiedDrive, tape.lastReadLog.value().drive);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, tapeMountedForRetrieve_nonExistentTape) {
+  const std::string drive = "drive";
+
+  ASSERT_THROW(m_catalogue->Tape()->tapeMountedForRetrieve(m_tape1.vid, drive), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeFull) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->setTapeFull(m_admin, m_tape1.vid, true);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_TRUE(tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeFull_nonExistentTape) {
+  ASSERT_THROW(m_catalogue->Tape()->setTapeFull(m_admin, m_tape1.vid, true), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeDirty) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+    ASSERT_TRUE(tape.dirty);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->setTapeDirty(m_admin, m_tape1.vid, false);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+    ASSERT_FALSE(tape.dirty);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeDirty_nonExistentTape) {
+  ASSERT_THROW(m_catalogue->Tape()->setTapeDirty(m_admin, m_tape1.vid, true), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, noSpaceLeftOnTape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->noSpaceLeftOnTape(m_tape1.vid);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_TRUE(tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, noSpaceLeftOnTape_nonExistentTape) {
+  ASSERT_THROW(m_catalogue->Tape()->noSpaceLeftOnTape(m_tape1.vid), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeIsFromCastorInUnitTests) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->setTapeIsFromCastorInUnitTests(m_tape1.vid);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_TRUE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  // do it twice
+  m_catalogue->Tape()->setTapeIsFromCastorInUnitTests(m_tape1.vid);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_TRUE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, setTapeIsFromCastor_nonExistentTape) {
+  ASSERT_THROW(m_catalogue->Tape()->setTapeIsFromCastorInUnitTests(m_tape1.vid), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesForWriting) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  m_catalogue->Tape()->tapeLabelled(m_tape1.vid, "tape_drive");
+
+  const auto tapes = m_catalogue->Tape()->getTapesForWriting(m_tape1.logicalLibraryName);
+
+  ASSERT_EQ(1, tapes.size());
+
+  const cta::catalogue::TapeForWriting tape = tapes.front();
+  ASSERT_EQ(m_tape1.vid, tape.vid);
+  ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+  ASSERT_EQ(m_tape1.vendor, tape.vendor);
+  ASSERT_EQ(m_tape1.tapePoolName, tape.tapePool);
+  ASSERT_EQ(m_vo.name, tape.vo);
+  ASSERT_EQ(0, tape.lastFSeq);
+  ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+  ASSERT_EQ(0, tape.dataOnTapeInBytes);
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapeLabelFormat) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  // Get Tape
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+  ASSERT_EQ(1, tapes.size());
+  const cta::common::dataStructures::Tape tape = tapes.front();
+  ASSERT_EQ(m_tape1.vid, tape.vid);
+
+  // Get label format and compare
+  const auto labelFormat = m_catalogue->Tape()->getTapeLabelFormat(m_tape1.vid);
+  ASSERT_EQ(tape.labelFormat, labelFormat);
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesForWritingOrderedByDataInBytesDesc) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+
+  m_catalogue->Tape()->tapeLabelled(m_tape1.vid, "tape_drive");
+
+  const auto tapes = m_catalogue->Tape()->getTapesForWriting(m_tape1.logicalLibraryName);
+
+  ASSERT_EQ(1, tapes.size());
+
+  const cta::catalogue::TapeForWriting tape = tapes.front();
+  ASSERT_EQ(m_tape1.vid, tape.vid);
+  ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+  ASSERT_EQ(m_tape1.vendor, tape.vendor);
+  ASSERT_EQ(m_tape1.tapePoolName, tape.tapePool);
+  ASSERT_EQ(m_vo.name, tape.vo);
+  ASSERT_EQ(0, tape.lastFSeq);
+  ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+  ASSERT_EQ(0, tape.dataOnTapeInBytes);
+
+  //Create a tape and insert a file in it
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+  m_catalogue->Tape()->tapeLabelled(m_tape2.vid, "tape_drive");
+
+  const uint64_t fileSize = 1234 * 1000000000UL;
+  {
+    auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & file1Written = *file1WrittenUP;
+    std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+    file1WrittenSet.insert(file1WrittenUP.release());
+    file1Written.archiveFileId        = 1234;
+    file1Written.diskInstance         = m_diskInstance.name;
+    file1Written.diskFileId           = "5678";
+    file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+    file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+    file1Written.size                 = fileSize;
+    file1Written.checksumBlob.insert(cta::checksum::ADLER32, 0x1000); // tests checksum with embedded zeros
+    file1Written.storageClassName     = m_storageClassSingleCopy.name;
+    file1Written.vid                  = m_tape2.vid;
+    file1Written.fSeq                 = 1;
+    file1Written.blockId              = 4321;
+    file1Written.copyNb               = 1;
+    file1Written.tapeDrive            = "tape_drive";
+    m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+  }
+
+  //The tape m_tape2 should be returned by the Catalogue::Tape()->getTapesForWriting() method
+  ASSERT_EQ(m_tape2.vid,m_catalogue->Tape()->getTapesForWriting(m_tape2.logicalLibraryName).front().vid);
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesForWriting_disabled_tape) {
+  const bool logicalLibraryIsDisabled = false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  auto tape = m_tape1;
+  tape.state = cta::common::dataStructures::Tape::DISABLED;
+  tape.stateReason = "test";
+  m_catalogue->Tape()->createTape(m_admin, tape);
+
+  m_catalogue->Tape()->tapeLabelled(m_tape1.vid, "tape_drive");
+
+  const auto tapes = m_catalogue->Tape()->getTapesForWriting(m_tape1.logicalLibraryName);
+
+  ASSERT_EQ(0, tapes.size());
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesForWriting_full_tape) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  auto tape1 = m_tape1;
+  tape1.full = true;
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+
+  m_catalogue->Tape()->tapeLabelled(tape1.vid, "tape_drive");
+
+  const auto tapes = m_catalogue->Tape()->getTapesForWriting(tape1.logicalLibraryName);
+
+  ASSERT_EQ(0, tapes.size());
+}
+
+TEST_P(cta_catalogue_TapeTest, DISABLED_getTapesForWriting_no_labelled_tapes) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  const auto tapes = m_catalogue->Tape()->getTapesForWriting(m_tape1.logicalLibraryName);
+
+  ASSERT_TRUE(tapes.empty());
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTapeActiveState) {
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->setTapeFull(m_admin, tape1.vid, true);
+
+  // ACTIVE - Reclaim allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::ACTIVE, std::nullopt,
+    "Testing");
+  ASSERT_NO_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc));
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTapeDisabledState) {
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->setTapeFull(m_admin, tape1.vid, true);
+
+  // ACTIVE - Reclaim allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::DISABLED, std::nullopt,
+    "Testing");
+  ASSERT_NO_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc));
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTapeNotAllowedStates) {
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->setTapeFull(m_admin, tape1.vid, true);
+
+  // REPACKING - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::REPACKING, std::nullopt,
+    "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+
+  // REPACKING_DISABLED - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::REPACKING_DISABLED,
+    std::nullopt, "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+
+  // REPACKING_PENDING - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::REPACKING_PENDING,
+    std::nullopt, "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+
+  // BROKEN - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::BROKEN, std::nullopt,
+    "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+
+  // BROKEN_PENDING - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::BROKEN_PENDING,
+    std::nullopt, "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+
+  // EXPORTED - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::EXPORTED, std::nullopt,
+    "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+
+  // EXPORTED_PENDING - Reclaim not allowed
+  m_catalogue->Tape()->modifyTapeState(m_admin, tape1.vid, cta::common::dataStructures::Tape::EXPORTED_PENDING,
+    std::nullopt, "Testing");
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, tape1.vid, dummyLc), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapes_non_existent_tape_pool) {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  const bool logicalLibraryIsDisabled = false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    cta::catalogue::TapeSearchCriteria criteria;
+    criteria.tapePool = "non_existent";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(criteria), cta::catalogue::UserSpecifiedANonExistentTapePool);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_deleteStorageClass) {
+  // TO BE DONE
+}
+
+
+
+TEST_P(cta_catalogue_TapeTest, createTape_emptyStringVid) {
+  const std::string vid = "";
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  {
+    auto tape = m_tape1;
+    tape.vid = "";
+    ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape), cta::catalogue::UserSpecifiedAnEmptyStringVid);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_emptyStringMediaType) {
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  auto tape = m_tape1;
+  tape.mediaType = "";
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape), cta::catalogue::UserSpecifiedAnEmptyStringMediaType);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_emptyStringVendor) {
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+   auto tape = m_tape1;
+   tape.vendor = "";
+   ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape), cta::catalogue::UserSpecifiedAnEmptyStringVendor);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_emptyStringLogicalLibraryName) {
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  auto tape = m_tape1;
+  tape.logicalLibraryName = "";
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape), cta::catalogue::UserSpecifiedAnEmptyStringLogicalLibraryName);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_emptyStringTapePoolName) {
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  auto tape = m_tape1;
+  tape.tapePoolName = "";
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape), cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_non_existent_logical_library) {
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, m_tape1), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_non_existent_tape_pool) {
+  const bool logicalLibraryIsDisabled= false;
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, m_tape1), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_9_exabytes_capacity) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  // The maximum size of an SQLite integer is a signed 64-bit integer
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  const auto tapes = m_catalogue->Tape()->getTapes();
+
+  ASSERT_EQ(1, tapes.size());
+
+  {
+    const auto &tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.state,tape.state);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const auto creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_same_twice) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, m_tape1), cta::exception::UserError);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_StateDoesNotExist) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  auto tape = m_tape1;
+  tape.state = (cta::common::dataStructures::Tape::State)42;
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape),cta::catalogue::UserSpecifiedANonExistentTapeState);
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_StateNotActiveWithoutReasonShouldThrow) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  auto tape1 = m_tape1;
+  tape1.state = cta::common::dataStructures::Tape::DISABLED;
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape1),
+    cta::catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
+
+  auto tape2 = m_tape2;
+  tape2.state = cta::common::dataStructures::Tape::BROKEN;
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape2),
+    cta::catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
+
+  tape2.stateReason = "Tape broken";
+  ASSERT_NO_THROW(m_catalogue->Tape()->createTape(m_admin, tape2));
+
+  auto tape3 = m_tape3;
+  tape3.state = cta::common::dataStructures::Tape::EXPORTED;
+  ASSERT_THROW(m_catalogue->Tape()->createTape(m_admin, tape3),
+    cta::catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive);
+
+  tape3.stateReason = "Tape exported";
+  ASSERT_NO_THROW(m_catalogue->Tape()->createTape(m_admin, tape3));
+}
+
+TEST_P(cta_catalogue_TapeTest, createTape_many_tapes) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  const uint64_t nbTapes = 10;
+
+  // Effectively clone the tapes from m_tape1 but give each one its own VID
+  for(uint64_t i = 1; i <= nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "vid" << i;
+
+    auto tape = m_tape1;
+    tape.vid = vid.str();
+    m_catalogue->Tape()->createTape(m_admin, tape);
+
+    {
+      const auto pools = m_catalogue->TapePool()->getTapePools();
+      ASSERT_EQ(1, pools.size());
+
+      const auto &pool = pools.front();
+      ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+      ASSERT_EQ(m_vo.name, pool.vo.name);
+      ASSERT_EQ(i, pool.nbTapes);
+      ASSERT_EQ(i * m_mediaType.capacityInBytes, pool.capacityBytes);
+      ASSERT_EQ(0, pool.dataBytes);
+      ASSERT_EQ(0, pool.nbPhysicalFiles);
+    }
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+
+    for(uint64_t i = 1; i <= nbTapes; i++) {
+      std::ostringstream vid;
+      vid << "vid" << i;
+
+      auto vidAndTapeItor = vidToTape.find(vid.str());
+      ASSERT_NE(vidToTape.end(), vidAndTapeItor);
+
+      const cta::common::dataStructures::Tape tape = vidAndTapeItor->second;
+      ASSERT_EQ(vid.str(), tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.state,tape.state);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = "";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.mediaType = "";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vendor = "";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.logicalLibrary = "";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.tapePool = "";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vo = "";
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.diskFileIds = std::vector<std::string>();
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.state = (cta::common::dataStructures::Tape::State)42;
+    ASSERT_THROW(m_catalogue->Tape()->getTapes(searchCriteria), cta::exception::UserError);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = "vid1";
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(1, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+    ASSERT_EQ("vid1", vidToTape.begin()->first);
+    ASSERT_EQ("vid1", vidToTape.begin()->second.vid);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.mediaType = m_tape1.mediaType;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_tape1.mediaType, vidToTape.begin()->second.mediaType);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vendor = m_tape1.vendor;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_tape1.vendor, vidToTape.begin()->second.vendor);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.logicalLibrary = m_tape1.logicalLibraryName;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_tape1.logicalLibraryName, vidToTape.begin()->second.logicalLibraryName);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.tapePool = m_tape1.tapePoolName;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_tape1.tapePoolName, vidToTape.begin()->second.tapePoolName);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vo = m_vo.name;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_vo.name, vidToTape.begin()->second.vo);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.capacityInBytes = m_mediaType.capacityInBytes;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_mediaType.capacityInBytes, vidToTape.begin()->second.capacityInBytes);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.state = m_tape1.state;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_tape1.state, vidToTape.begin()->second.state);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.full = m_tape1.full;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(m_tape1.full, vidToTape.begin()->second.full);
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = "non_existent_vid";
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_TRUE(tapes.empty());
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    std::vector<std::string> diskFileIds;
+    diskFileIds.push_back("non_existent_fid");
+    searchCriteria.diskFileIds = diskFileIds;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    ASSERT_TRUE(tapes.empty());
+  }
+
+  {
+    cta::catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vid = "vid1";
+    searchCriteria.logicalLibrary = m_tape1.logicalLibraryName;
+    searchCriteria.tapePool = m_tape1.tapePoolName;
+    searchCriteria.capacityInBytes = m_mediaType.capacityInBytes;
+    searchCriteria.state = m_tape1.state;
+    searchCriteria.full = m_tape1.full;
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes(searchCriteria);
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+    ASSERT_EQ("vid1", vidToTape.begin()->first);
+    ASSERT_EQ("vid1", vidToTape.begin()->second.vid);
+    ASSERT_EQ(m_tape1.logicalLibraryName, vidToTape.begin()->second.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, vidToTape.begin()->second.tapePoolName);
+    ASSERT_EQ(m_mediaType.capacityInBytes, vidToTape.begin()->second.capacityInBytes);
+    ASSERT_EQ(m_tape1.state, vidToTape.begin()->second.state);
+    ASSERT_EQ(m_tape1.full, vidToTape.begin()->second.full);
+  }
+
+  {
+    std::set<std::string> vids;
+    for(uint64_t i = 1; i <= nbTapes; i++) {
+      std::ostringstream vid;
+      vid << "vid" << i;
+      vids.insert(vid.str());
+    }
+
+    const cta::common::dataStructures::VidToTapeMap vidToTape = m_catalogue->Tape()->getTapesByVid(vids);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+
+    for(uint64_t i = 1; i <= nbTapes; i++) {
+      std::ostringstream vid;
+      vid << "vid" << i;
+
+      auto vidAndTapeItor = vidToTape.find(vid.str());
+      ASSERT_NE(vidToTape.end(), vidAndTapeItor);
+
+      const cta::common::dataStructures::Tape tape = vidAndTapeItor->second;
+      ASSERT_EQ(vid.str(), tape.vid);
+      ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+      ASSERT_EQ(m_tape1.vendor, tape.vendor);
+      ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+      ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+      ASSERT_EQ(m_vo.name, tape.vo);
+      ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+      ASSERT_EQ(m_tape1.full, tape.full);
+
+      ASSERT_FALSE(tape.isFromCastor);
+      ASSERT_EQ(m_tape1.comment, tape.comment);
+      ASSERT_FALSE(tape.labelLog);
+      ASSERT_FALSE(tape.lastReadLog);
+      ASSERT_FALSE(tape.lastWriteLog);
+
+      const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+}
+
+
+TEST_P(cta_catalogue_TapeTest, getTapesByVid_non_existent_tape) {
+
+  std::set<std::string> vids = {{"non_existent_tape"}};
+  ASSERT_THROW(m_catalogue->Tape()->getTapesByVid(vids), cta::exception::Exception);
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesByVid_no_vids) {
+
+  std::set<std::string> vids;
+  ASSERT_TRUE(m_catalogue->Tape()->getTapesByVid(vids).empty());
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesByVid_1_tape) {
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t nbTapes = 1;
+  std::set<std::string> allVids;
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    auto tape = m_tape1;
+    tape.vid = vid.str();
+    m_catalogue->Tape()->createTape(m_admin, tape);
+    allVids.insert(vid.str());
+  }
+
+  const auto vidToTapeMap = m_catalogue->Tape()->getTapesByVid(allVids);
+  ASSERT_EQ(nbTapes, vidToTapeMap.size());
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    const auto tapeItor = vidToTapeMap.find(vid.str());
+    ASSERT_NE(vidToTapeMap.end(), tapeItor);
+
+    ASSERT_EQ(vid.str(), tapeItor->second.vid);
+    ASSERT_EQ(m_tape1.mediaType, tapeItor->second.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tapeItor->second.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tapeItor->second.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tapeItor->second.tapePoolName);
+    ASSERT_EQ(m_vo.name, tapeItor->second.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tapeItor->second.capacityInBytes);
+    ASSERT_EQ(m_tape1.state, tapeItor->second.state);
+    ASSERT_EQ(m_tape1.full, tapeItor->second.full);
+
+    ASSERT_FALSE(tapeItor->second.isFromCastor);
+    ASSERT_EQ(0, tapeItor->second.readMountCount);
+    ASSERT_EQ(0, tapeItor->second.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tapeItor->second.comment);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, getTapesByVid_350_tapes) {
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t nbTapes = 310;
+  std::set<std::string> allVids;
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    auto tape = m_tape1;
+    tape.vid = vid.str();
+    m_catalogue->Tape()->createTape(m_admin, tape);
+    allVids.insert(vid.str());
+  }
+
+  const auto vidToTapeMap = m_catalogue->Tape()->getTapesByVid(allVids);
+  ASSERT_EQ(nbTapes, vidToTapeMap.size());
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    const auto tapeItor = vidToTapeMap.find(vid.str());
+    ASSERT_NE(vidToTapeMap.end(), tapeItor);
+
+    ASSERT_EQ(vid.str(), tapeItor->second.vid);
+    ASSERT_EQ(m_tape1.mediaType, tapeItor->second.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tapeItor->second.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tapeItor->second.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tapeItor->second.tapePoolName);
+    ASSERT_EQ(m_vo.name, tapeItor->second.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tapeItor->second.capacityInBytes);
+    ASSERT_EQ(m_tape1.state, tapeItor->second.state);
+    ASSERT_EQ(m_tape1.full, tapeItor->second.full);
+
+    ASSERT_FALSE(tapeItor->second.isFromCastor);
+    ASSERT_EQ(0, tapeItor->second.readMountCount);
+    ASSERT_EQ(0, tapeItor->second.writeMountCount);
+    ASSERT_EQ(m_tape1.comment, tapeItor->second.comment);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, getVidToLogicalLibrary_no_vids) {
+
+  std::set<std::string> vids;
+  ASSERT_TRUE(m_catalogue->Tape()->getVidToLogicalLibrary(vids).empty());
+}
+
+TEST_P(cta_catalogue_TapeTest, getVidToLogicalLibrary_1_tape) {
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t nbTapes = 1;
+  std::set<std::string> allVids;
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    auto tape = m_tape1;
+    tape.vid = vid.str();
+    m_catalogue->Tape()->createTape(m_admin, tape);
+    allVids.insert(vid.str());
+  }
+
+  const auto vidToLogicalLibrary = m_catalogue->Tape()->getVidToLogicalLibrary(allVids);
+  ASSERT_EQ(nbTapes, vidToLogicalLibrary.size());
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    const auto itor = vidToLogicalLibrary.find(vid.str());
+    ASSERT_NE(vidToLogicalLibrary.end(), itor);
+
+    ASSERT_EQ(m_tape1.logicalLibraryName, itor->second);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, getVidToLogicalLibrary_310_tapes) {
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  const uint32_t nbTapes = 310;
+  std::set<std::string> allVids;
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    auto tape = m_tape1;
+    tape.vid = vid.str();
+    m_catalogue->Tape()->createTape(m_admin, tape);
+    allVids.insert(vid.str());
+  }
+
+  const auto vidToLogicalLibrary = m_catalogue->Tape()->getVidToLogicalLibrary(allVids);
+  ASSERT_EQ(nbTapes, vidToLogicalLibrary.size());
+
+  for(uint32_t i = 0; i < nbTapes; i++) {
+    std::ostringstream vid;
+    vid << "V" << std::setfill('0') << std::setw(5) << i;
+    const std::string tapeComment = "Create tape " + vid.str();
+
+    const auto itor = vidToLogicalLibrary.find(vid.str());
+    ASSERT_NE(vidToLogicalLibrary.end(), itor);
+
+    ASSERT_EQ(m_tape1.logicalLibraryName, itor->second);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, getNbFilesOnTape_no_tape_files) {
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_EQ(0, m_catalogue->Tape()->getNbFilesOnTape(m_tape1.vid));
+}
+
+TEST_P(cta_catalogue_TapeTest, getNbFilesOnTape_one_tape_file) {
+
+  const std::string diskInstanceName1 = m_diskInstance.name;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  ASSERT_EQ(1, m_catalogue->Tape()->getNbFilesOnTape(m_tape1.vid));
+}
+
+TEST_P(cta_catalogue_TapeTest, checkTapeForLabel_no_tape_files) {
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_NO_THROW(m_catalogue->Tape()->checkTapeForLabel(m_tape1.vid));
+}
+
+TEST_P(cta_catalogue_TapeTest, checkTapeForLabel_one_tape_file) {
+
+  const std::string diskInstanceName1 = m_diskInstance.name;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  ASSERT_THROW(m_catalogue->Tape()->checkTapeForLabel(m_tape1.vid), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, checkTapeForLabel_one_tape_file_reclaimed_tape) {
+
+  const std::string diskInstanceName1 = m_diskInstance.name;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  ASSERT_THROW(m_catalogue->Tape()->checkTapeForLabel(m_tape1.vid), cta::exception::UserError);
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(diskInstanceName1, archiveFileId, dummyLc);
+
+  m_catalogue->Tape()->setTapeFull(m_admin, m_tape1.vid, true);
+  m_catalogue->Tape()->reclaimTape(m_admin, m_tape1.vid,dummyLc);
+
+  ASSERT_NO_THROW(m_catalogue->Tape()->checkTapeForLabel(m_tape1.vid));
+}
+
+TEST_P(cta_catalogue_TapeTest, checkTapeForLabel_not_in_the_catalogue) {
+
+  ASSERT_THROW(m_catalogue->Tape()->checkTapeForLabel(m_tape1.vid), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, checkTapeForLabel_empty_vid) {
+
+  const std::string vid = "";
+  ASSERT_THROW(m_catalogue->Tape()->checkTapeForLabel(vid), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTape_full_lastFSeq_0_no_tape_files) {
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->setTapeFull(m_admin, m_tape1.vid, true);
+  m_catalogue->Tape()->reclaimTape(m_admin, m_tape1.vid, dummyLc);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_FALSE(tape.full);
+    ASSERT_FALSE(tape.verificationStatus);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTape_not_full_lastFSeq_0_no_tape_files) {
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, m_tape1.vid, dummyLc), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTape_full_lastFSeq_1_no_tape_files) {
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  const std::string diskInstanceName1 = m_diskInstance.name;
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+
+    auto it = vidToTape.find(m_tape1.vid);
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP = std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+
+    auto it = vidToTape.find(m_tape1.vid);
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(1, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+    ASSERT_EQ(tapeDrive, tape.lastWriteLog.value().drive);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    m_catalogue->ArchiveFile()->DO_NOT_USE_deleteArchiveFile_DO_NOT_USE(diskInstanceName1, file1Written.archiveFileId, dummyLc);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+
+    auto it = vidToTape.find(m_tape1.vid);
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(1, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+    ASSERT_EQ(tapeDrive, tape.lastWriteLog.value().drive);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->setTapeFull(m_admin, m_tape1.vid, true);
+  m_catalogue->Tape()->reclaimTape(m_admin, m_tape1.vid, dummyLc);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const cta::common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+    ASSERT_EQ(tapeDrive, tape.lastWriteLog.value().drive);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapeTest, reclaimTape_full_lastFSeq_1_one_tape_file) {
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  const std::string diskInstanceName1 = m_diskInstance.name;
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+
+    auto it = vidToTape.find(m_tape1.vid);
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(0, tape.dataOnTapeInBytes);
+    ASSERT_EQ(0, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+    const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+    ASSERT_EQ(1, vidToTape.size());
+
+    auto it = vidToTape.find(m_tape1.vid);
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(file1Written.size, tape.dataOnTapeInBytes);
+    ASSERT_EQ(file1Written.size, tape.masterDataInBytes);
+    ASSERT_EQ(1, tape.nbMasterFiles);
+    ASSERT_EQ(1, tape.lastFSeq);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_TRUE((bool)tape.lastWriteLog);
+    ASSERT_EQ(tapeDrive, tape.lastWriteLog.value().drive);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->Tape()->setTapeFull(m_admin, m_tape1.vid, true);
+  ASSERT_THROW(m_catalogue->Tape()->reclaimTape(m_admin, m_tape1.vid, dummyLc), cta::exception::UserError);
+}
+
+
+
+
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/TapeCatalogueTest.hpp b/catalogue/tests/modules/TapeCatalogueTest.hpp
new file mode 100644
index 0000000000..66496f8e5d
--- /dev/null
+++ b/catalogue/tests/modules/TapeCatalogueTest.hpp
@@ -0,0 +1,62 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <map>
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueFactory.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/MediaType.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_TapeTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_TapeTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+  const cta::catalogue::CreateTapeAttributes m_tape2;
+  const cta::catalogue::CreateTapeAttributes m_tape3;
+
+  std::map<std::string, cta::common::dataStructures::Tape> tapeListToMap(
+    const std::list<cta::common::dataStructures::Tape> &listOfTapes);
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/TapeFileCatalogueTest.cpp b/catalogue/tests/modules/TapeFileCatalogueTest.cpp
new file mode 100644
index 0000000000..4f6ea1f503
--- /dev/null
+++ b/catalogue/tests/modules/TapeFileCatalogueTest.cpp
@@ -0,0 +1,1614 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CatalogueItor.hpp"
+#include "catalogue/CreateMountPolicyAttributes.hpp"
+#include "catalogue/InsertFileRecycleLog.hpp"
+#include "catalogue/TapeFileWritten.hpp"
+#include "catalogue/TapeItemWrittenPointer.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/TapeFileCatalogueTest.hpp"
+#include "common/dataStructures/DeleteArchiveRequest.hpp"
+#include "common/dataStructures/FileRecycleLog.hpp"
+#include "common/dataStructures/RequesterActivityMountRule.hpp"
+#include "common/dataStructures/RequesterMountRule.hpp"
+#include "common/dataStructures/RetrieveFileQueueCriteria.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_TapeFileTest::cta_catalogue_TapeFileTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_tape1(CatalogueTestUtils::getTape1()),
+    m_tape2(CatalogueTestUtils::getTape2()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_storageClassDualCopy(CatalogueTestUtils::getStorageClassDualCopy()) {
+}
+
+void cta_catalogue_TapeFileTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_TapeFileTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_TapeFileTest, moveFilesToRecycleLog) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  auto tape1 = m_tape1;
+  tape1.tapePoolName = tapePoolName1;
+  auto tape2 = m_tape2;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t nbArchiveFiles = 10; // Must be a multiple of 2 for this test
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+  std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+    std::ostringstream diskFileId;
+    diskFileId << (12345677 + i);
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file"<<i;
+
+    // Tape copy 1 written to tape
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = i;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassSingleCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = i;
+    fileWritten.blockId = i * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+  }
+  m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  {
+    ASSERT_TRUE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  }
+  log::LogContext dummyLc(m_dummyLog);
+  for(auto & tapeItemWritten: tapeFilesWrittenCopy1){
+    cta::catalogue::TapeFileWritten * tapeItem = static_cast<cta::catalogue::TapeFileWritten *>(tapeItemWritten.get());
+    cta::common::dataStructures::DeleteArchiveRequest req;
+    req.requester.name = m_admin.username;
+    req.archiveFileID = tapeItem->archiveFileId;
+    req.diskFileId = tapeItem->diskFileId;
+    req.diskFilePath = tapeItem->diskFilePath;
+    req.diskInstance = tapeItem->diskInstance;
+    req.archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(tapeItem->archiveFileId);
+    ASSERT_NO_THROW(m_catalogue->ArchiveFile()->moveArchiveFileToRecycleLog(req,dummyLc));
+  }
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+
+  std::vector<common::dataStructures::FileRecycleLog> deletedArchiveFiles;
+  {
+    auto itor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    while(itor.hasMore()){
+      deletedArchiveFiles.push_back(itor.next());
+    }
+  }
+
+  //And test that these files are there.
+  //Run the unit test for all the databases
+  ASSERT_EQ(nbArchiveFiles,deletedArchiveFiles.size());
+
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+
+    auto deletedArchiveFile = deletedArchiveFiles[i-1];
+
+    std::ostringstream diskFileId;
+    diskFileId << (12345677 + i);
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file"<<i;
+
+    ASSERT_EQ(i,deletedArchiveFile.archiveFileId);
+    ASSERT_EQ(diskInstance,deletedArchiveFile.diskInstanceName);
+    ASSERT_EQ(diskFileId.str(),deletedArchiveFile.diskFileId);
+    ASSERT_EQ(diskFilePath.str(),deletedArchiveFile.diskFilePath);
+    ASSERT_EQ(PUBLIC_DISK_USER,deletedArchiveFile.diskFileUid);
+    ASSERT_EQ(PUBLIC_DISK_GROUP,deletedArchiveFile.diskFileGid);
+    ASSERT_EQ(archiveFileSize,deletedArchiveFile.sizeInBytes);
+    ASSERT_EQ(cta::checksum::ChecksumBlob(checksum::ADLER32, "1357"),deletedArchiveFile.checksumBlob);
+    ASSERT_EQ(m_storageClassSingleCopy.name, deletedArchiveFile.storageClassName);
+    ASSERT_EQ(diskFileId.str(),deletedArchiveFile.diskFileIdWhenDeleted);
+    ASSERT_EQ(cta::catalogue::InsertFileRecycleLog::getDeletionReasonLog(m_admin.username,diskInstance),deletedArchiveFile.reasonLog);
+    ASSERT_EQ(tape1.vid, deletedArchiveFile.vid);
+    ASSERT_EQ(i,deletedArchiveFile.fSeq);
+    ASSERT_EQ(i * 100,deletedArchiveFile.blockId);
+    ASSERT_EQ(1, deletedArchiveFile.copyNb);
+  }
+
+  //Let's try the deletion of the files from the recycle-bin.
+  for(uint64_t i = 1; i <= nbArchiveFiles; i++) {
+    m_catalogue->FileRecycleLog()->deleteFilesFromRecycleLog(tape1.vid,dummyLc);
+  }
+
+  {
+    auto itor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_FALSE(itor.hasMore());
+  }
+}
+
+TEST_P(cta_catalogue_TapeFileTest, DeleteTapeFileCopyUsingArchiveID) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+
+  // Write a file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a second copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+  {
+    //Assert both copies written
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete copy of file on tape1
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape1.vid;
+    criteria.archiveFileId = 1;
+    auto archiveFileForDeletion = m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria);
+    archiveFileForDeletion.diskFileInfo.path = "/test/file1";
+    m_catalogue->TapeFile()->deleteTapeFileCopy(archiveFileForDeletion, reason);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    //The deleted file (fSeq = 1) is on the recycle log
+    auto recycleFileLog = fileRecycleLogItor.next();
+    ASSERT_EQ(1, recycleFileLog.fSeq);
+    ASSERT_EQ(tape1.vid, recycleFileLog.vid);
+    ASSERT_EQ(1, recycleFileLog.archiveFileId);
+    ASSERT_EQ(1, recycleFileLog.copyNb);
+    ASSERT_EQ(1 * 100, recycleFileLog.blockId);
+    ASSERT_EQ("(Deleted using cta-admin tf rm) " + reason, recycleFileLog.reasonLog);
+    ASSERT_EQ(std::string("/test/file1"), recycleFileLog.diskFilePath.value());
+  }
+
+  {
+    //get last archive file copy for deletions should fail
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape2.vid;
+    criteria.archiveFileId = 1;
+    ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria), exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_TapeFileTest, DeleteTapeFileCopyDoesNotExist) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  {
+    //delete copy of file that does not exist should fail
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape2.vid;
+    criteria.archiveFileId = 1;
+    ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria), exception::UserError);
+  }
+}
+
+
+
+TEST_P(cta_catalogue_TapeFileTest, DeleteTapeFileCopyUsingFXID) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const std::string tapePoolName1 = "tape_pool_name_1";
+  const std::string tapePoolName2 = "tape_pool_name_2";
+  const uint64_t nbPartialTapes = 1;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string diskInstance = m_diskInstance.name;
+  const std::string tapeDrive = "tape_drive";
+  const std::string reason = "reason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName1, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName2, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassDualCopy);
+
+  auto tape1 = m_tape1;
+  auto tape2 = m_tape2;
+  tape1.tapePoolName = tapePoolName1;
+  tape2.tapePoolName = tapePoolName2;
+
+  m_catalogue->Tape()->createTape(m_admin, tape1);
+  m_catalogue->Tape()->createTape(m_admin, tape2);
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  const uint64_t archiveFileSize = 2 * 1000 * 1000 * 1000;
+
+
+  // Write a file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape1.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 1;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+
+  // Write a second copy of file on tape
+  {
+    std::set<catalogue::TapeItemWrittenPointer> tapeFilesWrittenCopy1;
+
+    std::ostringstream diskFileId;
+    diskFileId << 12345677;
+
+    std::ostringstream diskFilePath;
+    diskFilePath << "/test/file1";
+
+    auto fileWrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+    auto & fileWritten = *fileWrittenUP;
+    fileWritten.archiveFileId = 1;
+    fileWritten.diskInstance = diskInstance;
+    fileWritten.diskFileId = diskFileId.str();
+    fileWritten.diskFilePath = diskFilePath.str();
+    fileWritten.diskFileOwnerUid = PUBLIC_DISK_USER;
+    fileWritten.diskFileGid = PUBLIC_DISK_GROUP;
+    fileWritten.size = archiveFileSize;
+    fileWritten.checksumBlob.insert(checksum::ADLER32, "1357");
+    fileWritten.storageClassName = m_storageClassDualCopy.name;
+    fileWritten.vid = tape2.vid;
+    fileWritten.fSeq = 1;
+    fileWritten.blockId = 1 * 100;
+    fileWritten.copyNb = 2;
+    fileWritten.tapeDrive = tapeDrive;
+    tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
+
+    m_catalogue->TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
+  }
+  {
+    //Assert both copies written
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+  }
+
+  {
+    //delete copy of file on tape1
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape1.vid;
+    criteria.diskInstance = diskInstance;
+    criteria.diskFileIds = std::vector<std::string>();
+    auto fid = std::to_string(strtol("BC614D", nullptr, 16));
+    criteria.diskFileIds.value().push_back(fid);
+    auto archiveFileForDeletion = m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria);
+    archiveFileForDeletion.diskFileInfo.path = "/test/file1";
+    m_catalogue->TapeFile()->deleteTapeFileCopy(archiveFileForDeletion, reason);
+    auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(1);
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto fileRecycleLogItor = m_catalogue->FileRecycleLog()->getFileRecycleLogItor();
+    ASSERT_TRUE(fileRecycleLogItor.hasMore());
+    //The previous file (fSeq = 1) is on the recycle log
+    auto recycleFileLog = fileRecycleLogItor.next();
+    ASSERT_EQ(1, recycleFileLog.fSeq);
+    ASSERT_EQ(tape1.vid, recycleFileLog.vid);
+    ASSERT_EQ(1, recycleFileLog.archiveFileId);
+    ASSERT_EQ(1, recycleFileLog.copyNb);
+    ASSERT_EQ(1 * 100, recycleFileLog.blockId);
+    ASSERT_EQ("(Deleted using cta-admin tf rm) " + reason, recycleFileLog.reasonLog);
+    ASSERT_EQ(std::string("/test/file1"), recycleFileLog.diskFilePath.value());
+  }
+
+  {
+    //delete last copy of file should fail
+    cta::catalogue::TapeFileSearchCriteria criteria;
+    criteria.vid = tape2.vid;
+    criteria.diskInstance = diskInstance;
+    criteria.diskFileIds = std::vector<std::string>();
+    auto fid = std::to_string(strtol("BC614D", nullptr, 16));
+    criteria.diskFileIds.value().push_back(fid);
+    ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileForDeletion(criteria), exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_TapeFileTest, prepareToRetrieveFileUsingArchiveFileId_repackingTapes) {
+  const std::string diskInstanceName1 = m_diskInstance.name;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  std::string repackingReason = "repackingReason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+  const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+  {
+    auto it = vidToTape.find(m_tape1.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+    tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  {
+    auto it = vidToTape.find(m_tape2.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape2.vid, tape.vid);
+    ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape2.vendor, tape.vendor);
+    ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape2.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape2.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    const auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+
+    const auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+  }
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  uint64_t minArchiveRequestAge = mountPolicyToAdd.minArchiveRequestAge;
+  uint64_t archivePriority = mountPolicyToAdd.archivePriority;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName1,
+    requesterName, comment);
+
+  const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName1, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+    m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc);
+
+    ASSERT_EQ(archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+
+    ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile1Itor = queueCriteria.archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    const auto copyNbToTapeFile2Itor = queueCriteria.archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+  }
+
+  m_catalogue->Tape()->modifyTapeState(m_admin, m_tape1.vid, cta::common::dataStructures::Tape::State::REPACKING,
+    std::nullopt, repackingReason);
+
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+    m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc);
+
+    ASSERT_EQ(archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+
+    ASSERT_EQ(1, queueCriteria.archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile2Itor = queueCriteria.archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+  }
+
+  m_catalogue->Tape()->modifyTapeState(m_admin, m_tape2.vid, cta::common::dataStructures::Tape::State::REPACKING,
+    std::nullopt, repackingReason);
+
+  ASSERT_THROW(m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt,
+    dummyLc), cta::exception::UserError);
+}
+
+
+TEST_P(cta_catalogue_TapeFileTest, prepareToRetrieveFileUsingArchiveFileId) {
+  const std::string diskInstanceName1 = m_diskInstance.name;
+  const std::string diskInstanceName2 = "disk_instance_2";
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstanceName1, "comment");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstanceName2, "comment");
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+  const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+  {
+    auto it = vidToTape.find(m_tape1.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  {
+    auto it = vidToTape.find(m_tape2.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape2.vid, tape.vid);
+    ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape2.vendor, tape.vendor);
+    ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape2.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape2.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP = std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const auto archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+  }
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  uint64_t minArchiveRequestAge = mountPolicyToAdd.minArchiveRequestAge;
+  uint64_t archivePriority = mountPolicyToAdd.archivePriority;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+  
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName1, requesterName, comment);
+
+  const std::list<cta::common::dataStructures::RequesterMountRule> rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName1, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+  const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+    m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc);
+
+  ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
+  ASSERT_EQ(archivePriority, queueCriteria.mountPolicy.archivePriority);
+  ASSERT_EQ(minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+
+  // Check that the diskInstanceName mismatch detection works
+  ASSERT_THROW(m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName2, archiveFileId, requesterIdentity, std::nullopt, dummyLc),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeFileTest, prepareToRetrieveFileUsingArchiveFileId_disabledTapes) {
+  const std::string diskInstanceName1 = m_diskInstance.name;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  std::string disabledReason = "disabledReason";
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+  const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+  {
+    auto it = vidToTape.find(m_tape1.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  {
+    auto it = vidToTape.find(m_tape2.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape2.vid, tape.vid);
+    ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape2.vendor, tape.vendor);
+    ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape2.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape2.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+
+    const auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+  }
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  uint64_t minArchiveRequestAge = mountPolicyToAdd.minArchiveRequestAge;
+  uint64_t archivePriority = mountPolicyToAdd.archivePriority;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName1,
+    requesterName, comment);
+
+  const auto rules = m_catalogue->RequesterMountRule()->getRequesterMountRules();
+  ASSERT_EQ(1, rules.size());
+
+  const cta::common::dataStructures::RequesterMountRule rule = rules.front();
+
+  ASSERT_EQ(diskInstanceName1, rule.diskInstance);
+  ASSERT_EQ(requesterName, rule.name);
+  ASSERT_EQ(mountPolicyName, rule.mountPolicy);
+  ASSERT_EQ(comment, rule.comment);
+  ASSERT_EQ(m_admin.username, rule.creationLog.username);
+  ASSERT_EQ(m_admin.host, rule.creationLog.host);
+  ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+      m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc);
+
+    ASSERT_EQ(archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+
+    ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile1Itor = queueCriteria.archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    const auto copyNbToTapeFile2Itor = queueCriteria.archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+
+  m_catalogue->Tape()->setTapeDisabled(m_admin, m_tape1.vid, disabledReason);
+
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+      m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc);
+
+    ASSERT_EQ(archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+
+    ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile1Itor = queueCriteria.archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    const auto copyNbToTapeFile2Itor = queueCriteria.archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+}
+
+TEST_P(cta_catalogue_TapeFileTest, prepareToRetrieveFileUsingArchiveFileId_returnNonSupersededFiles) {
+  const std::string diskInstanceName1 = m_diskInstance.name;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+  const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  //Create a superseder file
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 1;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  ASSERT_TRUE(m_catalogue->FileRecycleLog()->getFileRecycleLogItor().hasMore());
+
+  auto mountPolicyToAdd = CatalogueTestUtils::getMountPolicy1();
+  std::string mountPolicyName = mountPolicyToAdd.name;
+  uint64_t minArchiveRequestAge = mountPolicyToAdd.minArchiveRequestAge;
+  uint64_t archivePriority = mountPolicyToAdd.archivePriority;
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd);
+
+  const std::string comment = "Create mount rule for requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->RequesterMountRule()->createRequesterMountRule(m_admin, mountPolicyName, diskInstanceName1, requesterName, comment);
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+      m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc);
+
+    ASSERT_EQ(archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+
+    ASSERT_EQ(1, queueCriteria.archiveFile.tapeFiles.size());
+
+    const auto copyNbToTapeFile1Itor = queueCriteria.archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, queueCriteria.archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file2Written.copyNb, tapeFile1.copyNb);
+  }
+
+  std::string repackingReason = "repackingReason";
+  m_catalogue->Tape()->modifyTapeState(m_admin, m_tape2.vid, cta::common::dataStructures::Tape::State::REPACKING,
+    std::nullopt, repackingReason);
+
+  ASSERT_THROW(m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, std::nullopt, dummyLc),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapeFileTest, prepareToRetrieveFileUsingArchiveFileId_ActivityMountPolicy) {
+  const std::string diskInstanceName1 = m_diskInstance.name;
+  const std::string diskInstanceName2 = "disk_instance_2";
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+  m_catalogue->Tape()->createTape(m_admin, m_tape2);
+
+  const std::list<cta::common::dataStructures::Tape> tapes = m_catalogue->Tape()->getTapes();
+  const std::map<std::string, cta::common::dataStructures::Tape> vidToTape = CatalogueTestUtils::tapeListToMap(tapes);
+  {
+    auto it = vidToTape.find(m_tape1.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  {
+    auto it = vidToTape.find(m_tape2.vid);
+    ASSERT_TRUE(it != vidToTape.end());
+    const cta::common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(m_tape2.vid, tape.vid);
+    ASSERT_EQ(m_tape2.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape2.vendor, tape.vendor);
+    ASSERT_EQ(m_tape2.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape2.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape2.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape2.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const cta::common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+
+  ASSERT_FALSE(m_catalogue->ArchiveFile()->getArchiveFilesItor().hasMore());
+  ASSERT_THROW(m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId), cta::exception::Exception);
+
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const uint64_t archiveFileSize = 1;
+  const std::string tapeDrive = "tape_drive";
+
+  auto file1WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file1Written = *file1WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet;
+  file1WrittenSet.insert(file1WrittenUP.release());
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = diskInstanceName1;
+  file1Written.diskFileId           = "5678";
+  file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
+  file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
+  file1Written.size                 = archiveFileSize;
+  file1Written.checksumBlob.insert(cta::checksum::ADLER32, "1234");
+  file1Written.storageClassName     = m_storageClassSingleCopy.name;
+  file1Written.vid                  = m_tape1.vid;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.copyNb               = 1;
+  file1Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file1WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file1Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file1Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  auto file2WrittenUP=std::make_unique<cta::catalogue::TapeFileWritten>();
+  auto & file2Written = *file2WrittenUP;
+  std::set<cta::catalogue::TapeItemWrittenPointer> file2WrittenSet;
+  file2WrittenSet.insert(file2WrittenUP.release());
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+  file2Written.diskFileOwnerUid     = file1Written.diskFileOwnerUid;
+  file2Written.diskFileGid          = file1Written.diskFileGid;
+  file2Written.size                 = archiveFileSize;
+  file2Written.checksumBlob         = file1Written.checksumBlob;
+  file2Written.storageClassName     = m_storageClassSingleCopy.name;
+  file2Written.vid                  = m_tape2.vid;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.copyNb               = 2;
+  file2Written.tapeDrive            = tapeDrive;
+  m_catalogue->TapeFile()->filesWrittenToTape(file2WrittenSet);
+
+  {
+    const cta::common::dataStructures::ArchiveFile archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileId);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.checksumBlob, archiveFile.checksumBlob);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file2Written.diskFileOwnerUid, archiveFile.diskFileInfo.owner_uid);
+    ASSERT_EQ(file2Written.diskFileGid, archiveFile.diskFileInfo.gid);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_NE(copyNbToTapeFile1Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile1 = *copyNbToTapeFile1Itor;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.checksumBlob, tapeFile1.checksumBlob);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_NE(copyNbToTapeFile2Itor, archiveFile.tapeFiles.end());
+    const cta::common::dataStructures::TapeFile &tapeFile2 = *copyNbToTapeFile2Itor;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.checksumBlob, tapeFile2.checksumBlob);
+  }
+
+  auto mountPolicyToAdd1 = CatalogueTestUtils::getMountPolicy1();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd1);
+  auto mountPolicyToAdd2 = CatalogueTestUtils::getMountPolicy2();
+  m_catalogue->MountPolicy()->createMountPolicy(m_admin,mountPolicyToAdd2);
+
+    const std::string comment = "Create mount rule for requester+activity";
+    const std::string requesterName = "requester_name";
+    const std::string activityRegex = "^activity_[a-zA-Z0-9-]+$";
+    m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyToAdd1.name,
+      diskInstanceName1, requesterName, activityRegex, comment);
+
+    const std::string secondActivityRegex = "^activity_specific$";
+    m_catalogue->RequesterActivityMountRule()->createRequesterActivityMountRule(m_admin, mountPolicyToAdd2.name,
+      diskInstanceName1, requesterName, secondActivityRegex, comment);
+  {
+    const auto rules = m_catalogue->RequesterActivityMountRule()->getRequesterActivityMountRules();
+    ASSERT_EQ(2, rules.size());
+  }
+
+  cta::log::LogContext dummyLc(m_dummyLog);
+
+  cta::common::dataStructures::RequesterIdentity requesterIdentity;
+  requesterIdentity.name = requesterName;
+  requesterIdentity.group = "group";
+  std::optional<std::string> requestActivity = std::string("activity_retrieve");
+
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+      m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity,
+        requestActivity, dummyLc);
+
+    ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
+    ASSERT_EQ(mountPolicyToAdd1.archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(mountPolicyToAdd1.minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+  }
+
+  // Check that multiple matching policies returns the highest priority one for retrieve
+  requestActivity = std::string("activity_specific");
+  {
+    const cta::common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
+    m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName1, archiveFileId, requesterIdentity, requestActivity,
+      dummyLc);
+
+    ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
+    ASSERT_EQ(mountPolicyToAdd2.archivePriority, queueCriteria.mountPolicy.archivePriority);
+    ASSERT_EQ(mountPolicyToAdd2.minArchiveRequestAge, queueCriteria.mountPolicy.archiveMinRequestAge);
+  }
+
+
+  // Check that no matching activity detection works
+  requestActivity = std::string("no_matching_activity");
+  ASSERT_THROW(m_catalogue->TapeFile()->prepareToRetrieveFile(diskInstanceName2, archiveFileId, requesterIdentity,
+    requestActivity, dummyLc), cta::exception::UserError);
+}
+
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/TapeFileCatalogueTest.hpp b/catalogue/tests/modules/TapeFileCatalogueTest.hpp
new file mode 100644
index 0000000000..042f4742ee
--- /dev/null
+++ b/catalogue/tests/modules/TapeFileCatalogueTest.hpp
@@ -0,0 +1,57 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/MediaTypeWithLogs.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_TapeFileTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_TapeFileTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+  const cta::catalogue::CreateTapeAttributes m_tape2;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::StorageClass m_storageClassDualCopy;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/TapePoolCatalogueTest.cpp b/catalogue/tests/modules/TapePoolCatalogueTest.cpp
new file mode 100644
index 0000000000..3f07954606
--- /dev/null
+++ b/catalogue/tests/modules/TapePoolCatalogueTest.cpp
@@ -0,0 +1,1526 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/MediaType.hpp"
+#include "catalogue/rdbms/CommonExceptions.hpp"
+#include "catalogue/TapePool.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/TapePoolCatalogueTest.hpp"
+#include "common/Constants.hpp"
+#include "common/dataStructures/ArchiveRoute.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/Tape.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace unitTests {
+
+cta_catalogue_TapePoolTest::cta_catalogue_TapePoolTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_anotherVo(CatalogueTestUtils::getAnotherVo()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_anotherStorageClass(CatalogueTestUtils::getAnotherStorageClass()),
+    m_mediaType(CatalogueTestUtils::getMediaType()),
+    m_tape1(CatalogueTestUtils::getTape1()) {
+}
+
+void cta_catalogue_TapePoolTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_TapePoolTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_TapePoolTest, getTapePool_non_existent) {
+  const std::string tapePoolName = "non_existent_tape_pool";
+
+  ASSERT_FALSE(m_catalogue->TapePool()->tapePoolExists(tapePoolName));
+
+  const auto pool = m_catalogue->TapePool()->getTapePool(tapePoolName);
+
+  ASSERT_FALSE((bool)pool);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool) {
+  const std::string tapePoolName = "tape_pool";
+
+  ASSERT_FALSE(m_catalogue->TapePool()->tapePoolExists(tapePoolName));
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+
+  ASSERT_TRUE(m_catalogue->TapePool()->tapePoolExists(tapePoolName));
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(supply.value(), pool.supply.value());
+    ASSERT_EQ(supply, pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  {
+    const auto pool = m_catalogue->TapePool()->getTapePool(tapePoolName);
+
+    ASSERT_TRUE((bool)pool);
+
+    ASSERT_EQ(tapePoolName, pool->name);
+    ASSERT_EQ(m_vo.name, pool->vo.name);
+    ASSERT_EQ(nbPartialTapes, pool->nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool->encryption);
+    ASSERT_TRUE((bool)pool->supply);
+    ASSERT_EQ(supply.value(), pool->supply.value());
+    ASSERT_EQ(supply, pool->supply);
+    ASSERT_EQ(0, pool->nbTapes);
+    ASSERT_EQ(0, pool->capacityBytes);
+    ASSERT_EQ(0, pool->dataBytes);
+    ASSERT_EQ(0, pool->nbPhysicalFiles);
+    ASSERT_EQ(comment, pool->comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool->creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool->lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_null_supply) {
+  const std::string tapePoolName = "tape_pool";
+
+  ASSERT_FALSE(m_catalogue->TapePool()->tapePoolExists(tapePoolName));
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply;
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+
+  ASSERT_TRUE(m_catalogue->TapePool()->tapePoolExists(tapePoolName));
+
+  const auto pools = m_catalogue->TapePool()->getTapePools();
+
+  ASSERT_EQ(1, pools.size());
+
+  const auto &pool = pools.front();
+  ASSERT_EQ(tapePoolName, pool.name);
+  ASSERT_EQ(m_vo.name, pool.vo.name);
+  ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+  ASSERT_EQ(isEncrypted, pool.encryption);
+  ASSERT_FALSE((bool)pool.supply);
+  ASSERT_EQ(0, pool.nbTapes);
+  ASSERT_EQ(0, pool.capacityBytes);
+  ASSERT_EQ(0, pool.dataBytes);
+  ASSERT_EQ(0, pool.nbPhysicalFiles);
+  ASSERT_EQ(comment, pool.comment);
+
+  const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+  ASSERT_EQ(m_admin.username, creationLog.username);
+  ASSERT_EQ(m_admin.host, creationLog.host);
+
+  const cta::common::dataStructures::EntryLog lastModificationLog =
+    pool.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_same_twice) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  ASSERT_THROW(m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes,
+    isEncrypted, supply, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_vo_does_not_exist) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  ASSERT_THROW(m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes,
+    isEncrypted, supply, comment), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_tapes_of_mixed_state) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  cta::catalogue::TapeSearchCriteria criteria;
+  criteria.vid = m_tape1.vid;
+  ASSERT_EQ(0,m_catalogue->Tape()->getTapes(criteria).size());
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  auto tape_disabled_01 = m_tape1;
+  tape_disabled_01.vid = "D000001";
+  tape_disabled_01.state = cta::common::dataStructures::Tape::DISABLED;
+  tape_disabled_01.stateReason = "unit Test";
+  m_catalogue->Tape()->createTape(m_admin, tape_disabled_01);
+
+  auto tape_disabled_02 = m_tape1;
+  tape_disabled_02.vid = "D000002";
+  tape_disabled_02.state = cta::common::dataStructures::Tape::DISABLED;
+  tape_disabled_02.stateReason = "unit Test";
+  m_catalogue->Tape()->createTape(m_admin, tape_disabled_02);
+
+  auto tape_broken_01 = m_tape1;
+  tape_broken_01.vid = "B000002";
+  tape_broken_01.state = cta::common::dataStructures::Tape::BROKEN;
+  tape_broken_01.stateReason = "unit Test";
+  m_catalogue->Tape()->createTape(m_admin, tape_broken_01);
+
+  auto tape_exported_01 = m_tape1;
+  tape_exported_01.vid = "E000001";
+  tape_exported_01.state = cta::common::dataStructures::Tape::EXPORTED;
+  tape_exported_01.stateReason = "unit Test";
+  m_catalogue->Tape()->createTape(m_admin, tape_exported_01);
+
+  auto tape_full_01 = m_tape1;
+  tape_full_01.vid = "F000001";
+  tape_full_01.full = true;
+  m_catalogue->Tape()->createTape(m_admin, tape_full_01);
+
+  auto tape_full_02 = m_tape1;
+  tape_full_02.vid = "F000002";
+  tape_full_02.full = true;
+  m_catalogue->Tape()->createTape(m_admin, tape_full_02);
+
+  auto tape_full_03 = m_tape1;
+  tape_full_03.vid = "F000003";
+  tape_full_03.full = true;
+  m_catalogue->Tape()->createTape(m_admin, tape_full_03);
+
+  auto tape_broken_full_01 = m_tape1;
+  tape_broken_full_01.vid = "BFO001";
+  tape_broken_full_01.state = cta::common::dataStructures::Tape::BROKEN;
+  tape_broken_full_01.stateReason = "unit Test";
+  tape_broken_full_01.full = true;
+  m_catalogue->Tape()->createTape(m_admin, tape_broken_full_01);
+
+  auto tape_exported_full_01 = m_tape1;
+  tape_exported_full_01.vid = "EFO001";
+  tape_exported_full_01.state = cta::common::dataStructures::Tape::EXPORTED;
+  tape_exported_full_01.stateReason = "unit Test";
+  tape_exported_full_01.full = true;
+  m_catalogue->Tape()->createTape(m_admin, tape_exported_full_01);
+
+  auto tape_disabled_full_01 = m_tape1;
+  tape_disabled_full_01.vid = "DFO001";
+  tape_disabled_full_01.state = cta::common::dataStructures::Tape::DISABLED;
+  tape_disabled_full_01.stateReason = "unit Test";
+  tape_disabled_full_01.full = true;
+  m_catalogue->Tape()->createTape(m_admin, tape_disabled_full_01);
+
+  auto tape_disabled_full_02 = m_tape1;
+  tape_disabled_full_02.vid = "DFO002";
+  tape_disabled_full_02.full = true;
+  tape_disabled_full_02.state = cta::common::dataStructures::Tape::DISABLED;
+  tape_disabled_full_02.stateReason = "unit Test";
+  m_catalogue->Tape()->createTape(m_admin, tape_disabled_full_02);
+
+  const auto tapes = m_catalogue->Tape()->getTapes();
+
+  ASSERT_EQ(12, tapes.size());
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(12, pool.nbTapes);
+    ASSERT_EQ(12, pool.nbEmptyTapes);
+    ASSERT_EQ(4, pool.nbDisabledTapes);
+    ASSERT_EQ(7, pool.nbFullTapes);
+    ASSERT_EQ(1, pool.nbWritableTapes);
+    ASSERT_EQ(12 * m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  {
+    const auto pool = m_catalogue->TapePool()->getTapePool(m_tape1.tapePoolName);
+    ASSERT_TRUE((bool)pool);
+
+    ASSERT_EQ(m_tape1.tapePoolName, pool->name);
+    ASSERT_EQ(m_vo.name, pool->vo.name);
+    ASSERT_EQ(12, pool->nbTapes);
+    ASSERT_EQ(12, pool->nbEmptyTapes);
+    ASSERT_EQ(4, pool->nbDisabledTapes);
+    ASSERT_EQ(7, pool->nbFullTapes);
+    ASSERT_EQ(1, pool->nbWritableTapes);
+    ASSERT_EQ(12 * m_mediaType.capacityInBytes, pool->capacityBytes);
+    ASSERT_EQ(0, pool->dataBytes);
+    ASSERT_EQ(0, pool->nbPhysicalFiles);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, deleteTapePool) {
+  const uint64_t tapePoolNbPartialTapes = 2;
+  const bool tapePoolIsEncrypted = true;
+  const std::string tapePoolComment = "Create tape pool";
+  {
+    const std::optional<std::string> supply("value for the supply pool mechanism");
+    m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+    m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+    m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, tapePoolNbPartialTapes,
+      tapePoolIsEncrypted, supply, tapePoolComment);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(tapePoolNbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(tapePoolIsEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(tapePoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  // Create a separate archive route with another tape pool that has nothing to
+  // do with the tape pool being tested in order to test
+  // RdbmsCatalogue::tapePoolUsedInAnArchiveRoute()
+  const std::string anotherTapePoolName = "another_tape_pool";
+  const uint64_t anotherNbPartialTapes = 4;
+  const std::string anotherTapePoolComment = "Create another tape pool";
+  const bool anotherTapePoolIsEncrypted = false;
+  {
+    m_catalogue->StorageClass()->createStorageClass(m_admin, m_anotherStorageClass);
+    const std::optional<std::string> supply("value for the supply pool mechanism");
+    m_catalogue->VO()->createVirtualOrganization(m_admin, m_anotherVo);
+    m_catalogue->TapePool()->createTapePool(m_admin, anotherTapePoolName, m_anotherVo.name, anotherNbPartialTapes,
+      anotherTapePoolIsEncrypted, supply, anotherTapePoolComment);
+    const uint32_t copyNb = 1;
+    const std::string comment = "Create a separate archive route";
+    m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_anotherStorageClass.name, copyNb, anotherTapePoolName, comment);
+  }
+
+  {
+    const auto pools = CatalogueTestUtils::tapePoolListToMap(m_catalogue->TapePool()->getTapePools());
+
+    ASSERT_EQ(2, pools.size());
+
+    {
+      const auto poolMaplet = pools.find(m_tape1.tapePoolName);
+      ASSERT_NE(pools.end(), poolMaplet);
+
+      const auto &pool = poolMaplet->second;
+      ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+      ASSERT_EQ(m_vo.name, pool.vo.name);
+      ASSERT_EQ(tapePoolNbPartialTapes, pool.nbPartialTapes);
+      ASSERT_EQ(tapePoolIsEncrypted, pool.encryption);
+      ASSERT_EQ(0, pool.nbTapes);
+      ASSERT_EQ(0, pool.capacityBytes);
+      ASSERT_EQ(0, pool.dataBytes);
+      ASSERT_EQ(0, pool.nbPhysicalFiles);
+      ASSERT_EQ(tapePoolComment, pool.comment);
+
+      const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+
+    {
+      const auto poolMaplet = pools.find(anotherTapePoolName);
+      ASSERT_NE(pools.end(), poolMaplet);
+
+      const auto &pool = poolMaplet->second;
+      ASSERT_EQ(anotherTapePoolName, pool.name);
+      ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+      ASSERT_EQ(anotherNbPartialTapes, pool.nbPartialTapes);
+      ASSERT_EQ(anotherTapePoolIsEncrypted, pool.encryption);
+      ASSERT_EQ(0, pool.nbTapes);
+      ASSERT_EQ(0, pool.capacityBytes);
+      ASSERT_EQ(0, pool.dataBytes);
+      ASSERT_EQ(0, pool.nbPhysicalFiles);
+      ASSERT_EQ(anotherTapePoolComment, pool.comment);
+
+      const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+      ASSERT_EQ(m_admin.username, creationLog.username);
+      ASSERT_EQ(m_admin.host, creationLog.host);
+
+      const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+      ASSERT_EQ(creationLog, lastModificationLog);
+    }
+  }
+
+  m_catalogue->TapePool()->deleteTapePool(m_tape1.tapePoolName);
+
+  ASSERT_EQ(1, m_catalogue->TapePool()->getTapePools().size());
+}
+
+TEST_P(cta_catalogue_TapePoolTest, deleteTapePool_notEmpty) {
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->MediaType()->createMediaType(m_admin, m_mediaType);
+
+  m_catalogue->LogicalLibrary()->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled,
+    "Create logical library");
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->Tape()->createTape(m_admin, m_tape1);
+
+  ASSERT_TRUE(m_catalogue->Tape()->tapeExists(m_tape1.vid));
+
+  const auto tapes = m_catalogue->Tape()->getTapes();
+
+  ASSERT_EQ(1, tapes.size());
+
+  {
+    const auto tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.state,tape.state);
+    ASSERT_EQ(m_tape1.full, tape.full);
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const auto creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(m_tape1.tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(m_mediaType.capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  ASSERT_THROW(m_catalogue->TapePool()->deleteTapePool(m_tape1.tapePoolName),
+    cta::catalogue::UserSpecifiedAnEmptyTapePool);
+  ASSERT_THROW(m_catalogue->TapePool()->deleteTapePool(m_tape1.tapePoolName), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_emptyStringTapePoolName) {
+  const std::string tapePoolName = "";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  ASSERT_THROW(m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName, m_vo.name, nbPartialTapes, isEncrypted,
+    supply, comment), cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_emptyStringVO) {
+  const std::string vo = "";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  ASSERT_THROW(m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, "", nbPartialTapes, isEncrypted,
+    supply, comment), cta::catalogue::UserSpecifiedAnEmptyStringVo);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, createTapePool_emptyStringComment) {
+  const std::string tapePoolName = "tape_pool";
+
+  ASSERT_FALSE(m_catalogue->TapePool()->tapePoolExists(tapePoolName));
+
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  ASSERT_THROW(m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes,
+    isEncrypted, supply, comment), cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, deleteTapePool_non_existent) {
+    ASSERT_THROW(m_catalogue->TapePool()->deleteTapePool("non_existent_tape_pool"), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, deleteTapePool_used_in_an_archive_route) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  const uint32_t copyNb = 1;
+  const std::string comment = "Create archive route";
+  m_catalogue->ArchiveRoute()->createArchiveRoute(m_admin, m_storageClassSingleCopy.name, copyNb, tapePoolName, comment);
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes = m_catalogue->ArchiveRoute()->getArchiveRoutes();
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const std::list<cta::common::dataStructures::ArchiveRoute> routes
+      = m_catalogue->ArchiveRoute()->getArchiveRoutes(m_storageClassSingleCopy.name, tapePoolName);
+
+    ASSERT_EQ(1, routes.size());
+
+    const cta::common::dataStructures::ArchiveRoute route = routes.front();
+    ASSERT_EQ(m_storageClassSingleCopy.name, route.storageClassName);
+    ASSERT_EQ(copyNb, route.copyNb);
+    ASSERT_EQ(tapePoolName, route.tapePoolName);
+    ASSERT_EQ(comment, route.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = route.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = route.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  ASSERT_THROW(m_catalogue->TapePool()->deleteTapePool(tapePoolName),
+    cta::catalogue::UserSpecifiedTapePoolUsedInAnArchiveRoute);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolVo) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  auto modifiedVo = m_vo;
+  modifiedVo.name = "modified_vo";
+  m_catalogue->VO()->createVirtualOrganization(m_admin, modifiedVo);
+  m_catalogue->TapePool()->modifyTapePoolVo(m_admin, tapePoolName, modifiedVo.name);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(modifiedVo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolVo_emptyStringTapePool) {
+  const std::string tapePoolName = "";
+  const std::string modifiedVo = "modified_vo";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolVo(m_admin, tapePoolName, modifiedVo),
+    cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolVo_emptyStringVo) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedVo = "";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolVo(m_admin, tapePoolName, modifiedVo),
+    cta::catalogue::UserSpecifiedAnEmptyStringVo);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolVo_VoDoesNotExist) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedVo = "DoesNotExists";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolVo(m_admin, tapePoolName, modifiedVo),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolNbPartialTapes) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t modifiedNbPartialTapes = 5;
+  m_catalogue->TapePool()->modifyTapePoolNbPartialTapes(m_admin, tapePoolName, modifiedNbPartialTapes);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(modifiedNbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolNbPartialTapes_emptyStringTapePoolName) {
+  const std::string tapePoolName = "";
+  const uint64_t modifiedNbPartialTapes = 5;
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolNbPartialTapes(m_admin, tapePoolName, modifiedNbPartialTapes),
+    cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolNbPartialTapes_nonExistentTapePool) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 5;
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolNbPartialTapes(m_admin, tapePoolName, nbPartialTapes),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolComment) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "Modified comment";
+  m_catalogue->TapePool()->modifyTapePoolComment(m_admin, tapePoolName, modifiedComment);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(modifiedComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolComment_emptyStringTapePoolName) {
+    const std::string tapePoolName = "";
+  const std::string modifiedComment = "Modified comment";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolComment(m_admin, tapePoolName, modifiedComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolComment_emptyStringComment) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedComment = "";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolComment(m_admin, tapePoolName, modifiedComment),
+    cta::catalogue::UserSpecifiedAnEmptyStringComment);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolComment_nonExistentTapePool) {
+    const std::string tapePoolName = "tape_pool";
+  const std::string comment = "Comment";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolComment(m_admin, tapePoolName, comment),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, setTapePoolEncryption) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const bool modifiedIsEncrypted = !isEncrypted;
+  m_catalogue->TapePool()->setTapePoolEncryption(m_admin, tapePoolName, modifiedIsEncrypted);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(modifiedIsEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, setTapePoolEncryption_nonExistentTapePool) {
+  const std::string tapePoolName = "tape_pool";
+  const bool isEncrypted = false;
+  ASSERT_THROW(m_catalogue->TapePool()->setTapePoolEncryption(m_admin, tapePoolName, isEncrypted),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolSupply) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)supply);
+    ASSERT_EQ(supply.value(), pool.supply.value());
+    ASSERT_EQ(supply, pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedSupply("Modified supply");
+  m_catalogue->TapePool()->modifyTapePoolSupply(m_admin, tapePoolName, modifiedSupply);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)supply);
+    ASSERT_EQ(modifiedSupply, pool.supply.value());
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolSupply_emptyStringTapePoolName) {
+    const std::string tapePoolName = "";
+  const std::string modifiedSupply = "Modified supply";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolSupply(m_admin, tapePoolName, modifiedSupply),
+    cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolSupply_emptyStringSupply) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)supply);
+    ASSERT_EQ(supply.value(), pool.supply.value());
+    ASSERT_EQ(supply, pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string modifiedSupply;
+  m_catalogue->TapePool()->modifyTapePoolSupply(m_admin, tapePoolName, modifiedSupply);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_FALSE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolSupply_nonExistentTapePool) {
+  const std::string tapePoolName = "tape_pool";
+  const std::string supply = "value for the supply pool mechanism";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolSupply(m_admin, tapePoolName, supply), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, getTapePools_filterName) {
+  const std::string tapePoolName = "tape_pool";
+  const std::string secondTapePoolName = "tape_pool_2";
+
+  const uint64_t nbFirstPoolPartialTapes = 2;
+  const uint64_t nbSecondPoolPartialTapes = 3;
+
+  const bool firstPoolIsEncrypted = true;
+  const bool secondPoolIsEncrypted = false;
+
+  const std::optional<std::string> firstPoolSupply("value for the supply first pool mechanism");
+  const std::optional<std::string> secondPoolSupply("value for the supply second pool mechanism");
+
+  const std::string firstPoolComment = "Create first tape pool";
+  const std::string secondPoolComment = "Create second tape pool";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_anotherVo);
+
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName, m_vo.name, nbFirstPoolPartialTapes,
+    firstPoolIsEncrypted, firstPoolSupply, firstPoolComment);
+  m_catalogue->TapePool()->createTapePool(m_admin, secondTapePoolName, m_anotherVo.name, nbSecondPoolPartialTapes,
+    secondPoolIsEncrypted, secondPoolSupply, secondPoolComment);
+
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = tapePoolName;
+    const auto pools = m_catalogue->TapePool()->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbFirstPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(firstPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(firstPoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = secondTapePoolName;
+    const auto pools = m_catalogue->TapePool()->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(secondTapePoolName, pool.name);
+    ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+    ASSERT_EQ(nbSecondPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(secondPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(secondPoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = "no pool with such name";
+
+    ASSERT_THROW(m_catalogue->TapePool()->getTapePools(criteria), cta::catalogue::UserSpecifiedANonExistentTapePool);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = "";
+
+    ASSERT_THROW(m_catalogue->TapePool()->getTapePools(criteria), cta::exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, getTapePools_filterVO) {
+  const std::string tapePoolName = "tape_pool";
+  const std::string secondTapePoolName = "tape_pool_2";
+
+  const uint64_t nbFirstPoolPartialTapes = 2;
+  const uint64_t nbSecondPoolPartialTapes = 3;
+
+  const bool firstPoolIsEncrypted = true;
+  const bool secondPoolIsEncrypted = false;
+
+  const std::optional<std::string> firstPoolSupply("value for the supply first pool mechanism");
+  const std::optional<std::string> secondPoolSupply("value for the supply second pool mechanism");
+
+  const std::string firstPoolComment = "Create first tape pool";
+  const std::string secondPoolComment = "Create second tape pool";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_anotherVo);
+
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName, m_vo.name, nbFirstPoolPartialTapes,
+    firstPoolIsEncrypted, firstPoolSupply, firstPoolComment);
+  m_catalogue->TapePool()->createTapePool(m_admin, secondTapePoolName, m_anotherVo.name, nbSecondPoolPartialTapes,
+    secondPoolIsEncrypted, secondPoolSupply, secondPoolComment);
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = m_vo.name;
+    const auto pools = m_catalogue->TapePool()->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbFirstPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(firstPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(firstPoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = m_anotherVo.name;
+    const auto pools = m_catalogue->TapePool()->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(secondTapePoolName, pool.name);
+    ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+    ASSERT_EQ(nbSecondPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(secondPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(secondPoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = "no vo with such name";
+
+    ASSERT_THROW(m_catalogue->TapePool()->getTapePools(criteria),
+      cta::catalogue::UserSpecifiedANonExistentVirtualOrganization);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = "";
+
+    ASSERT_THROW(m_catalogue->TapePool()->getTapePools(criteria), cta::exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, getTapePools_filterEncrypted) {
+  const std::string tapePoolName = "tape_pool";
+  const std::string secondTapePoolName = "tape_pool_2";
+
+  const uint64_t nbFirstPoolPartialTapes = 2;
+  const uint64_t nbSecondPoolPartialTapes = 3;
+
+  const bool firstPoolIsEncrypted = true;
+  const bool secondPoolIsEncrypted = false;
+
+  const std::optional<std::string> firstPoolSupply("value for the supply first pool mechanism");
+  const std::optional<std::string> secondPoolSupply("value for the supply second pool mechanism");
+
+  const std::string firstPoolComment = "Create first tape pool";
+  const std::string secondPoolComment = "Create second tape pool";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_anotherVo);
+
+  m_catalogue->TapePool()->createTapePool(m_admin, tapePoolName, m_vo.name, nbFirstPoolPartialTapes,
+    firstPoolIsEncrypted, firstPoolSupply, firstPoolComment);
+  m_catalogue->TapePool()->createTapePool(m_admin, secondTapePoolName, m_anotherVo.name, nbSecondPoolPartialTapes,
+    secondPoolIsEncrypted, secondPoolSupply, secondPoolComment);
+
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.encrypted = true;
+    const auto pools = m_catalogue->TapePool()->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbFirstPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(firstPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(firstPoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.encrypted = false;
+    const auto pools = m_catalogue->TapePool()->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(secondTapePoolName, pool.name);
+    ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+    ASSERT_EQ(nbSecondPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(secondPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(secondPoolComment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolName) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string newTapePoolName = "new_tape_pool";
+  m_catalogue->TapePool()->modifyTapePoolName(m_admin, tapePoolName, newTapePoolName);
+
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(newTapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolName_emptyStringCurrentTapePoolName) {
+    const std::string tapePoolName = "";
+  const std::string newTapePoolName = "new_tape_pool";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolName(m_admin, tapePoolName, newTapePoolName),
+    cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+TEST_P(cta_catalogue_TapePoolTest, modifyTapePoolName_emptyStringNewTapePoolName) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    comment);
+  {
+    const auto pools = m_catalogue->TapePool()->getTapePools();
+
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(isEncrypted, pool.encryption);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(comment, pool.comment);
+
+    const cta::common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const cta::common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const std::string newTapePoolName = "";
+  ASSERT_THROW(m_catalogue->TapePool()->modifyTapePoolName(m_admin, tapePoolName, newTapePoolName),
+    cta::catalogue::UserSpecifiedAnEmptyStringTapePoolName);
+}
+
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/TapePoolCatalogueTest.hpp b/catalogue/tests/modules/TapePoolCatalogueTest.hpp
new file mode 100644
index 0000000000..9701d7ee87
--- /dev/null
+++ b/catalogue/tests/modules/TapePoolCatalogueTest.hpp
@@ -0,0 +1,56 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_TapePoolTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_TapePoolTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::VirtualOrganization m_anotherVo;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::StorageClass m_anotherStorageClass;
+  const cta::catalogue::MediaType m_mediaType;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+};
+
+}  // namespace unitTests
diff --git a/catalogue/tests/modules/VirtualOrganizationCatalogueTest.cpp b/catalogue/tests/modules/VirtualOrganizationCatalogueTest.cpp
new file mode 100644
index 0000000000..801ba27dae
--- /dev/null
+++ b/catalogue/tests/modules/VirtualOrganizationCatalogueTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "catalogue/tests/CatalogueTestUtils.hpp"
+#include "catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/exception/UserError.hpp"
+#include "common/log/DummyLogger.hpp"
+#include "common/log/LogContext.hpp"
+#include "common/utils/utils.hpp"
+
+namespace unitTests {
+
+cta_catalogue_VirtualOrganizationTest::cta_catalogue_VirtualOrganizationTest()
+  : m_dummyLog("dummy", "dummy"),
+    m_admin(CatalogueTestUtils::getAdmin()),
+    m_vo(CatalogueTestUtils::getVo()),
+    m_storageClassSingleCopy(CatalogueTestUtils::getStorageClass()),
+    m_diskInstance(CatalogueTestUtils::getDiskInstance()),
+    m_tape1(CatalogueTestUtils::getTape1()) {
+}
+
+void cta_catalogue_VirtualOrganizationTest::SetUp() {
+  cta::log::LogContext dummyLc(m_dummyLog);
+  m_catalogue = CatalogueTestUtils::createCatalogue(GetParam(), &dummyLc);
+}
+
+void cta_catalogue_VirtualOrganizationTest::TearDown() {
+  m_catalogue.reset();
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, createVirtualOrganization) {
+  cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, createVirtualOrganizationAlreadyExists) {
+  cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+  ASSERT_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, createVirtualOrganizationAlreadyExistsCaseSensitive) {
+  cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+  cta::utils::toUpper(vo.name);
+  ASSERT_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, createVirtualOrganizationEmptyComment) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+  vo.comment = "";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, createVirtualOrganizationEmptyName) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  vo.name = "";
+  vo.comment = "comment";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, createVirtualOrganizationEmptyDiskInstanceName) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  vo.diskInstanceName = "";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, deleteVirtualOrganization) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  ASSERT_NO_THROW(m_catalogue->VO()->deleteVirtualOrganization(vo.name));
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, deleteVirtualOrganizationUsedByTapePool) {
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+  const std::string comment = "Create tape pool";
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, comment);
+
+  ASSERT_THROW(m_catalogue->VO()->deleteVirtualOrganization(m_vo.name), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, deleteVirtualOrganizationNameDoesNotExist) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  ASSERT_THROW(m_catalogue->VO()->deleteVirtualOrganization("DOES_NOT_EXIST"),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, deleteVirtualOrganizationUsedByStorageClass) {
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->StorageClass()->createStorageClass(m_admin, m_storageClassSingleCopy);
+  ASSERT_THROW(m_catalogue->VO()->deleteVirtualOrganization(m_vo.name), cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, getVirtualOrganizations) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  std::list<cta::common::dataStructures::VirtualOrganization> vos = m_catalogue->VO()->getVirtualOrganizations();
+  ASSERT_EQ(1,vos.size());
+
+  auto &voRetrieved = vos.front();
+  ASSERT_EQ(vo.name,voRetrieved.name);
+  ASSERT_EQ(vo.readMaxDrives,voRetrieved.readMaxDrives);
+  ASSERT_EQ(vo.writeMaxDrives,voRetrieved.writeMaxDrives);
+  ASSERT_EQ(vo.diskInstanceName,voRetrieved.diskInstanceName);
+  ASSERT_EQ(vo.comment,voRetrieved.comment);
+  
+  ASSERT_EQ(m_admin.host,voRetrieved.creationLog.host);
+  ASSERT_EQ(m_admin.username,voRetrieved.creationLog.username);
+  ASSERT_EQ(m_admin.host,voRetrieved.lastModificationLog.host);
+  ASSERT_EQ(m_admin.username,voRetrieved.lastModificationLog.username);
+
+
+  ASSERT_NO_THROW(m_catalogue->VO()->deleteVirtualOrganization(vo.name));
+  vos = m_catalogue->VO()->getVirtualOrganizations();
+  ASSERT_EQ(0,vos.size());
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationName) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  std::string newVoName = "NewVoName";
+
+  ASSERT_NO_THROW(m_catalogue->VO()->modifyVirtualOrganizationName(m_admin,vo.name,newVoName));
+
+  auto vos = m_catalogue->VO()->getVirtualOrganizations();
+
+  auto voFront = vos.front();
+  ASSERT_EQ(newVoName,voFront.name);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationNameVoDoesNotExists) {
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationName(m_admin,"DOES_NOT_EXIST","NEW_NAME"),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationNameThatAlreadyExists) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  std::string vo2Name = "vo2";
+  std::string vo1Name = vo.name;
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  vo.name = vo2Name;
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationName(m_admin,vo1Name,vo2Name),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationComment) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  std::string newComment = "newComment";
+
+  ASSERT_NO_THROW(m_catalogue->VO()->modifyVirtualOrganizationComment(m_admin,vo.name,newComment));
+
+  auto vos = m_catalogue->VO()->getVirtualOrganizations();
+  auto &frontVo = vos.front();
+
+  ASSERT_EQ(newComment, frontVo.comment);
+
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationComment(m_admin,"DOES not exists","COMMENT_DOES_NOT_EXIST"),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationReadMaxDrives) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  uint64_t readMaxDrives = 42;
+  ASSERT_NO_THROW(m_catalogue->VO()->modifyVirtualOrganizationReadMaxDrives(m_admin,vo.name,readMaxDrives));
+
+  auto vos = m_catalogue->VO()->getVirtualOrganizations();
+  auto &frontVo = vos.front();
+
+  ASSERT_EQ(readMaxDrives,frontVo.readMaxDrives);
+
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationReadMaxDrives(m_admin,"DOES not exists",readMaxDrives),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationWriteMaxDrives) {
+  cta::common::dataStructures::VirtualOrganization vo =CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  uint64_t writeMaxDrives = 42;
+  ASSERT_NO_THROW(m_catalogue->VO()->modifyVirtualOrganizationWriteMaxDrives(m_admin,vo.name,writeMaxDrives));
+
+  auto vos = m_catalogue->VO()->getVirtualOrganizations();
+  auto &frontVo = vos.front();
+
+  ASSERT_EQ(writeMaxDrives,frontVo.writeMaxDrives);
+
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationWriteMaxDrives(m_admin,"DOES not exists",writeMaxDrives),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationMaxFileSize) {
+  cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  uint64_t maxFileSize = 1;
+  ASSERT_NO_THROW(m_catalogue->VO()->modifyVirtualOrganizationMaxFileSize(m_admin,vo.name,maxFileSize));
+
+  auto vos = m_catalogue->VO()->getVirtualOrganizations();
+  auto &frontVo = vos.front();
+
+  ASSERT_EQ(maxFileSize,frontVo.maxFileSize);
+
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationMaxFileSize(m_admin,"DOES not exists", maxFileSize),
+    cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationDiskInstanceName) {
+  cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  std::string diskInstanceName = "diskInstanceName";
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, diskInstanceName, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->modifyVirtualOrganizationDiskInstanceName(m_admin,vo.name,diskInstanceName));
+
+  auto vos = m_catalogue->VO()->getVirtualOrganizations();
+  auto &frontVo = vos.front();
+
+  ASSERT_EQ(diskInstanceName,frontVo.diskInstanceName);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationDiskInstanceNameNonExistingVO) {
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationDiskInstanceName(m_admin,"DOES not exists",
+    "VO_DOES_NOT_EXIST"),cta::exception::UserError);
+}
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, modifyVirtualOrganizationDiskInstanceNameNonExistingDiskInstance) {
+  const cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  ASSERT_NO_THROW(m_catalogue->VO()->createVirtualOrganization(m_admin,vo));
+
+  const std::string diskInstanceName = "diskInstanceName";
+  ASSERT_THROW(m_catalogue->VO()->modifyVirtualOrganizationDiskInstanceName(m_admin,vo.name,diskInstanceName),
+    cta::exception::Exception);
+}
+
+
+TEST_P(cta_catalogue_VirtualOrganizationTest, getVirtualOrganizationOfTapepool) {
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const std::optional<std::string> supply("value for the supply pool mechanism");
+
+  cta::common::dataStructures::VirtualOrganization vo = CatalogueTestUtils::getVo();
+
+  m_catalogue->DiskInstance()->createDiskInstance(m_admin, m_diskInstance.name, m_diskInstance.comment);
+  m_catalogue->VO()->createVirtualOrganization(m_admin,vo);
+  m_catalogue->TapePool()->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply,
+    "Create tape pool");
+
+  cta::common::dataStructures::VirtualOrganization voFromTapepool =
+    m_catalogue->VO()->getVirtualOrganizationOfTapepool(m_tape1.tapePoolName);
+  ASSERT_EQ(vo,voFromTapepool);
+
+  ASSERT_THROW(m_catalogue->VO()->getVirtualOrganizationOfTapepool("DOES_NOT_EXIST"),cta::exception::Exception);
+}
+
+}  // namespace unitTests
\ No newline at end of file
diff --git a/catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp b/catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp
new file mode 100644
index 0000000000..fa02451549
--- /dev/null
+++ b/catalogue/tests/modules/VirtualOrganizationCatalogueTest.hpp
@@ -0,0 +1,52 @@
+/*
+ * @project      The CERN Tape Archive (CTA)
+ * @copyright    Copyright © 2022 CERN
+ * @license      This program is free software, distributed under the terms of the GNU General Public
+ *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+ *               redistribute it and/or modify it under the terms of the GPL Version 3, or (at your
+ *               option) any later version.
+ *
+ *               This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ *               WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ *               PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ *               In applying this licence, CERN does not waive the privileges and immunities
+ *               granted to it by virtue of its status as an Intergovernmental Organization or
+ *               submit itself to any jurisdiction.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "catalogue/Catalogue.hpp"
+#include "catalogue/CreateTapeAttributes.hpp"
+#include "common/dataStructures/DiskInstance.hpp"
+#include "common/dataStructures/SecurityIdentity.hpp"
+#include "common/dataStructures/StorageClass.hpp"
+#include "common/dataStructures/VirtualOrganization.hpp"
+#include "common/log/DummyLogger.hpp"
+
+namespace unitTests {
+
+class cta_catalogue_VirtualOrganizationTest : public ::testing::TestWithParam<cta::catalogue::CatalogueFactory **> {
+public:
+  cta_catalogue_VirtualOrganizationTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+protected:
+  cta::log::DummyLogger m_dummyLog;
+  std::unique_ptr<cta::catalogue::Catalogue> m_catalogue;
+
+  const cta::common::dataStructures::SecurityIdentity m_admin;
+  const cta::common::dataStructures::VirtualOrganization m_vo;
+  const cta::common::dataStructures::StorageClass m_storageClassSingleCopy;
+  const cta::common::dataStructures::DiskInstance m_diskInstance;
+  const cta::catalogue::CreateTapeAttributes m_tape1;
+};
+
+}  // namespace unitTests
diff --git a/disk/DiskSystem.cpp b/disk/DiskSystem.cpp
index e4bf79b69c..a1158a4d3d 100644
--- a/disk/DiskSystem.cpp
+++ b/disk/DiskSystem.cpp
@@ -146,7 +146,8 @@ void DiskSystemFreeSpaceList::fetchDiskSystemFreeSpace(const std::set<std::strin
     entry.targetedFreeSpace = m_systemList.at(ds).targetedFreeSpace;
 
     if (updateCatalogue) {
-      catalogue.modifyDiskInstanceSpaceFreeSpace(diskInstanceSpace.name, diskInstanceSpace.diskInstance, freeSpace);   
+      catalogue.DiskInstanceSpace()->modifyDiskInstanceSpaceFreeSpace(diskInstanceSpace.name,
+        diskInstanceSpace.diskInstance, freeSpace);
     }
   }
   if(failedToFetchDiskSystems.size()){
diff --git a/disk/DiskSystemTest.cpp b/disk/DiskSystemTest.cpp
index fcdd16347b..e26fef1763 100644
--- a/disk/DiskSystemTest.cpp
+++ b/disk/DiskSystemTest.cpp
@@ -74,13 +74,13 @@ namespace unitTests {
         // create disk instance
         std::string diskInstanceName = "DiskInstanceNameSpinner";
         std::string diskInstanceComment = "Comment";
-        catalogue.createDiskInstance(m_cliId, diskInstanceName, diskInstanceComment);
+        catalogue.DiskInstance()->createDiskInstance(m_cliId, diskInstanceName, diskInstanceComment);
         // create disk instance space
         std::string diskInstanceSpaceName = "DiskInstanceSpaceSpinner";
         std::string freeSpaceQueryURL = "eos:ctaeos:spinners";
         uint64_t refrestInterval = 1; 
         std::string diskInstanceSpaceComment = "Comment";
-        catalogue.createDiskInstanceSpace(m_cliId, diskInstanceSpaceName, diskInstanceName, freeSpaceQueryURL, refrestInterval, diskInstanceSpaceComment);
+        catalogue.DiskInstanceSpace()->createDiskInstanceSpace(m_cliId, diskInstanceSpaceName, diskInstanceName, freeSpaceQueryURL, refrestInterval, diskInstanceSpaceComment);
 
         m_diskSystemSpinner.name = "DiskSystemNameSpinner";
         m_diskSystemSpinner.fileRegexp = "root://ctaeos.archiveretrieve-1215709git0e38ccd0-xi98.svc.cluster.local//eos/ctaeos/cta(.*)eos.space=spinners";
@@ -88,7 +88,7 @@ namespace unitTests {
         m_diskSystemSpinner.sleepTime = 1;
         m_diskSystemSpinner.comment = "Comment";
 
-        catalogue.createDiskSystem(m_cliId,m_diskSystemSpinner.name, diskInstanceName, diskInstanceSpaceName, m_diskSystemSpinner.fileRegexp,
+        catalogue.DiskSystem()->createDiskSystem(m_cliId,m_diskSystemSpinner.name, diskInstanceName, diskInstanceSpaceName, m_diskSystemSpinner.fileRegexp,
           m_diskSystemSpinner.targetedFreeSpace, m_diskSystemSpinner.sleepTime,m_diskSystemSpinner.comment);
 
       }
@@ -98,13 +98,13 @@ namespace unitTests {
         // create disk instance
         std::string diskInstanceName = "ctaeos";
         std::string diskInstanceComment = "Comment";
-        catalogue.createDiskInstance(m_cliId, diskInstanceName, diskInstanceComment);
+        catalogue.DiskInstance()->createDiskInstance(m_cliId, diskInstanceName, diskInstanceComment);
         // create disk instance space
         std::string diskInstanceSpaceName = "DiskInstanceSpaceDefault";
         std::string freeSpaceQueryURL = "eosSpace:default";
         uint64_t refrestInterval = 1; 
         std::string diskInstanceSpaceComment = "Comment";
-        catalogue.createDiskInstanceSpace(m_cliId, diskInstanceSpaceName, diskInstanceName, freeSpaceQueryURL, refrestInterval, diskInstanceSpaceComment);
+        catalogue.DiskInstanceSpace()->createDiskInstanceSpace(m_cliId, diskInstanceSpaceName, diskInstanceName, freeSpaceQueryURL, refrestInterval, diskInstanceSpaceComment);
         // create disk system
         m_diskSystemDefault.name = "DiskSystemNameDefault";
         m_diskSystemDefault.fileRegexp = "root://ctaeos.archiveretrieve-1215709git0e38ccd0-xi98.svc.cluster.local//eos/ctaeos/cta(.*)eos.space=default";
@@ -112,7 +112,7 @@ namespace unitTests {
         m_diskSystemDefault.sleepTime = 1;
         m_diskSystemDefault.comment = "Comment";
 
-        catalogue.createDiskSystem(m_cliId,m_diskSystemDefault.name, diskInstanceName, diskInstanceSpaceName, m_diskSystemDefault.fileRegexp,
+        catalogue.DiskSystem()->createDiskSystem(m_cliId,m_diskSystemDefault.name, diskInstanceName, diskInstanceSpaceName, m_diskSystemDefault.fileRegexp,
           m_diskSystemDefault.targetedFreeSpace, m_diskSystemDefault.sleepTime,m_diskSystemDefault.comment);
 
       }
@@ -122,7 +122,7 @@ namespace unitTests {
     
     auto & catalogue = getCatalogue();
     
-    auto allDiskSystem = catalogue.getAllDiskSystems();
+    auto allDiskSystem = catalogue.DiskSystem()->getAllDiskSystems();
     
     std::string dstURL = "root://ctaeos.archiveretrieve-1215709git0e38ccd0-xi98.svc.cluster.local//eos/ctaeos/cta/54065a67-a3ea-4a44-b213-6f6a6f4e2cf4?eos.lfn=fxid:7&eos.ruid=0&eos.rgid=0&eos.injection=1&eos.workflow=retrieve_written&eos.space=spinners&toto=5";
     
@@ -144,13 +144,13 @@ namespace unitTests {
     // create disk instance
     std::string diskInstanceName = "DiskInstanceNameConstantFreeSpace";
     std::string diskInstanceComment = "Comment";
-    catalogue.createDiskInstance(m_cliId, diskInstanceName, diskInstanceComment);
+    catalogue.DiskInstance()->createDiskInstance(m_cliId, diskInstanceName, diskInstanceComment);
     // create disk instance space
     std::string diskInstanceSpaceName = "DiskInstanceSpaceConstantFreeSpace";
     std::string freeSpaceQueryURL = "constantFreeSpace:200";
     uint64_t refrestInterval = 1; 
     std::string diskInstanceSpaceComment = "Comment";
-    catalogue.createDiskInstanceSpace(m_cliId, diskInstanceSpaceName, diskInstanceName, freeSpaceQueryURL, refrestInterval, diskInstanceSpaceComment);
+    catalogue.DiskInstanceSpace()->createDiskInstanceSpace(m_cliId, diskInstanceSpaceName, diskInstanceName, freeSpaceQueryURL, refrestInterval, diskInstanceSpaceComment);
     // create disk system
     cta::disk::DiskSystem constantFreeSpaceDiskSystem;
 
@@ -160,10 +160,10 @@ namespace unitTests {
     constantFreeSpaceDiskSystem.sleepTime = 1;
     constantFreeSpaceDiskSystem.comment = "Comment";
     
-    catalogue.createDiskSystem(m_cliId,constantFreeSpaceDiskSystem.name, diskInstanceName, diskInstanceSpaceName, constantFreeSpaceDiskSystem.fileRegexp,
+    catalogue.DiskSystem()->createDiskSystem(m_cliId,constantFreeSpaceDiskSystem.name, diskInstanceName, diskInstanceSpaceName, constantFreeSpaceDiskSystem.fileRegexp,
       constantFreeSpaceDiskSystem.targetedFreeSpace, constantFreeSpaceDiskSystem.sleepTime,constantFreeSpaceDiskSystem.comment);
 
-    auto allDiskSystem = catalogue.getAllDiskSystems();
+    auto allDiskSystem = catalogue.DiskSystem()->getAllDiskSystems();
     
     cta::disk::DiskSystemFreeSpaceList diskSystemFreeSpaceList (allDiskSystem);
     std::set<std::string> diskSystemsToFetch {"DiskSystemNotExists"};
diff --git a/frontend-grpc/FrontendGRpcSvc.cpp b/frontend-grpc/FrontendGRpcSvc.cpp
index 6e76a5aa94..3ca755654f 100644
--- a/frontend-grpc/FrontendGRpcSvc.cpp
+++ b/frontend-grpc/FrontendGRpcSvc.cpp
@@ -220,7 +220,7 @@ Status CtaRpcImpl::Delete(::grpc::ServerContext* context, const ::cta::frontend:
     // Delete the file from the catalogue or from the objectstore if archive request is created
     cta::utils::Timer t;
     try {
-        deleteRequest.archiveFile = m_catalogue->getArchiveFileById(deleteRequest.archiveFileID);
+        deleteRequest.archiveFile = m_catalogue->ArchiveFile()->getArchiveFileById(deleteRequest.archiveFileID);
     } catch (cta::exception::Exception &ex){
         lc.log(cta::log::WARNING, "Deleted file is not in catalog.");
     }
diff --git a/frontend-grpc/Main.cpp b/frontend-grpc/Main.cpp
index 8335c79db9..ce134dfacf 100644
--- a/frontend-grpc/Main.cpp
+++ b/frontend-grpc/Main.cpp
@@ -135,8 +135,9 @@ int main(const int argc, char *const *const argv) {
                                                                        nbArchiveFileListingConns);
     auto catalogue = catalogueFactory->create();
     try {
-        catalogue->ping();
-        lc.log(log::INFO, "Connected to catalog " + catalogue->getSchemaVersion().getSchemaVersion<std::string>());
+        catalogue->Schema()->ping();
+        lc.log(log::INFO, "Connected to catalog " + catalogue->Schema()->getSchemaVersion().
+            getSchemaVersion<std::string>());
     } catch (cta::exception::Exception &ex) {
         lc.log(cta::log::CRIT, ex.getMessageValue());
         exit(1);
diff --git a/frontend/common/FrontendService.cpp b/frontend/common/FrontendService.cpp
index 0ad7331b71..4ed101bb0a 100644
--- a/frontend/common/FrontendService.cpp
+++ b/frontend/common/FrontendService.cpp
@@ -115,7 +115,7 @@ FrontendService::FrontendService(const std::string& configFilename) : m_archiveF
       catalogue_numberofconnections.value(), nbArchiveFileListingConns);
     m_catalogue = catalogueFactory->create();
     try {
-      m_catalogue->ping();
+      m_catalogue->Schema()->ping();
     } catch(cta::exception::Exception& ex) {
       auto lc = getLogContext();
       lc.log(cta::log::CRIT, ex.getMessageValue());
diff --git a/objectstore/AlgorithmsTest.cpp b/objectstore/AlgorithmsTest.cpp
index 32a13eaa62..46a2c7cb21 100644
--- a/objectstore/AlgorithmsTest.cpp
+++ b/objectstore/AlgorithmsTest.cpp
@@ -21,7 +21,7 @@
 #include "AgentReference.hpp"
 #include "ArchiveQueueAlgorithms.hpp"
 #include "BackendVFS.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "common/dataStructures/JobQueueType.hpp"
 #include "common/log/DummyLogger.hpp"
 #ifdef STDOUT_LOGGING
diff --git a/objectstore/GarbageCollectorTest.cpp b/objectstore/GarbageCollectorTest.cpp
index e49a07d779..f89d3e4fe9 100644
--- a/objectstore/GarbageCollectorTest.cpp
+++ b/objectstore/GarbageCollectorTest.cpp
@@ -24,7 +24,8 @@
 #include "ArchiveQueue.hpp"
 #include "ArchiveRequest.hpp"
 #include "BackendVFS.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyTapeCatalogue.hpp"
 #include "common/dataStructures/ArchiveFile.hpp"
 #include "common/dataStructures/JobQueueType.hpp"
 #include "common/exception/Exception.hpp"
@@ -618,9 +619,9 @@ TEST_F(ObjectStore, GarbageCollectorRetrieveRequest) {
     break;
   }
   // Mark the tape as enabled
-  catalogue.addEnabledTape("Tape0");
+  static_cast<cta::catalogue::DummyTapeCatalogue*>(catalogue.Tape().get())->addEnabledTape("Tape0");
   // Mark the other tape as disabled
-  catalogue.addDisabledTape("Tape1");
+  static_cast<cta::catalogue::DummyTapeCatalogue*>(catalogue.Tape().get())->addDisabledTape("Tape1");
   // Create the garbage collector and run it twice.
   cta::objectstore::AgentReference gcAgentRef("unitTestGarbageCollector", dl);
   cta::objectstore::Agent gcAgent(gcAgentRef.getAgentAddress(), be);
@@ -1668,7 +1669,7 @@ TEST_F(ObjectStore, GarbageCollectorRetrieveRequestRepackRepackingTape) {
   gcAgent.setTimeout_us(0);
   gcAgent.insertAndRegisterSelf(lc);
 
-  catalogue.addRepackingTape("Tape0");
+  static_cast<cta::catalogue::DummyTapeCatalogue*>(catalogue.Tape().get())->addRepackingTape("Tape0");
 
   cta::objectstore::GarbageCollector gc(be, gcAgentRef, catalogue);
   gc.runOnePass(lc);
diff --git a/objectstore/Helpers.cpp b/objectstore/Helpers.cpp
index 75ac3be204..8fd30b4678 100644
--- a/objectstore/Helpers.cpp
+++ b/objectstore/Helpers.cpp
@@ -392,7 +392,7 @@ std::string Helpers::selectBestRetrieveQueue(const std::set<std::string>& candid
       }
     }
     // Add in all the entries we need for this batch of candidates
-    auto tapeStatuses = catalogue.getTapesByVid(candidateVids);
+    auto tapeStatuses = catalogue.Tape()->getTapesByVid(candidateVids);
     for(auto& ts : tapeStatuses) {
       g_tapeStatuses[ts.first].tapeStatus = ts.second;
       g_tapeStatuses[ts.first].updateTime = time(nullptr);
@@ -450,7 +450,7 @@ std::string Helpers::selectBestRetrieveQueue(const std::set<std::string>& candid
       // Get the cached tape status value before releasing the lock
       if(g_tapeStatuses.find(v) == g_tapeStatuses.end()) {
         // Handle corner case where there are two candidate vids and the second candidate was evicted because it is stale
-        auto tapeStatuses = catalogue.getTapesByVid(v);
+        auto tapeStatuses = catalogue.Tape()->getTapesByVid(v);
         if(tapeStatuses.size() != 1) {
           throw cta::exception::Exception("In Helpers::selectBestRetrieveQueue(): candidate vid not found in the TAPE table.");
         }
diff --git a/objectstore/QueueCleanupRunner.cpp b/objectstore/QueueCleanupRunner.cpp
index 21a6c4228a..e9115e5359 100644
--- a/objectstore/QueueCleanupRunner.cpp
+++ b/objectstore/QueueCleanupRunner.cpp
@@ -76,7 +76,7 @@ void QueueCleanupRunner::runOnePass(log::LogContext &logContext) {
     cta::common::dataStructures::Tape tapeToCheck;
 
     try {
-      auto vidToTapesMap = m_catalogue.getTapesByVid(queue.vid); //throws an exception if the vid is not found on the database
+      auto vidToTapesMap = m_catalogue.Tape()->getTapesByVid(queue.vid); //throws an exception if the vid is not found on the database
       tapeToCheck = vidToTapesMap.at(queue.vid);
     } catch (const exception::UserError &ex) {
       log::ScopedParamContainer params(logContext);
@@ -189,7 +189,7 @@ void QueueCleanupRunner::runOnePass(log::LogContext &logContext) {
       cta::common::dataStructures::Tape tapeToModify;
 
       try {
-        auto vidToTapesMap = m_catalogue.getTapesByVid(qForCleanup.vid); //throws an exception if the vid is not found on the database
+        auto vidToTapesMap = m_catalogue.Tape()->getTapesByVid(qForCleanup.vid); //throws an exception if the vid is not found on the database
         tapeToModify = vidToTapesMap.at(qForCleanup.vid);
       } catch (const exception::UserError &ex) {
         log::ScopedParamContainer params(logContext);
@@ -204,15 +204,18 @@ void QueueCleanupRunner::runOnePass(log::LogContext &logContext) {
       std::optional<std::string> prevReason = tapeToModify.stateReason;
       switch (tapeToModify.state) {
       case common::dataStructures::Tape::REPACKING_PENDING:
-        m_catalogue.modifyTapeState(admin, qForCleanup.vid, common::dataStructures::Tape::REPACKING, common::dataStructures::Tape::REPACKING_PENDING, prevReason.value_or("QueueCleanupRunner: changed tape state to REPACKING"));
+        m_catalogue.Tape()->modifyTapeState(admin, qForCleanup.vid, common::dataStructures::Tape::REPACKING,
+          common::dataStructures::Tape::REPACKING_PENDING, prevReason.value_or("QueueCleanupRunner: changed tape state to REPACKING"));
         m_db.clearRetrieveQueueStatisticsCache(qForCleanup.vid);
         break;
       case common::dataStructures::Tape::BROKEN_PENDING:
-        m_catalogue.modifyTapeState(admin, qForCleanup.vid, common::dataStructures::Tape::BROKEN, common::dataStructures::Tape::BROKEN_PENDING, prevReason.value_or("QueueCleanupRunner: changed tape state to BROKEN"));
+        m_catalogue.Tape()->modifyTapeState(admin, qForCleanup.vid, common::dataStructures::Tape::BROKEN,
+          common::dataStructures::Tape::BROKEN_PENDING, prevReason.value_or("QueueCleanupRunner: changed tape state to BROKEN"));
         m_db.clearRetrieveQueueStatisticsCache(qForCleanup.vid);
         break;
       case common::dataStructures::Tape::EXPORTED_PENDING:
-        m_catalogue.modifyTapeState(admin, qForCleanup.vid, common::dataStructures::Tape::EXPORTED, common::dataStructures::Tape::EXPORTED_PENDING, prevReason.value_or("QueueCleanupRunner: changed tape state to EXPORTED"));
+        m_catalogue.Tape()->modifyTapeState(admin, qForCleanup.vid, common::dataStructures::Tape::EXPORTED,
+          common::dataStructures::Tape::EXPORTED_PENDING, prevReason.value_or("QueueCleanupRunner: changed tape state to EXPORTED"));
         m_db.clearRetrieveQueueStatisticsCache(qForCleanup.vid);
         break;
       default:
diff --git a/objectstore/QueueCleanupRunnerConcurrentTest.cpp b/objectstore/QueueCleanupRunnerConcurrentTest.cpp
index f37ba98443..877694b963 100644
--- a/objectstore/QueueCleanupRunnerConcurrentTest.cpp
+++ b/objectstore/QueueCleanupRunnerConcurrentTest.cpp
@@ -32,15 +32,16 @@
 #include <algorithm>
 #include <uuid/uuid.h>
 
-#include "objectstore/ObjectStoreFixture.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyTapeCatalogue.hpp"
+#include "common/log/StdoutLogger.hpp"
 #include "objectstore/BackendVFS.hpp"
 #include "objectstore/GarbageCollector.hpp"
+#include "objectstore/ObjectStoreFixture.hpp"
 #include "objectstore/QueueCleanupRunner.hpp"
 #include "scheduler/OStoreDB/OStoreDBFactory.hpp"
 #include "scheduler/OStoreDB/OStoreDBWithAgent.hpp"
 #include "scheduler/Scheduler.hpp"
-#include "common/log/StdoutLogger.hpp"
 
 #include "objectstore/QueueCleanupRunnerTestUtils.hpp"
 
@@ -298,7 +299,7 @@ TEST_P(QueueCleanupRunnerConcurrentTest, CleanupRunnerParameterizedTest) {
     auto initialRetrieveQueueToReportJobs = tapeQueueStateTrans.initialSetup.retrieveQueueToReportJobs;
 
     // Initial tape state
-    catalogue.modifyTapeState(dummyAdmin, vid, initialState, std::nullopt, "Testing");
+    catalogue.Tape()->modifyTapeState(dummyAdmin, vid, initialState, std::nullopt, "Testing");
 
     // Assert initial queue setup, for pre-validation of tests
     {
@@ -355,7 +356,8 @@ TEST_P(QueueCleanupRunnerConcurrentTest, CleanupRunnerParameterizedTest) {
     auto expectedRetrieveQueueToReportJobs = tapeQueueStateTrans.finalSetup.retrieveQueueToReportJobs;
 
     // Check final tape state
-    ASSERT_EQ(expectedState, catalogue.getTapeState(vid));
+    const auto tapeState = static_cast<cta::catalogue::DummyTapeCatalogue*>(catalogue.Tape().get())->getTapeState(vid);
+    ASSERT_EQ(expectedState, tapeState);
 
     // Assert final queue setup
     {
diff --git a/objectstore/QueueCleanupRunnerTest.cpp b/objectstore/QueueCleanupRunnerTest.cpp
index 0333a2614b..bce276abf5 100644
--- a/objectstore/QueueCleanupRunnerTest.cpp
+++ b/objectstore/QueueCleanupRunnerTest.cpp
@@ -32,15 +32,16 @@
 #include <algorithm>
 #include <uuid/uuid.h>
 
-#include "objectstore/ObjectStoreFixture.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyTapeCatalogue.hpp"
+#include "common/log/StdoutLogger.hpp"
 #include "objectstore/BackendVFS.hpp"
 #include "objectstore/GarbageCollector.hpp"
-#include "objectstore/QueueCleanupRunnerTestUtils.hpp"
+#include "objectstore/ObjectStoreFixture.hpp"
 #include "objectstore/QueueCleanupRunner.hpp"
+#include "objectstore/QueueCleanupRunnerTestUtils.hpp"
 #include "scheduler/OStoreDB/OStoreDBFactory.hpp"
 #include "scheduler/Scheduler.hpp"
-#include "common/log/StdoutLogger.hpp"
 
 //#define STDOUT_LOGGING
 
@@ -274,7 +275,7 @@ TEST_P(QueueCleanupRunnerTest, CleanupRunnerParameterizedTest) {
     auto initialRetrieveQueueToReportJobs = tapeQueueStateTrans.initialSetup.retrieveQueueToReportJobs;
 
     // Initial tape state
-    catalogue.modifyTapeState(dummyAdmin, vid, initialState, std::nullopt, "Testing");
+    catalogue.Tape()->modifyTapeState(dummyAdmin, vid, initialState, std::nullopt, "Testing");
 
     // Assert initial queue setup, for pre-validation of tests
     {
@@ -327,7 +328,8 @@ TEST_P(QueueCleanupRunnerTest, CleanupRunnerParameterizedTest) {
     auto finalRetrieveQueueToReportJobs = tapeQueueStateTrans.finalSetup.retrieveQueueToReportJobs;
 
     // Check final tape state
-    ASSERT_EQ(finalDesiredState, catalogue.getTapeState(vid));
+    const auto tapeState = static_cast<cta::catalogue::DummyTapeCatalogue*>(catalogue.Tape().get())->getTapeState(vid);
+    ASSERT_EQ(finalDesiredState, tapeState);
 
     // Assert final queue setup
     {
diff --git a/objectstore/SorterTest.cpp b/objectstore/SorterTest.cpp
index 122a1b4abe..7d9b674e3f 100644
--- a/objectstore/SorterTest.cpp
+++ b/objectstore/SorterTest.cpp
@@ -36,7 +36,7 @@
 #include "ArchiveQueue.hpp"
 #include "RetrieveQueue.hpp"
 #include "EntryLogSerDeser.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "Sorter.hpp"
 
 #include "ObjectStoreFixture.hpp"
diff --git a/scheduler/ArchiveMount.cpp b/scheduler/ArchiveMount.cpp
index 6cdaafdda7..eea55e9061 100644
--- a/scheduler/ArchiveMount.cpp
+++ b/scheduler/ArchiveMount.cpp
@@ -133,7 +133,7 @@ std::string cta::ArchiveMount::getMountTransactionId() const {
 // updateCatalogueWithTapeFilesWritten
 //------------------------------------------------------------------------------
 void cta::ArchiveMount::updateCatalogueWithTapeFilesWritten(const std::set<cta::catalogue::TapeItemWrittenPointer>& tapeFilesWritten) {
-  m_catalogue.filesWrittenToTape(tapeFilesWritten);
+  m_catalogue.TapeFile()->filesWrittenToTape(tapeFilesWritten);
 }
 
 //------------------------------------------------------------------------------
@@ -339,7 +339,7 @@ void cta::ArchiveMount::setTapeMounted(cta::log::LogContext& logContext) const {
   utils::Timer t;
   log::ScopedParamContainer spc(logContext);
   try {
-    m_catalogue.tapeMountedForArchive(m_dbMount->getMountInfo().vid, m_dbMount->getMountInfo().drive);
+    m_catalogue.Tape()->tapeMountedForArchive(m_dbMount->getMountInfo().vid, m_dbMount->getMountInfo().drive);
     auto catalogueTime = t.secs(cta::utils::Timer::resetCounter);
     spc.add("catalogueTime", catalogueTime);
     logContext.log(log::INFO, "In ArchiveMount::setTapeMounted(): success.");
@@ -355,5 +355,5 @@ void cta::ArchiveMount::setTapeMounted(cta::log::LogContext& logContext) const {
 // setTapeFull()
 //------------------------------------------------------------------------------
 void cta::ArchiveMount::setTapeFull() {
-  m_catalogue.noSpaceLeftOnTape(m_dbMount->getMountInfo().vid);
+  m_catalogue.Tape()->noSpaceLeftOnTape(m_dbMount->getMountInfo().vid);
 }
diff --git a/scheduler/OStoreDB/OStoreDB.cpp b/scheduler/OStoreDB/OStoreDB.cpp
index 66c347d65a..3c09822f29 100644
--- a/scheduler/OStoreDB/OStoreDB.cpp
+++ b/scheduler/OStoreDB/OStoreDB.cpp
@@ -229,7 +229,7 @@ std::list<SchedulerDatabase::RetrieveQueueCleanupInfo> OStoreDB::getRetrieveQueu
 void OStoreDB::fetchMountInfo(SchedulerDatabase::TapeMountDecisionInfo& tmdi, RootEntry& re,
   SchedulerDatabase::PurposeGetMountInfo purpose, log::LogContext & logContext) {
   utils::Timer t, t2;
-  std::list<common::dataStructures::MountPolicy> mountPolicies = m_catalogue.getCachedMountPolicies();
+  std::list<common::dataStructures::MountPolicy> mountPolicies = m_catalogue.MountPolicy()->getCachedMountPolicies();
   // Walk the archive queues for USER for statistics
   for (auto & aqp : re.dumpArchiveQueues(common::dataStructures::JobQueueType::JobsToTransferForUser)) {
     objectstore::ArchiveQueue aqueue(aqp.address, m_objectStore);
@@ -385,7 +385,7 @@ void OStoreDB::fetchMountInfo(SchedulerDatabase::TapeMountDecisionInfo& tmdi, Ro
     // mount candidates list.
     auto rqSummary = rqueue.getJobsSummary();
     bool isPotentialMount = false;
-    auto vidToTapeMap = m_catalogue.getTapesByVid(rqp.vid);
+    auto vidToTapeMap = m_catalogue.Tape()->getTapesByVid(rqp.vid);
     common::dataStructures::Tape::State tapeState = vidToTapeMap.at(rqp.vid).state;
     if (tapeState == common::dataStructures::Tape::ACTIVE ||
         tapeState == common::dataStructures::Tape::REPACKING) {
@@ -502,7 +502,7 @@ void OStoreDB::fetchMountInfo(SchedulerDatabase::TapeMountDecisionInfo& tmdi, Ro
   // If a next mount exists the drive "counts double", but the corresponding drive
   // is either about to mount, or about to replace its current mount.
   double registerFetchTime = 0;
-  const auto driveStates = m_catalogue.getTapeDrives();
+  const auto driveStates = m_catalogue.DriveState()->getTapeDrives();
   registerFetchTime = t.secs(utils::Timer::resetCounter);
   using common::dataStructures::DriveStatus;
   std::set<int> activeDriveStatuses = {
@@ -3671,12 +3671,12 @@ bool OStoreDB::RetrieveMount::testReserveDiskSpace(const cta::DiskSpaceReservati
   
   // Get the current file systems list from the catalogue
   cta::disk::DiskSystemList diskSystemList;
-  diskSystemList = m_oStoreDB.m_catalogue.getAllDiskSystems();
+  diskSystemList = m_oStoreDB.m_catalogue.DiskSystem()->getAllDiskSystems();
   diskSystemList.setExternalFreeDiskSpaceScript(externalFreeDiskSpaceScript);
   cta::disk::DiskSystemFreeSpaceList diskSystemFreeSpace(diskSystemList);
 
   // Get the existing reservation map from drives.
-  auto previousDrivesReservations = m_oStoreDB.m_catalogue.getDiskSpaceReservations();
+  auto previousDrivesReservations = m_oStoreDB.m_catalogue.DriveState()->getDiskSpaceReservations();
   // Get the free space from disk systems involved.
   std::set<std::string> diskSystemNames;
   for (auto const & dsrr: diskSpaceReservationRequest) {
@@ -3747,12 +3747,12 @@ bool OStoreDB::RetrieveMount::reserveDiskSpace(const cta::DiskSpaceReservationRe
 
   // Get the current file systems list from the catalogue
   cta::disk::DiskSystemList diskSystemList;
-  diskSystemList = m_oStoreDB.m_catalogue.getAllDiskSystems();
+  diskSystemList = m_oStoreDB.m_catalogue.DiskSystem()->getAllDiskSystems();
   diskSystemList.setExternalFreeDiskSpaceScript(externalFreeDiskSpaceScript);
   cta::disk::DiskSystemFreeSpaceList diskSystemFreeSpace(diskSystemList);
 
   // Get the existing reservation map from drives.
-  auto previousDrivesReservations = m_oStoreDB.m_catalogue.getDiskSpaceReservations();
+  auto previousDrivesReservations = m_oStoreDB.m_catalogue.DriveState()->getDiskSpaceReservations();
   // Get the free space from disk systems involved.
   std::set<std::string> diskSystemNames;
   for (auto const & dsrr: diskSpaceReservationRequest) {
@@ -3812,7 +3812,8 @@ bool OStoreDB::RetrieveMount::reserveDiskSpace(const cta::DiskSpaceReservationRe
     }
   }
 
-  m_oStoreDB.m_catalogue.reserveDiskSpace(mountInfo.drive, mountInfo.mountId, diskSpaceReservationRequest, logContext);
+  m_oStoreDB.m_catalogue.DriveState()->reserveDiskSpace(mountInfo.drive, mountInfo.mountId, diskSpaceReservationRequest,
+    logContext);
   return true;
 }
 
@@ -3975,7 +3976,8 @@ void OStoreDB::RetrieveMount::flushAsyncSuccessReports(std::list<cta::SchedulerD
       }
     }
   }
-  this->m_oStoreDB.m_catalogue.releaseDiskSpace(mountInfo.drive, mountInfo.mountId, diskSpaceReservationRequest, lc);
+  this->m_oStoreDB.m_catalogue.DriveState()->releaseDiskSpace(mountInfo.drive, mountInfo.mountId,
+    diskSpaceReservationRequest, lc);
   // 2) Queue the retrieve requests for repack.
   for (auto & repackRequestQueue: jobsToRequeueForRepackMap) {
     typedef objectstore::ContainerAlgorithms<RetrieveQueue,RetrieveQueueToReportToRepackForSuccess> RQTRTRFSAlgo;
@@ -4573,7 +4575,7 @@ objectstore::ArchiveRequest::RepackInfo OStoreDB::ArchiveJob::getRepackInfoAfter
 void OStoreDB::RepackArchiveSuccessesReportBatch::report(log::LogContext& lc) {
   objectstore::RepackRequest req(m_repackRequest.getAddressIfSet(),this->m_oStoreDb.m_objectStore);
   req.fetchNoLock();
-  this->m_oStoreDb.m_catalogue.setTapeDirty(req.getInfo().vid);
+  this->m_oStoreDb.m_catalogue.Tape()->setTapeDirty(req.getInfo().vid);
   OStoreDB::RepackArchiveReportBatch::report(lc);
 }
 
@@ -4937,7 +4939,7 @@ void OStoreDB::RetrieveJob::failTransfer(const std::string &failureReason, log::
   if (diskSystemName) {
     cta::DiskSpaceReservationRequest dsrr;
     dsrr.addRequest(diskSystemName.value(), archiveFile.fileSize);
-    this->m_oStoreDB.m_catalogue.releaseDiskSpace(m_retrieveMount->getMountInfo().drive,
+    this->m_oStoreDB.m_catalogue.DriveState()->releaseDiskSpace(m_retrieveMount->getMountInfo().drive,
     m_retrieveMount->getMountInfo().mountId, dsrr, lc);
   }
 
diff --git a/scheduler/OStoreDB/OStoreDBTest.cpp b/scheduler/OStoreDB/OStoreDBTest.cpp
index e4180c96f3..9863d36330 100644
--- a/scheduler/OStoreDB/OStoreDBTest.cpp
+++ b/scheduler/OStoreDB/OStoreDBTest.cpp
@@ -21,7 +21,7 @@
 #include <string>
 #include <utility>
 
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "catalogue/InMemoryCatalogue.hpp"
 #include "common/exception/Exception.hpp"
 #include "common/log/Logger.hpp"
diff --git a/scheduler/RetrieveMount.cpp b/scheduler/RetrieveMount.cpp
index 992f5bc49b..5e3bfd4660 100644
--- a/scheduler/RetrieveMount.cpp
+++ b/scheduler/RetrieveMount.cpp
@@ -356,7 +356,7 @@ void cta::RetrieveMount::setTapeMounted(cta::log::LogContext& logContext) const
   utils::Timer t;
   log::ScopedParamContainer spc(logContext);
   try {
-    m_catalogue.tapeMountedForRetrieve(m_dbMount->getMountInfo().vid, m_dbMount->getMountInfo().drive);
+    m_catalogue.Tape()->tapeMountedForRetrieve(m_dbMount->getMountInfo().vid, m_dbMount->getMountInfo().drive);
     auto catalogueTime = t.secs(cta::utils::Timer::resetCounter);
     spc.add("catalogueTime", catalogueTime);
     logContext.log(log::INFO, "In RetrieveMount::setTapeMounted(): success.");
diff --git a/scheduler/Scheduler.cpp b/scheduler/Scheduler.cpp
index 958255b396..fc02eccbc7 100644
--- a/scheduler/Scheduler.cpp
+++ b/scheduler/Scheduler.cpp
@@ -73,7 +73,7 @@ Scheduler::~Scheduler() throw() { }
 //------------------------------------------------------------------------------
 void Scheduler::ping(log::LogContext & lc) {
   cta::utils::Timer t;
-  m_catalogue.ping();
+  m_catalogue.Schema()->ping();
   auto catalogueTime = t.secs(cta::utils::Timer::resetCounter);
   m_db.ping();
   auto schedulerDbTime = t.secs(cta::utils::Timer::resetCounter);
@@ -98,7 +98,7 @@ void Scheduler::waitSchedulerDbSubthreadsComplete() {
 //------------------------------------------------------------------------------
 void Scheduler::authorizeAdmin(const common::dataStructures::SecurityIdentity &cliIdentity, log::LogContext & lc){
   cta::utils::Timer t;
-  if(!(m_catalogue.isAdmin(cliIdentity))) {
+  if(!(m_catalogue.AdminUser()->isAdmin(cliIdentity))) {
     std::stringstream msg;
     msg << "User: " << cliIdentity.username << " on host: " << cliIdentity.host << " is not authorized to execute CTA admin commands";
     throw exception::UserError(msg.str());
@@ -115,7 +115,8 @@ void Scheduler::authorizeAdmin(const common::dataStructures::SecurityIdentity &c
 uint64_t Scheduler::checkAndGetNextArchiveFileId(const std::string &instanceName,
   const std::string &storageClassName, const common::dataStructures::RequesterIdentity &user, log::LogContext &lc) {
   cta::utils::Timer t;
-  const uint64_t archiveFileId = m_catalogue.checkAndGetNextArchiveFileId(instanceName, storageClassName, user);
+  const uint64_t archiveFileId = m_catalogue.ArchiveFile()->checkAndGetNextArchiveFileId(instanceName, storageClassName,
+    user);
   const auto catalogueTime = t.secs();
   const auto schedulerDbTime = catalogueTime;
 
@@ -144,7 +145,7 @@ std::string Scheduler::queueArchiveWithGivenId(const uint64_t archiveFileId, con
   if (!request.fileSize)
     throw cta::exception::UserError(std::string("Rejecting archive request for zero-length file: ")+request.diskFileInfo.path);
 
-  const auto queueCriteria = m_catalogue.getArchiveFileQueueCriteria(instanceName, request.storageClass,
+  const auto queueCriteria = m_catalogue.ArchiveFile()->getArchiveFileQueueCriteria(instanceName, request.storageClass,
     request.requester);
   auto catalogueTime = t.secs(cta::utils::Timer::resetCounter);
 
@@ -198,10 +199,11 @@ std::string Scheduler::queueRetrieve(
   // Get the queue criteria
   common::dataStructures::RetrieveFileQueueCriteria queueCriteria;
 
-  queueCriteria = m_catalogue.prepareToRetrieveFile(instanceName, request.archiveFileID, request.requester, request.activity, lc, request.mountPolicy);
+  queueCriteria = m_catalogue.TapeFile()->prepareToRetrieveFile(instanceName, request.archiveFileID, request.requester,
+    request.activity, lc, request.mountPolicy);
   queueCriteria.archiveFile.diskFileInfo = request.diskFileInfo;
 
-  auto diskSystemList = m_catalogue.getAllDiskSystems();
+  auto diskSystemList = m_catalogue.DiskSystem()->getAllDiskSystems();
   auto catalogueTime = t.secs(cta::utils::Timer::resetCounter);
   // By default, the scheduler makes its decision based on all available vids. But if a vid is specified in the protobuf,
   // ignore all the others.
@@ -282,7 +284,7 @@ void Scheduler::deleteArchive(const std::string &instanceName, const common::dat
     // no need to do anything else, if file was failed it will not be in the catalogue.
   }
   tl.insertAndReset("schedulerDbTime",t);
-  m_catalogue.moveArchiveFileToRecycleLog(request,lc);
+  m_catalogue.ArchiveFile()->moveArchiveFileToRecycleLog(request,lc);
   tl.insertAndReset("catalogueTime",t);
   log::ScopedParamContainer spc(lc);
   tl.addToLog(spc);
@@ -302,7 +304,7 @@ void Scheduler::deleteFailed(const std::string &objectId, log::LogContext & lc)
 
 void Scheduler::checkTapeCanBeRepacked(const std::string & vid, const SchedulerDatabase::QueueRepackRequest & repackRequest){
   try{
-    auto vidToTapesMap = m_catalogue.getTapesByVid(vid); //throws an exception if the vid is not found on the database
+    auto vidToTapesMap = m_catalogue.Tape()->getTapesByVid(vid); //throws an exception if the vid is not found on the database
     cta::common::dataStructures::Tape tapeToCheck = vidToTapesMap.at(vid);
 
     if(!tapeToCheck.full){
@@ -478,7 +480,7 @@ void Scheduler::expandRepackRequest(std::unique_ptr<RepackRequest>& repackReques
 
   //We need to get the ArchiveRoutes to allow the retrieval of the tapePool in which the
   //tape where the file is is located
-  std::list<common::dataStructures::ArchiveRoute> routes = m_catalogue.getArchiveRoutes();
+  std::list<common::dataStructures::ArchiveRoute> routes = m_catalogue.ArchiveRoute()->getArchiveRoutes();
   timingList.insertAndReset("catalogueGetArchiveRoutesTime",t);
   //To identify the routes, we need to have both the dist instance name and the storage class name
   //thus, the key of the map is a pair of string
@@ -491,7 +493,8 @@ void Scheduler::expandRepackRequest(std::unique_ptr<RepackRequest>& repackReques
   cta::SchedulerDatabase::RepackRequest::TotalStatsFiles totalStatsFile;
   repackRequest->m_dbReq->fillLastExpandedFSeqAndTotalStatsFile(fSeq,totalStatsFile);
   timingList.insertAndReset("fillTotalStatsFileBeforeExpandTime",t);
-  cta::catalogue::Catalogue::ArchiveFileItor archiveFilesForCatalogue = m_catalogue.getArchiveFilesForRepackItor(repackInfo.vid, fSeq);
+  cta::catalogue::ArchiveFileItor archiveFilesForCatalogue = m_catalogue.ArchiveFile()->getArchiveFilesForRepackItor(
+    repackInfo.vid, fSeq);
   timingList.insertAndReset("catalogueGetArchiveFilesForRepackItorTime",t);
 
   std::stringstream dirBufferURL;
@@ -519,7 +522,7 @@ void Scheduler::expandRepackRequest(std::unique_ptr<RepackRequest>& repackReques
     }
   }
 
-  std::list<common::dataStructures::StorageClass> storageClasses = m_catalogue.getStorageClasses();
+  std::list<common::dataStructures::StorageClass> storageClasses = m_catalogue.StorageClass()->getStorageClasses();
 
   repackRequest->m_dbReq->setExpandStartedAndChangeStatus();
   uint64_t nbRetrieveSubrequestsQueued = 0;
@@ -677,7 +680,7 @@ void Scheduler::expandRepackRequest(std::unique_ptr<RepackRequest>& repackReques
       retrieveSubRequest.fileBufferURL = dirBufferURL.str() + fileName.str();
     }
   }
-  auto diskSystemList = m_catalogue.getAllDiskSystems();
+  auto diskSystemList = m_catalogue.DiskSystem()->getAllDiskSystems();
   timingList.insertAndReset("getDisksystemsListTime",t);
   try{
     // Note: the highest fSeq will be recorded internally in the following call.
@@ -795,7 +798,7 @@ void Scheduler::RepackReportBatch::report(log::LogContext& lc) {
 //------------------------------------------------------------------------------
 common::dataStructures::DesiredDriveState Scheduler::getDesiredDriveState(const std::string& driveName, log::LogContext & lc) {
   utils::Timer t;
-  const auto driveStates = m_catalogue.getTapeDrives();
+  const auto driveStates = m_catalogue.DriveState()->getTapeDrives();
   for (const auto & driveState : driveStates) {
     if (driveState.driveName == driveName) {
       const auto schedulerDbTime = t.secs();
@@ -920,7 +923,7 @@ std::map<std::string, std::list<common::dataStructures::ArchiveJob> > Scheduler:
 //------------------------------------------------------------------------------
 std::list<common::dataStructures::ArchiveJob> Scheduler::getPendingArchiveJobs(const std::string &tapePoolName, log::LogContext & lc) const {
   utils::Timer t;
-  if(!m_catalogue.tapePoolExists(tapePoolName)) {
+  if(!m_catalogue.TapePool()->tapePoolExists(tapePoolName)) {
     throw exception::UserError(std::string("Tape pool ") + tapePoolName + " does not exist");
   }
   auto catalogueTime = t.secs(utils::Timer::resetCounter);
@@ -965,7 +968,7 @@ std::list<common::dataStructures::RetrieveJob> Scheduler::getPendingRetrieveJobs
 std::optional<cta::common::dataStructures::TapeDrive> Scheduler::getDriveState(const std::string& tapeDriveName,
   log::LogContext* lc) const {
   utils::Timer t;
-  auto ret = m_catalogue.getTapeDrive(tapeDriveName);
+  auto ret = m_catalogue.DriveState()->getTapeDrive(tapeDriveName);
   auto schedulerDbTime = t.secs();
   log::ScopedParamContainer spc(*lc);
   spc.add("schedulerDbTime", schedulerDbTime);
@@ -978,7 +981,7 @@ std::optional<cta::common::dataStructures::TapeDrive> Scheduler::getDriveState(c
 //------------------------------------------------------------------------------
 std::list<common::dataStructures::TapeDrive> Scheduler::getDriveStates(const common::dataStructures::SecurityIdentity &cliIdentity, log::LogContext & lc) const {
   utils::Timer t;
-  const auto ret = m_catalogue.getTapeDrives();
+  const auto ret = m_catalogue.DriveState()->getTapeDrives();
   auto schedulerDbTime = t.secs();
   log::ScopedParamContainer spc(lc);
   spc.add("schedulerDbTime", schedulerDbTime);
@@ -999,7 +1002,7 @@ void Scheduler::sortAndGetTapesForMountInfo(std::unique_ptr<SchedulerDatabase::T
   // we can filter the potential mounts to the ones that this tape server can serve.
   catalogue::TapeSearchCriteria searchCriteria;
   searchCriteria.logicalLibrary = logicalLibraryName;
-  auto eligibleTapesList = m_catalogue.getTapes(searchCriteria);
+  auto eligibleTapesList = m_catalogue.Tape()->getTapes(searchCriteria);
   std::set<std::string> eligibleTapeSet;
   for(auto& t : eligibleTapesList) {
     eligibleTapeSet.insert(t.vid);
@@ -1022,7 +1025,7 @@ void Scheduler::sortAndGetTapesForMountInfo(std::unique_ptr<SchedulerDatabase::T
 
   common::dataStructures::VidToTapeMap retrieveTapesInfo;
   if(!retrieveTapeSet.empty()) {
-    retrieveTapesInfo = m_catalogue.getTapesByVid(retrieveTapeSet);
+    retrieveTapesInfo = m_catalogue.Tape()->getTapesByVid(retrieveTapeSet);
     getTapeInfoTime = timer.secs(utils::Timer::resetCounter);
     for(auto& m : mountInfo->potentialMounts) {
       if(m.type == common::dataStructures::MountType::Retrieve) {
@@ -1049,7 +1052,7 @@ void Scheduler::sortAndGetTapesForMountInfo(std::unique_ptr<SchedulerDatabase::T
   std::map<std::string,common::dataStructures::VirtualOrganization> tapepoolVoMap;
   for (auto & tapepool: tapepoolsPotentialOrExistingMounts) {
     try {
-      tapepoolVoMap[tapepool] = m_catalogue.getCachedVirtualOrganizationOfTapepool(tapepool);
+      tapepoolVoMap[tapepool] = m_catalogue.VO()->getCachedVirtualOrganizationOfTapepool(tapepool);
     } catch (cta::exception::Exception & ex){
       //The VO of this tapepool does not exist, abort the scheduling as we need it to know the number of allocated drives
       //the VO is allowed to use
@@ -1200,7 +1203,7 @@ void Scheduler::sortAndGetTapesForMountInfo(std::unique_ptr<SchedulerDatabase::T
         // https://trac.cppcheck.net/ticket/10739
         // cppcheck-suppress internalAstError
         [](decltype(*mountInfo->potentialMounts.cbegin())& m){ return common::dataStructures::getMountBasicType(m.type) == common::dataStructures::MountType::ArchiveAllTypes; } )) {
-    tapeList = m_catalogue.getTapesForWriting(logicalLibraryName);
+    tapeList = m_catalogue.Tape()->getTapesForWriting(logicalLibraryName);
     getTapeForWriteTime = timer.secs(utils::Timer::resetCounter);
   }
 
@@ -1220,7 +1223,7 @@ void Scheduler::sortAndGetTapesForMountInfo(std::unique_ptr<SchedulerDatabase::T
 //------------------------------------------------------------------------------
 std::optional<common::dataStructures::LogicalLibrary> Scheduler::getLogicalLibrary(const std::string& libraryName, double& getLogicalLibraryTime){
   utils::Timer timer;
-  auto logicalLibraries = m_catalogue.getLogicalLibraries();
+  auto logicalLibraries = m_catalogue.LogicalLibrary()->getLogicalLibraries();
   std::optional<common::dataStructures::LogicalLibrary> ret;
   auto logicalLibraryItor = std::find_if(logicalLibraries.begin(),logicalLibraries.end(),[libraryName](const cta::common::dataStructures::LogicalLibrary& ll){
     return (ll.name == libraryName);
@@ -1693,7 +1696,7 @@ std::list<common::dataStructures::QueueAndMountSummary> Scheduler::getQueuesAndM
 
   // Obtain a map of vids to tape info from the catalogue
   utils::Timer catalogueVidToLogicalLibraryTimer;
-  const auto vid_to_logical_library = m_catalogue.getVidToLogicalLibrary(tapesWithAQueue);
+  const auto vid_to_logical_library = m_catalogue.Tape()->getVidToLogicalLibrary(tapesWithAQueue);
   const auto catalogueVidToLogicalLibraryTime = catalogueVidToLogicalLibraryTimer.secs();
 
   for (auto & pm: mountDecisionInfo->potentialMounts) {
@@ -1773,11 +1776,11 @@ std::list<common::dataStructures::QueueAndMountSummary> Scheduler::getQueuesAndM
   for (auto & mountOrQueue: ret) {
     if (common::dataStructures::MountType::ArchiveForUser==mountOrQueue.mountType || common::dataStructures::MountType::ArchiveForRepack==mountOrQueue.mountType) {
       utils::Timer catalogueGetTapePoolTimer;
-      const auto tapePool = m_catalogue.getTapePool(mountOrQueue.tapePool);
+      const auto tapePool = m_catalogue.TapePool()->getTapePool(mountOrQueue.tapePool);
       catalogueGetTapePoolTotalTime += catalogueGetTapePoolTimer.secs();
       if (tapePool) {
         utils::Timer catalogueGetVoTimer;
-        const auto vo = m_catalogue.getCachedVirtualOrganizationOfTapepool(tapePool->name);
+        const auto vo = m_catalogue.VO()->getCachedVirtualOrganizationOfTapepool(tapePool->name);
         catalogueGetVoTotalTime += catalogueGetVoTimer.secs();
         mountOrQueue.vo = vo.name;
         mountOrQueue.readMaxDrives = vo.readMaxDrives;
@@ -1793,14 +1796,14 @@ std::list<common::dataStructures::QueueAndMountSummary> Scheduler::getQueuesAndM
       cta::catalogue::TapeSearchCriteria tsc;
       tsc.vid = mountOrQueue.vid;
       utils::Timer catalogueGetTapesTimer;
-      auto tapes=m_catalogue.getTapes(tsc);
+      auto tapes=m_catalogue.Tape()->getTapes(tsc);
       catalogueGetTapesTotalTime += catalogueGetTapesTimer.secs();
       if (tapes.size() != 1) {
         throw cta::exception::Exception("In Scheduler::getQueuesAndMountSummaries(): got unexpected number of tapes from catalogue for a retrieve.");
       }
       auto &t=tapes.front();
       utils::Timer catalogueGetVoTimer;
-      const auto vo = m_catalogue.getCachedVirtualOrganizationOfTapepool(t.tapePoolName);
+      const auto vo = m_catalogue.VO()->getCachedVirtualOrganizationOfTapepool(t.tapePoolName);
       catalogueGetVoTotalTime += catalogueGetVoTimer.secs();
       mountOrQueue.vo = vo.name;
       mountOrQueue.readMaxDrives = vo.readMaxDrives;
@@ -1831,12 +1834,12 @@ void Scheduler::triggerTapeStateChange(const common::dataStructures::SecurityIde
   using Tape = common::dataStructures::Tape;
 
   // Tape must exist on catalogue
-  if (!m_catalogue.tapeExists(vid)) {
+  if (!m_catalogue.Tape()->tapeExists(vid)) {
     throw cta::exception::UserError("The VID " + vid + " does not exist");
   }
 
   // Validate tape state change based on previous state
-  auto prev_state = m_catalogue.getTapesByVid(vid)[vid].state;
+  auto prev_state = m_catalogue.Tape()->getTapesByVid(vid)[vid].state;
 
   // If previous and desired states are the same, do nothing
   if (prev_state == new_state) return;
@@ -1889,11 +1892,11 @@ void Scheduler::triggerTapeStateChange(const common::dataStructures::SecurityIde
   case Tape::DISABLED:
   case Tape::REPACKING_DISABLED:
     // Simply set the new tape state
-    m_catalogue.modifyTapeState(admin, vid, new_state, prev_state, stateReason);
+    m_catalogue.Tape()->modifyTapeState(admin, vid, new_state, prev_state, stateReason);
     break;
   case Tape::BROKEN:
     try {
-      m_catalogue.modifyTapeState(admin, vid, Tape::BROKEN_PENDING, prev_state, stateReason);
+      m_catalogue.Tape()->modifyTapeState(admin, vid, Tape::BROKEN_PENDING, prev_state, stateReason);
     } catch (catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive & ex) {
       throw catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive(
               std::regex_replace(ex.getMessageValue(), std::regex(Tape::stateToString(Tape::BROKEN_PENDING)), Tape::stateToString(Tape::BROKEN)));
@@ -1903,10 +1906,10 @@ void Scheduler::triggerTapeStateChange(const common::dataStructures::SecurityIde
   case Tape::REPACKING:
     if (prev_state == Tape::REPACKING_DISABLED) {
       // If tape is on REPACKING_DISABLED state, move it directly to REPACKING
-      m_catalogue.modifyTapeState(admin, vid, new_state, prev_state, stateReason);
+      m_catalogue.Tape()->modifyTapeState(admin, vid, new_state, prev_state, stateReason);
     } else {
       try {
-        m_catalogue.modifyTapeState(admin, vid, Tape::REPACKING_PENDING, prev_state, stateReason);
+        m_catalogue.Tape()->modifyTapeState(admin, vid, Tape::REPACKING_PENDING, prev_state, stateReason);
       } catch (catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive & ex) {
         throw catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive(
                 std::regex_replace(ex.getMessageValue(), std::regex(Tape::stateToString(Tape::REPACKING_PENDING)), Tape::stateToString(Tape::REPACKING)));
@@ -1916,7 +1919,7 @@ void Scheduler::triggerTapeStateChange(const common::dataStructures::SecurityIde
     break;
   case Tape::EXPORTED:
     try {
-      m_catalogue.modifyTapeState(admin, vid, Tape::EXPORTED_PENDING, prev_state, stateReason);
+      m_catalogue.Tape()->modifyTapeState(admin, vid, Tape::EXPORTED_PENDING, prev_state, stateReason);
     } catch (catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive & ex) {
       throw catalogue::UserSpecifiedAnEmptyStringReasonWhenTapeStateNotActive(
               std::regex_replace(ex.getMessageValue(), std::regex(Tape::stateToString(Tape::EXPORTED_PENDING)), Tape::stateToString(Tape::EXPORTED)));
diff --git a/scheduler/SchedulerDatabaseTest.cpp b/scheduler/SchedulerDatabaseTest.cpp
index d61ddb797b..294f3a9d61 100644
--- a/scheduler/SchedulerDatabaseTest.cpp
+++ b/scheduler/SchedulerDatabaseTest.cpp
@@ -22,7 +22,7 @@
 #include <exception>
 #include <future>
 
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "catalogue/InMemoryCatalogue.hpp"
 #include "common/dataStructures/SecurityIdentity.hpp"
 #include "common/log/DummyLogger.hpp"
@@ -640,14 +640,14 @@ TEST_P(SchedulerDatabaseTest, popRetrieveRequestsWithDisksytem) {
   cta::SchedulerDatabase &db = getDb();
   cta::catalogue::Catalogue &catalogue = getCatalogue();
 
-  catalogue.createDiskInstance(common::dataStructures::SecurityIdentity(), "di", "No comment");
-  catalogue.createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-A", "di", "constantFreeSpace:999999999999", 60, "No comment");
-  catalogue.createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-A", "di", "dis-A", "$root://a.disk.system/", 10UL*1000*1000*1000, 15*60, "No comment");
+  catalogue.DiskInstance()->createDiskInstance(common::dataStructures::SecurityIdentity(), "di", "No comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-A", "di", "constantFreeSpace:999999999999", 60, "No comment");
+  catalogue.DiskSystem()->createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-A", "di", "dis-A", "$root://a.disk.system/", 10UL*1000*1000*1000, 15*60, "No comment");
 
-  catalogue.createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-B", "di", "constantFreeSpace:999999999999", 60, "No comment");
-  catalogue.createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-B", "di", "dis-B", "$root://b.disk.system/", 10UL*1000*1000*1000, 15*60,"No comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-B", "di", "constantFreeSpace:999999999999", 60, "No comment");
+  catalogue.DiskSystem()->createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-B", "di", "dis-B", "$root://b.disk.system/", 10UL*1000*1000*1000, 15*60,"No comment");
 
-  auto diskSystemList = catalogue.getAllDiskSystems();
+  auto diskSystemList = catalogue.DiskSystem()->getAllDiskSystems();
 
   // Inject 10 retrieve jobs to the db.
   const size_t filesToDo = 10;
@@ -734,11 +734,11 @@ TEST_P(SchedulerDatabaseTest, popRetrieveRequestsWithBackpressure) {
   // Create the disk system list
   // only one disk system per queue, like in the production
 
-  catalogue.createDiskInstance(common::dataStructures::SecurityIdentity(), "di", "No comment");
-  catalogue.createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-A", "di", "constantFreeSpace:6000", 60, "No comment");
-  catalogue.createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-A", "di", "dis-A", "$root://a.disk.system/", 0UL, 15*60, "No comment");
+  catalogue.DiskInstance()->createDiskInstance(common::dataStructures::SecurityIdentity(), "di", "No comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-A", "di", "constantFreeSpace:6000", 60, "No comment");
+  catalogue.DiskSystem()->createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-A", "di", "dis-A", "$root://a.disk.system/", 0UL, 15*60, "No comment");
 
-  auto diskSystemList = catalogue.getAllDiskSystems();
+  auto diskSystemList = catalogue.DiskSystem()->getAllDiskSystems();
 
   // Inject 10 retrieve jobs to the db.
   const size_t filesToDo = 10;
@@ -827,13 +827,13 @@ TEST_P(SchedulerDatabaseTest, popRetrieveRequestsWithDiskSystemNotFetcheable) {
   cta::SchedulerDatabase &db = getDb();
   cta::catalogue::Catalogue &catalogue = getCatalogue();
 
-  catalogue.createDiskInstance(common::dataStructures::SecurityIdentity(), "di", "No comment");
-  catalogue.createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-error", "di", "constantFreeSpace-6000", 60, "No comment");
+  catalogue.DiskInstance()->createDiskInstance(common::dataStructures::SecurityIdentity(), "di", "No comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace(common::dataStructures::SecurityIdentity(), "dis-error", "di", "constantFreeSpace-6000", 60, "No comment");
 
-  catalogue.createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-Error", "di", "dis-error", "$root://error.disk.system/", 0UL,
+  catalogue.DiskSystem()->createDiskSystem(common::dataStructures::SecurityIdentity(), "ds-Error", "di", "dis-error", "$root://error.disk.system/", 0UL,
     15*60,"No comment");
 
-  auto diskSystemList = catalogue.getAllDiskSystems();
+  auto diskSystemList = catalogue.DiskSystem()->getAllDiskSystems();
   // Inject 10 retrieve jobs to the db.
   const size_t filesToDo = 10;
   std::list<std::future<void>> jobInsertions;
diff --git a/scheduler/SchedulerTest.cpp b/scheduler/SchedulerTest.cpp
index a0d5e64ae6..013f6ed0b3 100644
--- a/scheduler/SchedulerTest.cpp
+++ b/scheduler/SchedulerTest.cpp
@@ -27,6 +27,7 @@
 #include "catalogue/InMemoryCatalogue.hpp"
 #include "catalogue/MediaType.hpp"
 #include "catalogue/SchemaCreatingSqliteCatalogue.hpp"
+#include "catalogue/TapeFileWritten.hpp"
 #include "catalogue/TapeItemWrittenPointer.hpp"
 #include "common/dataStructures/DiskInstance.hpp"
 #include "common/dataStructures/JobQueueType.hpp"
@@ -213,13 +214,13 @@ public:
     mountPolicy.minRetrieveRequestAge = minRetrieveRequestAge;
     mountPolicy.comment = mountPolicyComment;
 
-    ASSERT_TRUE(catalogue.getMountPolicies().empty());
+    ASSERT_TRUE(catalogue.MountPolicy()->getMountPolicies().empty());
 
-    catalogue.createMountPolicy(
+    catalogue.MountPolicy()->createMountPolicy(
       s_adminOnAdminHost,
       mountPolicy);
 
-    const std::list<common::dataStructures::MountPolicy> groups = catalogue.getMountPolicies();
+    const std::list<common::dataStructures::MountPolicy> groups = catalogue.MountPolicy()->getMountPolicies();
     ASSERT_EQ(1, groups.size());
     const common::dataStructures::MountPolicy group = groups.front();
     ASSERT_EQ(mountPolicyName, group.name);
@@ -229,12 +230,12 @@ public:
     ASSERT_EQ(minRetrieveRequestAge, group.retrieveMinRequestAge);
     ASSERT_EQ(mountPolicyComment, group.comment);
 
-    m_catalogue->createDiskInstance(s_adminOnAdminHost, s_diskInstance, "comment");
+    m_catalogue->DiskInstance()->createDiskInstance(s_adminOnAdminHost, s_diskInstance, "comment");
 
     const std::string ruleComment = "create requester mount-rule";
-    catalogue.createRequesterMountRule(s_adminOnAdminHost, mountPolicyName, s_diskInstance, s_userName, ruleComment);
+    catalogue.RequesterMountRule()->createRequesterMountRule(s_adminOnAdminHost, mountPolicyName, s_diskInstance, s_userName, ruleComment);
 
-    const std::list<common::dataStructures::RequesterMountRule> rules = catalogue.getRequesterMountRules();
+    const auto rules = catalogue.RequesterMountRule()->getRequesterMountRules();
     ASSERT_EQ(1, rules.size());
 
     const common::dataStructures::RequesterMountRule rule = rules.front();
@@ -253,24 +254,24 @@ public:
     vo.readMaxDrives = 1;
     vo.maxFileSize = 0;
     vo.diskInstanceName = s_diskInstance;
-    m_catalogue->createVirtualOrganization(s_adminOnAdminHost,vo);
+    m_catalogue->VO()->createVirtualOrganization(s_adminOnAdminHost,vo);
 
     common::dataStructures::StorageClass storageClass;
     storageClass.name = s_storageClassName;
     storageClass.nbCopies = 1;
     storageClass.vo.name = vo.name;
     storageClass.comment = "create storage class";
-    m_catalogue->createStorageClass(s_adminOnAdminHost, storageClass);
+    m_catalogue->StorageClass()->createStorageClass(s_adminOnAdminHost, storageClass);
 
     const uint16_t nbPartialTapes = 1;
     const std::string tapePoolComment = "Tape-pool comment";
     const bool tapePoolEncryption = false;
     const std::optional<std::string> tapePoolSupply("value for the supply pool mechanism");
-    catalogue.createTapePool(s_adminOnAdminHost, s_tapePoolName, vo.name, nbPartialTapes, tapePoolEncryption, tapePoolSupply,
-      tapePoolComment);
+    catalogue.TapePool()->createTapePool(s_adminOnAdminHost, s_tapePoolName, vo.name, nbPartialTapes, tapePoolEncryption,
+      tapePoolSupply, tapePoolComment);
     const uint32_t copyNb = 1;
     const std::string archiveRouteComment = "Archive-route comment";
-    catalogue.createArchiveRoute(s_adminOnAdminHost, s_storageClassName, copyNb, s_tapePoolName,
+    catalogue.ArchiveRoute()->createArchiveRoute(s_adminOnAdminHost, s_storageClassName, copyNb, s_tapePoolName,
       archiveRouteComment);
 
     cta::catalogue::MediaType mediaType;
@@ -278,14 +279,14 @@ public:
     mediaType.capacityInBytes = s_mediaTypeCapacityInBytes;
     mediaType.cartridge = "cartridge";
     mediaType.comment = "comment";
-    catalogue.createMediaType(s_adminOnAdminHost, mediaType);
+    catalogue.MediaType()->createMediaType(s_adminOnAdminHost, mediaType);
 
     const std::string driveName = "tape_drive";
     const auto tapeDrive = getDefaultTapeDrive(driveName);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
     const std::string driveName2 = "drive0";
     const auto tapeDrive2 = getDefaultTapeDrive(driveName2);
-    catalogue.createTapeDrive(tapeDrive2);
+    catalogue.DriveState()->createTapeDrive(tapeDrive2);
   }
 
   cta::catalogue::CreateTapeAttributes getDefaultTape() {
@@ -545,10 +546,10 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file) {
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = true;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -556,11 +557,11 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
-  catalogue.tapeLabelled(s_vid, driveName);
+  catalogue.Tape()->tapeLabelled(s_vid, driveName);
 
   {
     // Emulate a tape server by asking for a mount and then a file (and succeed the transfer)
@@ -572,7 +573,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file) {
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     //Test that no mount is available when a logical library is disabled
     ASSERT_EQ(nullptr, mount.get());
-    catalogue.setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
+    catalogue.LogicalLibrary()->setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
     //continue our test
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     ASSERT_NE(nullptr, mount.get());
@@ -749,10 +750,10 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file_with_specific_mount_p
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = true;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -760,12 +761,12 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file_with_specific_mount_p
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
 
-  catalogue.tapeLabelled(s_vid, "tape_drive");
+  catalogue.Tape()->tapeLabelled(s_vid, "tape_drive");
 
   {
     // Emulate a tape server by asking for a mount and then a file (and succeed the transfer)
@@ -777,7 +778,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file_with_specific_mount_p
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     //Test that no mount is available when a logical library is disabled
     ASSERT_EQ(nullptr, mount.get());
-    catalogue.setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
+    catalogue.LogicalLibrary()->setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
     //continue our test
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     ASSERT_NE(nullptr, mount.get());
@@ -831,7 +832,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_file_with_specific_mount_p
     mountPolicy.minRetrieveRequestAge = s_minRetrieveRequestAge;
     mountPolicy.comment = "custom mount policy";
 
-    catalogue.createMountPolicy(s_adminOnAdminHost, mountPolicy);
+    catalogue.MountPolicy()->createMountPolicy(s_adminOnAdminHost, mountPolicy);
   }
 
   {
@@ -938,13 +939,13 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     mountPolicy.minRetrieveRequestAge = minRetrieveRequestAge;
     mountPolicy.comment = mountPolicyComment;
 
-    ASSERT_TRUE(catalogue.getMountPolicies().empty());
+    ASSERT_TRUE(catalogue.MountPolicy()->getMountPolicies().empty());
 
-    catalogue.createMountPolicy(
+    catalogue.MountPolicy()->createMountPolicy(
       s_adminOnAdminHost,
       mountPolicy);
 
-    const std::list<common::dataStructures::MountPolicy> groups = catalogue.getMountPolicies();
+    const std::list<common::dataStructures::MountPolicy> groups = catalogue.MountPolicy()->getMountPolicies();
     ASSERT_EQ(1, groups.size());
     const common::dataStructures::MountPolicy group = groups.front();
     ASSERT_EQ(mountPolicyName, group.name);
@@ -957,12 +958,13 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     cta::common::dataStructures::DiskInstance di;
     di.name = s_diskInstance;
     di.comment = "comment";
-    catalogue.createDiskInstance(s_adminOnAdminHost, di.name, di.comment);
+    catalogue.DiskInstance()->createDiskInstance(s_adminOnAdminHost, di.name, di.comment);
 
     const std::string ruleComment = "create requester mount-rule";
-    catalogue.createRequesterMountRule(s_adminOnAdminHost, mountPolicyName, di.name, s_userName, ruleComment);
+    catalogue.RequesterMountRule()->createRequesterMountRule(s_adminOnAdminHost, mountPolicyName, di.name, s_userName,
+      ruleComment);
 
-    const std::list<common::dataStructures::RequesterMountRule> rules = catalogue.getRequesterMountRules();
+    const auto rules = catalogue.RequesterMountRule()->getRequesterMountRules();
     ASSERT_EQ(1, rules.size());
 
     const common::dataStructures::RequesterMountRule rule = rules.front();
@@ -981,32 +983,32 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     vo.readMaxDrives = 1;
     vo.maxFileSize = 0;
     vo.diskInstanceName = s_diskInstance;
-    catalogue.createVirtualOrganization(s_adminOnAdminHost,vo);
+    catalogue.VO()->createVirtualOrganization(s_adminOnAdminHost,vo);
 
     common::dataStructures::StorageClass storageClass;
     storageClass.name = dualCopyStorageClassName;
     storageClass.nbCopies = 2;
     storageClass.vo.name = vo.name;
     storageClass.comment = "create dual copy storage class";
-    catalogue.createStorageClass(s_adminOnAdminHost, storageClass);
+    catalogue.StorageClass()->createStorageClass(s_adminOnAdminHost, storageClass);
 
     const uint16_t nbPartialTapes = 1;
     const std::string tapePool1Comment = "Tape-pool for copy number 1";
     const std::string tapePool2Comment = "Tape-pool for copy number 2";
     const bool tapePoolEncryption = false;
     const std::optional<std::string> tapePoolSupply("value for the supply pool mechanism");
-    catalogue.createTapePool(s_adminOnAdminHost, tapePool1Name, vo.name, nbPartialTapes, tapePoolEncryption,
+    catalogue.TapePool()->createTapePool(s_adminOnAdminHost, tapePool1Name, vo.name, nbPartialTapes, tapePoolEncryption,
       tapePoolSupply, tapePool1Comment);
-    catalogue.createTapePool(s_adminOnAdminHost, tapePool2Name, vo.name, nbPartialTapes, tapePoolEncryption,
+    catalogue.TapePool()->createTapePool(s_adminOnAdminHost, tapePool2Name, vo.name, nbPartialTapes, tapePoolEncryption,
       tapePoolSupply, tapePool2Comment);
 
     const std::string archiveRoute1Comment = "Archive-route for copy number 1";
     const std::string archiveRoute2Comment = "Archive-route for copy number 2";
     const uint32_t archiveRoute1CopyNb = 1;
     const uint32_t archiveRoute2CopyNb = 2;
-    catalogue.createArchiveRoute(s_adminOnAdminHost, dualCopyStorageClassName, archiveRoute1CopyNb, tapePool1Name,
+    catalogue.ArchiveRoute()->createArchiveRoute(s_adminOnAdminHost, dualCopyStorageClassName, archiveRoute1CopyNb, tapePool1Name,
       archiveRoute1Comment);
-    catalogue.createArchiveRoute(s_adminOnAdminHost, dualCopyStorageClassName, archiveRoute2CopyNb, tapePool2Name,
+    catalogue.ArchiveRoute()->createArchiveRoute(s_adminOnAdminHost, dualCopyStorageClassName, archiveRoute2CopyNb, tapePool2Name,
       archiveRoute1Comment);
 
     cta::catalogue::MediaType mediaType;
@@ -1014,14 +1016,14 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     mediaType.capacityInBytes = s_mediaTypeCapacityInBytes;
     mediaType.cartridge = "cartridge";
     mediaType.comment = "comment";
-    catalogue.createMediaType(s_adminOnAdminHost, mediaType);
+    catalogue.MediaType()->createMediaType(s_adminOnAdminHost, mediaType);
 
     const std::string driveName = "tape_drive";
     const auto tapeDrive = getDefaultTapeDrive(driveName);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
     const std::string driveName2 = "drive0";
     const auto tapeDrive2 = getDefaultTapeDrive(driveName2);
-    catalogue.createTapeDrive(tapeDrive2);
+    catalogue.DriveState()->createTapeDrive(tapeDrive2);
   }
 
 #ifdef STDOUT_LOGGING
@@ -1076,10 +1078,10 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
   // tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = true;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1098,12 +1100,12 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     tape.full = false;
     tape.state = common::dataStructures::Tape::ACTIVE;
     tape.comment = "Comment";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
 
-  catalogue.tapeLabelled(copy1TapeVid, driveName);
+  catalogue.Tape()->tapeLabelled(copy1TapeVid, driveName);
 
   // Archive copy 1 to tape
   {
@@ -1117,7 +1119,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     //Test that no mount is available when a logical library is disabled
     ASSERT_EQ(nullptr, mount.get());
-    catalogue.setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
+    catalogue.LogicalLibrary()->setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
     //continue our test
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     ASSERT_NE(nullptr, mount.get());
@@ -1159,7 +1161,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
 
   // Create the environment for the migration of copy 2 to happen (library +
   // tape)
-  catalogue.setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,true);
+  catalogue.LogicalLibrary()->setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,true);
   const std::string copy2TapeVid = "COPY_2_TAPE";
   {
     using namespace cta;
@@ -1173,10 +1175,10 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     tape.full = false;
     tape.state = common::dataStructures::Tape::ACTIVE;
     tape.comment = "Comment";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
-  catalogue.tapeLabelled(copy2TapeVid, driveName);
+  catalogue.Tape()->tapeLabelled(copy2TapeVid, driveName);
 
   // Archive copy 2 to tape
   {
@@ -1189,7 +1191,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     //Test that no mount is available when a logical library is disabled
     ASSERT_EQ(nullptr, mount.get());
-    catalogue.setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
+    catalogue.LogicalLibrary()->setLogicalLibraryDisabled(s_adminOnAdminHost,s_libraryName,false);
     //continue our test
     mount.reset(scheduler.getNextMount(s_libraryName, driveName, lc).release());
     ASSERT_NE(nullptr, mount.get());
@@ -1240,7 +1242,7 @@ TEST_P(SchedulerTest, archive_report_and_retrieve_new_dual_copy_file) {
     requester.group = "userGroup";
     std::optional<std::string> activity;
     const common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
-      catalogue.prepareToRetrieveFile(s_diskInstance, archiveFileId, requester, activity, lc);
+      catalogue.TapeFile()->prepareToRetrieveFile(s_diskInstance, archiveFileId, requester, activity, lc);
     ASSERT_EQ(2, queueCriteria.archiveFile.tapeFiles.size());
 
     std::map<uint8_t, common::dataStructures::TapeFile> copyNbToTape;
@@ -1399,10 +1401,10 @@ TEST_P(SchedulerTest, archive_and_retrieve_failure) {
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1410,12 +1412,12 @@ TEST_P(SchedulerTest, archive_and_retrieve_failure) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
 
-  catalogue.tapeLabelled(s_vid, driveName);
+  catalogue.Tape()->tapeLabelled(s_vid, driveName);
 
   {
     // Emulate a tape server by asking for a mount and then a file (and succeed the transfer)
@@ -1649,10 +1651,10 @@ TEST_P(SchedulerTest, archive_and_retrieve_report_failure) {
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1660,12 +1662,12 @@ TEST_P(SchedulerTest, archive_and_retrieve_report_failure) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
 
-  catalogue.tapeLabelled(s_vid, driveName);
+  catalogue.Tape()->tapeLabelled(s_vid, driveName);
 
   {
     // Emulate a tape server by asking for a mount and then a file (and succeed the transfer)
@@ -1893,10 +1895,10 @@ TEST_P(SchedulerTest, retry_archive_until_max_reached) {
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1904,11 +1906,11 @@ TEST_P(SchedulerTest, retry_archive_until_max_reached) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
-  catalogue.tapeLabelled(s_vid, driveName);
+  catalogue.Tape()->tapeLabelled(s_vid, driveName);
 
   {
     // Emulate a tape server by asking for a mount and then a file
@@ -2025,7 +2027,7 @@ TEST_P(SchedulerTest, repack) {
    // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
 
   common::dataStructures::SecurityIdentity cliId;
@@ -2038,7 +2040,7 @@ TEST_P(SchedulerTest, repack) {
     tape.vid = tape1;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(cliId, tape);
+    catalogue.Tape()->createTape(cliId, tape);
   }
 
   //The queueing of a repack request should fail if the tape to repack is not full
@@ -2049,7 +2051,7 @@ TEST_P(SchedulerTest, repack) {
   qrr.m_vid = "NOT_EXIST";
   ASSERT_THROW(scheduler.queueRepack(cliId, qrr, lc),cta::exception::UserError);
 
-  catalogue.setTapeFull(cliId,tape1,true);
+  catalogue.Tape()->setTapeFull(cliId,tape1,true);
 
   // Create and then cancel repack
   qrr.m_vid = tape1;
@@ -2066,12 +2068,12 @@ TEST_P(SchedulerTest, repack) {
   std::string tape2 = "TAPE2";
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
     tape.vid = tape2;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
     tape.full = true;
-    catalogue.createTape(cliId, tape);
+    catalogue.Tape()->createTape(cliId, tape);
   }
   qrr.m_vid = tape2;
   scheduler.queueRepack(cliId, qrr, lc);
@@ -2104,7 +2106,7 @@ TEST_P(SchedulerTest, getNextRepackRequestToExpand) {
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
 
   common::dataStructures::SecurityIdentity cliId;
@@ -2117,7 +2119,7 @@ TEST_P(SchedulerTest, getNextRepackRequestToExpand) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(cliId, tape);
+    catalogue.Tape()->createTape(cliId, tape);
   }
 
   //Queue the first repack request
@@ -2133,7 +2135,7 @@ TEST_P(SchedulerTest, getNextRepackRequestToExpand) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(cliId, tape);
+    catalogue.Tape()->createTape(cliId, tape);
   }
 
   //Queue the second repack request
@@ -2179,9 +2181,9 @@ TEST_P(SchedulerTest, expandRepackRequest) {
   auto &schedulerDB = getSchedulerDB();
 
   setupDefaultCatalogue();
-  catalogue.createDiskInstance({"user", "host"}, "diskInstance", "no comment");
-  catalogue.createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "constantFreeSpace:10", 10, "no comment");
-  catalogue.createDiskSystem({"user", "host"}, "diskSystem", "diskInstance", "diskInstanceSpace", "/public_dir/public_file", 10L*1000*1000*1000, 15*60, "no comment");
+  catalogue.DiskInstance()->createDiskInstance({"user", "host"}, "diskInstance", "no comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "constantFreeSpace:10", 10, "no comment");
+  catalogue.DiskSystem()->createDiskSystem({"user", "host"}, "diskSystem", "diskInstance", "diskInstanceSpace", "/public_dir/public_file", 10L*1000*1000*1000, 15*60, "no comment");
 
 
 #ifdef STDOUT_LOGGING
@@ -2202,7 +2204,7 @@ TEST_P(SchedulerTest, expandRepackRequest) {
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   uint64_t nbTapesToRepack = 10;
   uint64_t nbTapesForTest = 2; //corresponds to the targetAvailableRequests variable in the Scheduler::promoteRepackRequestsToToExpand() method
@@ -2221,7 +2223,7 @@ TEST_P(SchedulerTest, expandRepackRequest) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -2265,7 +2267,7 @@ TEST_P(SchedulerTest, expandRepackRequest) {
         tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
       }
       //update the DB tape
-      catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
       tapeFilesWrittenCopy1.clear();
     }
   }
@@ -2533,7 +2535,7 @@ TEST_P(SchedulerTest, expandRepackRequestRetrieveFailed) {
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -2545,7 +2547,7 @@ TEST_P(SchedulerTest, expandRepackRequestRetrieveFailed) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -2588,7 +2590,7 @@ TEST_P(SchedulerTest, expandRepackRequestRetrieveFailed) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the expandRepackRequest method
@@ -2739,7 +2741,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) {
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -2751,7 +2753,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a repack destination tape
@@ -2760,7 +2762,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) {
   {
     auto tape = getDefaultTape();
     tape.vid = vidDestination;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -2803,7 +2805,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the expandRepackRequest method
@@ -2998,7 +3000,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveFailed) {
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3010,7 +3012,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveFailed) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a repack destination tape
@@ -3018,7 +3020,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveFailed) {
   {
     auto tape = getDefaultTape();
     tape.vid = vidDestinationRepack;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -3061,7 +3063,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveFailed) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the expandRepackRequest method
@@ -3262,7 +3264,7 @@ TEST_P(SchedulerTest, expandRepackRequestRepackingTape) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3274,7 +3276,7 @@ TEST_P(SchedulerTest, expandRepackRequestRepackingTape) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -3317,7 +3319,7 @@ TEST_P(SchedulerTest, expandRepackRequestRepackingTape) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   // Queue the repack request for a repacking tape
@@ -3369,7 +3371,8 @@ TEST_P(SchedulerTest, expandRepackRequestRepackingDisabledTape) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled,
+    "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3381,7 +3384,7 @@ TEST_P(SchedulerTest, expandRepackRequestRepackingDisabledTape) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING_DISABLED;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -3424,7 +3427,7 @@ TEST_P(SchedulerTest, expandRepackRequestRepackingDisabledTape) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   // Queue the repack request for a repacking tape
@@ -3476,7 +3479,7 @@ TEST_P(SchedulerTest, expandRepackRequestBrokenTape) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3488,7 +3491,7 @@ TEST_P(SchedulerTest, expandRepackRequestBrokenTape) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::BROKEN;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   {
@@ -3538,7 +3541,7 @@ TEST_P(SchedulerTest, expandRepackRequestDisabledTape) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3550,12 +3553,12 @@ TEST_P(SchedulerTest, expandRepackRequestDisabledTape) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::DISABLED;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   {
     cta::SchedulerDatabase::QueueRepackRequest qrr(vid,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveOnly,
-                                                   common::dataStructures::MountPolicy::s_defaultMountPolicyForRepack,s_defaultRepackNoRecall);
+                                                  common::dataStructures::MountPolicy::s_defaultMountPolicyForRepack,s_defaultRepackNoRecall);
     ASSERT_THROW(scheduler.queueRepack(admin,qrr,lc),cta::exception::UserError);
     scheduler.waitSchedulerDbSubthreadsComplete();
 
@@ -3600,7 +3603,7 @@ TEST_P(SchedulerTest, expandRepackRequestActiveTape) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3612,12 +3615,12 @@ TEST_P(SchedulerTest, expandRepackRequestActiveTape) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::ACTIVE;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   {
     cta::SchedulerDatabase::QueueRepackRequest qrr(vid,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveOnly,
-                                                   common::dataStructures::MountPolicy::s_defaultMountPolicyForRepack,s_defaultRepackNoRecall);
+      common::dataStructures::MountPolicy::s_defaultMountPolicyForRepack,s_defaultRepackNoRecall);
     ASSERT_THROW(scheduler.queueRepack(admin,qrr,lc),cta::exception::UserError);
     scheduler.waitSchedulerDbSubthreadsComplete();
 
@@ -3631,7 +3634,8 @@ TEST_P(SchedulerTest, expandRepackRequestActiveTape) {
     ASSERT_EQ(nullptr,repackRequestToExpand);
   }
 }
-/* Disabled tapes should be ok to be mounted, because it is a transient state
+
+/*
 TEST_P(SchedulerTest, noMountIsTriggeredWhenTapeIsDisabled) {
   using namespace cta;
   using namespace cta::objectstore;
@@ -3662,7 +3666,7 @@ TEST_P(SchedulerTest, noMountIsTriggeredWhenTapeIsDisabled) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3672,7 +3676,7 @@ TEST_P(SchedulerTest, noMountIsTriggeredWhenTapeIsDisabled) {
     auto tape = getDefaultTape();
     tape.vid = vid;
     tape.full = true;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -3715,7 +3719,7 @@ TEST_P(SchedulerTest, noMountIsTriggeredWhenTapeIsDisabled) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the queueing of the Retrieve Request and try to mount after having disabled the tape
@@ -3733,20 +3737,20 @@ TEST_P(SchedulerTest, noMountIsTriggeredWhenTapeIsDisabled) {
   //disabled the tape
 
   std::string disabledReason = "Disabled reason";
-  catalogue.setTapeDisabled(admin,vid,disabledReason);
+  catalogue.Tape()->setTapeDisabled(admin,vid,disabledReason);
   const std::string driveName = "tape_drive";
 
   //No mount should be returned by getNextMount
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
   //enable the tape
-  catalogue.modifyTapeState(admin,vid,common::dataStructures::Tape::ACTIVE,std::nullopt,std::nullopt);
+  catalogue.Tape()->modifyTapeState(admin, vid, common::dataStructures::Tape::ACTIVE, std::nullopt, std::nullopt);
 
   //A mount should be returned by getNextMount
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName,driveName,lc));
 
   //disable the tape
-  catalogue.setTapeDisabled(admin,vid,disabledReason);
+  catalogue.Tape()->setTapeDisabled(admin,vid,disabledReason);
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName,driveName,lc));
 
   //create repack mount policy
@@ -3765,9 +3769,9 @@ TEST_P(SchedulerTest, noMountIsTriggeredWhenTapeIsDisabled) {
   mountPolicy.minRetrieveRequestAge = minRetrieveRequestAge;
   mountPolicy.comment = mountPolicyComment;
 
-  catalogue.createMountPolicy(s_adminOnAdminHost, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(s_adminOnAdminHost, mountPolicy);
 
-  auto mountPolicies = catalogue.getMountPolicies();
+  auto mountPolicies = catalogue.MountPolicy()->getMountPolicies();
 
   auto mountPolicyItor = std::find_if(mountPolicies.begin(),mountPolicies.end(), [](const common::dataStructures::MountPolicy &mountPolicy){
         return mountPolicy.name.rfind("repack", 0) == 0;
@@ -3834,7 +3838,7 @@ TEST_P(SchedulerTest, emptyMountIsTriggeredWhenCancelledRetrieveRequest) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -3844,7 +3848,7 @@ TEST_P(SchedulerTest, emptyMountIsTriggeredWhenCancelledRetrieveRequest) {
     auto tape = getDefaultTape();
     tape.vid = vid;
     tape.full = true;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -3887,7 +3891,7 @@ TEST_P(SchedulerTest, emptyMountIsTriggeredWhenCancelledRetrieveRequest) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the queueing of the Retrieve Request and try to mount after having disabled the tape
@@ -3906,7 +3910,7 @@ TEST_P(SchedulerTest, emptyMountIsTriggeredWhenCancelledRetrieveRequest) {
   }
   //disabled the tape
   std::string disabledReason = "reason";
-  catalogue.setTapeDisabled(admin,vid,disabledReason);
+  catalogue.Tape()->setTapeDisabled(admin,vid,disabledReason);
 
   //No mount should be returned by getNextMount
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
@@ -4004,10 +4008,10 @@ TEST_P(SchedulerTest, DISABLED_archiveReportMultipleAndQueueRetrievesWithActivit
   // Create the environment for the migrations to happen (library + tapes)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -4017,8 +4021,8 @@ TEST_P(SchedulerTest, DISABLED_archiveReportMultipleAndQueueRetrievesWithActivit
     auto tape = getDefaultTape();
     std::string vid = s_vid + std::to_string(i);
     tape.vid = vid;
-    catalogue.createTape(s_adminOnAdminHost, tape);
-    catalogue.tapeLabelled(vid, driveName);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->tapeLabelled(vid, driveName);
   }
 
 
@@ -4190,7 +4194,7 @@ TEST_P(SchedulerTest, expandRepackRequestAddCopiesOnly) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   //Create the source tape
   std::string vid = "VIDSOURCE";
@@ -4200,27 +4204,27 @@ TEST_P(SchedulerTest, expandRepackRequestAddCopiesOnly) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create two different destination tapepool
   std::string tapepool2Name = "tapepool2";
   const std::optional<std::string> supply;
-  catalogue.createTapePool(admin,tapepool2Name,"vo",1,false,supply,"comment");
+  catalogue.TapePool()->createTapePool(admin,tapepool2Name,"vo",1,false,supply,"comment");
 
   std::string tapepool3Name = "tapepool3";
-  catalogue.createTapePool(admin,tapepool3Name,"vo",1,false,supply,"comment");
+  catalogue.TapePool()->createTapePool(admin,tapepool3Name,"vo",1,false,supply,"comment");
 
   //Create a storage class in the catalogue
   common::dataStructures::StorageClass storageClass;
   storageClass.name = s_storageClassName;
   storageClass.nbCopies = 3;
   storageClass.comment = "Create storage class";
-  catalogue.modifyStorageClassNbCopies(admin,storageClass.name,storageClass.nbCopies);
+  catalogue.StorageClass()->modifyStorageClassNbCopies(admin,storageClass.name,storageClass.nbCopies);
 
   //Create the two archive routes for the new copies
-  catalogue.createArchiveRoute(admin,storageClass.name,2,tapepool2Name,"ArchiveRoute2");
-  catalogue.createArchiveRoute(admin,storageClass.name,3,tapepool3Name,"ArchiveRoute3");
+  catalogue.ArchiveRoute()->createArchiveRoute(admin,storageClass.name,2,tapepool2Name,"ArchiveRoute2");
+  catalogue.ArchiveRoute()->createArchiveRoute(admin,storageClass.name,3,tapepool3Name,"ArchiveRoute3");
 
   //Create two other destinationTape
   std::string vidDestination1 = "VIDDESTINATION1";
@@ -4228,7 +4232,7 @@ TEST_P(SchedulerTest, expandRepackRequestAddCopiesOnly) {
     auto tape = getDefaultTape();
     tape.vid = vidDestination1;
     tape.tapePoolName = tapepool2Name;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   std::string vidDestination2 = "VIDDESTINATION2";
@@ -4236,7 +4240,7 @@ TEST_P(SchedulerTest, expandRepackRequestAddCopiesOnly) {
     auto tape = getDefaultTape();
     tape.vid = vidDestination2;
     tape.tapePoolName = tapepool3Name;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string tapeDrive = "tape_drive";
@@ -4273,7 +4277,7 @@ TEST_P(SchedulerTest, expandRepackRequestAddCopiesOnly) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the expanding requeue the Repack after the creation of
@@ -4442,7 +4446,7 @@ TEST_P(SchedulerTest, expandRepackRequestShouldFailIfArchiveRouteMissing) {
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   //Create the source tape
   std::string vidCopyNb1 = "VIDSOURCE";
@@ -4452,23 +4456,23 @@ TEST_P(SchedulerTest, expandRepackRequestShouldFailIfArchiveRouteMissing) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create two different destination tapepool
   std::string tapepool2Name = "tapepool2";
   const std::optional<std::string> supply;
-  catalogue.createTapePool(admin,tapepool2Name,"vo",1,false,supply,"comment");
+  catalogue.TapePool()->createTapePool(admin,tapepool2Name,"vo",1,false,supply,"comment");
 
   //Create a storage class in the catalogue
   common::dataStructures::StorageClass storageClass;
   storageClass.name = s_storageClassName;
   storageClass.nbCopies = 2;
   storageClass.comment = "Create storage class";
-  catalogue.modifyStorageClassNbCopies(admin,storageClass.name,storageClass.nbCopies);
+  catalogue.StorageClass()->modifyStorageClassNbCopies(admin,storageClass.name,storageClass.nbCopies);
 
   //Create the one archive route for the second copy
-  catalogue.createArchiveRoute(admin,storageClass.name,2,tapepool2Name,"ArchiveRoute3");
+  catalogue.ArchiveRoute()->createArchiveRoute(admin,storageClass.name,2,tapepool2Name,"ArchiveRoute3");
 
   //Create two other destinationTape
   std::string vidCopyNb2_source = "VIDCOPYNB2_SOURCE";
@@ -4478,7 +4482,7 @@ TEST_P(SchedulerTest, expandRepackRequestShouldFailIfArchiveRouteMissing) {
     tape.tapePoolName = tapepool2Name;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   std::string vidCopyNb2_destination = "VIDCOPYNB2_DESTINATION";
@@ -4486,7 +4490,7 @@ TEST_P(SchedulerTest, expandRepackRequestShouldFailIfArchiveRouteMissing) {
     auto tape = getDefaultTape();
     tape.vid = vidCopyNb2_destination;
     tape.tapePoolName = tapepool2Name;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string tapeDrive = "tape_drive";
@@ -4523,7 +4527,7 @@ TEST_P(SchedulerTest, expandRepackRequestShouldFailIfArchiveRouteMissing) {
       tapeFilesWrittenCopy.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy);
     tapeFilesWrittenCopy.clear();
   }
   //Archive the second copy of the files in the tape located in the tapepool2
@@ -4555,12 +4559,12 @@ TEST_P(SchedulerTest, expandRepackRequestShouldFailIfArchiveRouteMissing) {
       tapeFilesWrittenCopy.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy);
     tapeFilesWrittenCopy.clear();
   }
-  catalogue.setTapeFull(admin,vidCopyNb2_source,true);
+  catalogue.Tape()->setTapeFull(admin,vidCopyNb2_source,true);
   //Delete the archive route of the second copy and repack the tape that contains these second copies
-  catalogue.deleteArchiveRoute(storageClass.name,2);
+  catalogue.ArchiveRoute()->deleteArchiveRoute(storageClass.name,2);
   {
     std::string vid = vidCopyNb2_source;
     cta::SchedulerDatabase::QueueRepackRequest qrr(vid,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveAndAddCopies,
@@ -4624,7 +4628,7 @@ TEST_P(SchedulerTest, expandRepackRequestMoveAndAddCopies){
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   //Create the source tape
   std::string vid = "VIDSOURCE";
@@ -4634,27 +4638,27 @@ TEST_P(SchedulerTest, expandRepackRequestMoveAndAddCopies){
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create two different destination tapepool
   std::string tapepool2Name = "tapepool2";
   const std::optional<std::string> supply;
-  catalogue.createTapePool(admin,tapepool2Name,"vo",1,false,supply,"comment");
+  catalogue.TapePool()->createTapePool(admin,tapepool2Name,"vo",1,false,supply,"comment");
 
   std::string tapepool3Name = "tapepool3";
-  catalogue.createTapePool(admin,tapepool3Name,"vo",1,false,supply,"comment");
+  catalogue.TapePool()->createTapePool(admin,tapepool3Name,"vo",1,false,supply,"comment");
 
   //Create a storage class in the catalogue
   common::dataStructures::StorageClass storageClass;
   storageClass.name = s_storageClassName;
   storageClass.nbCopies = 3;
   storageClass.comment = "Create storage class";
-  catalogue.modifyStorageClassNbCopies(admin,storageClass.name,storageClass.nbCopies);
+  catalogue.StorageClass()->modifyStorageClassNbCopies(admin,storageClass.name,storageClass.nbCopies);
 
   //Create the two archive routes for the new copies
-  catalogue.createArchiveRoute(admin,storageClass.name,2,tapepool2Name,"ArchiveRoute2");
-  catalogue.createArchiveRoute(admin,storageClass.name,3,tapepool3Name,"ArchiveRoute3");
+  catalogue.ArchiveRoute()->createArchiveRoute(admin,storageClass.name,2,tapepool2Name,"ArchiveRoute2");
+  catalogue.ArchiveRoute()->createArchiveRoute(admin,storageClass.name,3,tapepool3Name,"ArchiveRoute3");
 
   //Create two other destinationTape and one for the move workflow
   std::string vidDestination1 = "VIDDESTINATION1";
@@ -4662,7 +4666,7 @@ TEST_P(SchedulerTest, expandRepackRequestMoveAndAddCopies){
     auto tape = getDefaultTape();
     tape.vid = vidDestination1;
     tape.tapePoolName = tapepool2Name;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   std::string vidDestination2 = "VIDDESTINATION2";
@@ -4671,14 +4675,14 @@ TEST_P(SchedulerTest, expandRepackRequestMoveAndAddCopies){
     tape.vid = vidDestination2;
     tape.tapePoolName = tapepool3Name;
     tape.full = false;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   std::string vidMove = "VIDMOVE";
   {
     auto tape = getDefaultTape();
     tape.vid = vidMove;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string tapeDrive = "tape_drive";
@@ -4715,7 +4719,7 @@ TEST_P(SchedulerTest, expandRepackRequestMoveAndAddCopies){
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the expanding requeue the Repack after the creation of
@@ -4904,7 +4908,7 @@ TEST_P(SchedulerTest, cancelRepackRequest) {
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   std::ostringstream ossVid;
   ossVid << s_vid << "_" << 1;
@@ -4915,14 +4919,14 @@ TEST_P(SchedulerTest, cancelRepackRequest) {
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
   //Create a repack destination tape
   std::string vidDestination = "VIDDESTINATION";
   {
     auto tape = getDefaultTape();
     tape.vid = vidDestination;
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -4965,7 +4969,7 @@ TEST_P(SchedulerTest, cancelRepackRequest) {
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the expandRepackRequest method
@@ -5143,15 +5147,15 @@ TEST_P(SchedulerTest, getNextMountEmptyArchiveForRepackIfNbFilesQueuedIsLessThan
   //Create environment for the test
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
-  catalogue.modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,10000);
+  catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,10000);
 
   Sorter sorter(agentReference,backend,catalogue);
   for(uint64_t i = 0; i < s_minFilesToWarrantAMount; ++i) {
@@ -5247,17 +5251,17 @@ TEST_P(SchedulerTest, getNextMountTapeStatesThatShouldNotReturnAMount) {
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
 
   auto tape = getDefaultTape();
   {
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   const std::string driveName = "tape_drive";
 
-  catalogue.tapeLabelled(s_vid, driveName);
+  catalogue.Tape()->tapeLabelled(s_vid, driveName);
 
   {
     // This first initialization is normally done by the dataSession function.
@@ -5294,24 +5298,24 @@ TEST_P(SchedulerTest, getNextMountTapeStatesThatShouldNotReturnAMount) {
 
   scheduler.waitSchedulerDbSubthreadsComplete();
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::BROKEN,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::BROKEN,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::BROKEN,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::BROKEN,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::EXPORTED,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::EXPORTED,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::EXPORTED,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::EXPORTED,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::DISABLED,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::DISABLED,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::DISABLED,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::DISABLED,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
   {
@@ -5360,24 +5364,24 @@ TEST_P(SchedulerTest, getNextMountTapeStatesThatShouldNotReturnAMount) {
     scheduler.queueRetrieve(s_diskInstance, request, lc);
     scheduler.waitSchedulerDbSubthreadsComplete();
   }
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::BROKEN,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::BROKEN,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::BROKEN,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::BROKEN,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::EXPORTED,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::EXPORTED,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::EXPORTED,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::EXPORTED,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::REPACKING_DISABLED,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::DISABLED,std::nullopt,std::string("Test"));
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::DISABLED,std::nullopt,std::string("Test"));
   ASSERT_EQ(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::DISABLED,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost,tape.vid,common::dataStructures::Tape::ACTIVE,common::dataStructures::Tape::DISABLED,std::nullopt);
   ASSERT_NE(nullptr,scheduler.getNextMount(s_libraryName, driveName, lc));
 }
 
@@ -5392,9 +5396,9 @@ TEST_P(SchedulerTest, repackRetrieveRequestsFailToFetchDiskSystem){
   //cta::objectstore::Backend& backend = schedulerDB.getBackend();
 
   setupDefaultCatalogue();
-  catalogue.createDiskInstance({"user", "host"}, "diskInstance", "no comment");
-  catalogue.createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "eos:ctaeos:default", 10, "no comment");
-  catalogue.createDiskSystem({"user", "host"}, "repackBuffer", "diskInstance", "diskInstanceSpace", tempDirectory.path(), 10L*1000*1000*1000, 15*60, "no comment");
+  catalogue.DiskInstance()->createDiskInstance({"user", "host"}, "diskInstance", "no comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "eos:ctaeos:default", 10, "no comment");
+  catalogue.DiskSystem()->createDiskSystem({"user", "host"}, "repackBuffer", "diskInstance", "diskInstanceSpace", tempDirectory.path(), 10L*1000*1000*1000, 15*60, "no comment");
 
 #ifdef STDOUT_LOGGING
   log::StdoutLogger dl("dummy", "unitTest");
@@ -5413,14 +5417,14 @@ TEST_P(SchedulerTest, repackRetrieveRequestsFailToFetchDiskSystem){
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   {
     auto tape = getDefaultTape();
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -5462,7 +5466,7 @@ TEST_P(SchedulerTest, repackRetrieveRequestsFailToFetchDiskSystem){
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
 
@@ -5497,11 +5501,11 @@ TEST_P(SchedulerTest, repackRetrieveRequestsFailToFetchDiskSystem){
       reservationRequest.addRequest(job->diskSystemName().value(), job->archiveFile.fileSize);
     }
     ASSERT_EQ(10,jobBatch.size());
-    auto diskSpaceReservedBefore = catalogue.getTapeDrive(driveName).value().reservedBytes;
+    auto diskSpaceReservedBefore = catalogue.DriveState()->getTapeDrive(driveName).value().reservedBytes;
     //Trying to reserve disk space should result in 10 jobs should fail
     ASSERT_FALSE(retrieveMount->reserveDiskSpace(reservationRequest, lc));
     //No extra disk space was reserved
-    auto diskSpaceReservedAfter = catalogue.getTapeDrive(driveName).value().reservedBytes;
+    auto diskSpaceReservedAfter = catalogue.DriveState()->getTapeDrive(driveName).value().reservedBytes;
     ASSERT_EQ(diskSpaceReservedAfter, diskSpaceReservedBefore);
   }
   /*
@@ -5541,9 +5545,9 @@ TEST_P(SchedulerTest, expandRepackRequestShouldThrowIfUseBufferNotRecallButNoDir
   auto &scheduler = getScheduler();
 
   setupDefaultCatalogue();
-  catalogue.createDiskInstance({"user", "host"}, "diskInstance", "no comment");
-  catalogue.createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "eos:ctaeos:default", 10, "no comment");
-  catalogue.createDiskSystem({"user", "host"}, "repackBuffer", "diskInstance", "diskInstanceSpace", tempDirectory.path(), 10L*1000*1000*1000, 15*60, "no comment");
+  catalogue.DiskInstance()->createDiskInstance({"user", "host"}, "diskInstance", "no comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "eos:ctaeos:default", 10, "no comment");
+  catalogue.DiskSystem()->createDiskSystem({"user", "host"}, "repackBuffer", "diskInstance", "diskInstanceSpace", tempDirectory.path(), 10L*1000*1000*1000, 15*60, "no comment");
 
 #ifdef STDOUT_LOGGING
   log::StdoutLogger dl("dummy", "unitTest");
@@ -5562,14 +5566,14 @@ TEST_P(SchedulerTest, expandRepackRequestShouldThrowIfUseBufferNotRecallButNoDir
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   {
     auto tape = getDefaultTape();
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -5611,7 +5615,7 @@ TEST_P(SchedulerTest, expandRepackRequestShouldThrowIfUseBufferNotRecallButNoDir
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
 
@@ -5640,9 +5644,9 @@ TEST_P(SchedulerTest, expandRepackRequestShouldNotThrowIfTapeDisabledButNoRecall
   auto &scheduler = getScheduler();
 
   setupDefaultCatalogue();
-  catalogue.createDiskInstance({"user", "host"}, "diskInstance", "no comment");
-  catalogue.createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "eos:ctaeos:default", 10, "no comment");
-  catalogue.createDiskSystem({"user", "host"}, "repackBuffer", "diskInstance", "diskInstanceSpace",tempDirectory.path(), 10L*1000*1000*1000, 15*60, "no comment");
+  catalogue.DiskInstance()->createDiskInstance({"user", "host"}, "diskInstance", "no comment");
+  catalogue.DiskInstanceSpace()->createDiskInstanceSpace({"user", "host"}, "diskInstanceSpace", "diskInstance", "eos:ctaeos:default", 10, "no comment");
+  catalogue.DiskSystem()->createDiskSystem({"user", "host"}, "repackBuffer", "diskInstance", "diskInstanceSpace",tempDirectory.path(), 10L*1000*1000*1000, 15*60, "no comment");
 
 #ifdef STDOUT_LOGGING
   log::StdoutLogger dl("dummy", "unitTest");
@@ -5661,14 +5665,14 @@ TEST_P(SchedulerTest, expandRepackRequestShouldNotThrowIfTapeDisabledButNoRecall
 
   //Create a logical library in the catalogue
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(admin, s_libraryName, libraryIsDisabled, "Create logical library");
 
   {
     auto tape = getDefaultTape();
     tape.full = true;
     tape.state = common::dataStructures::Tape::REPACKING;
     tape.stateReason = "Test";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   //Create a storage class in the catalogue
@@ -5710,7 +5714,7 @@ TEST_P(SchedulerTest, expandRepackRequestShouldNotThrowIfTapeDisabledButNoRecall
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
 
@@ -5763,21 +5767,21 @@ TEST_P(SchedulerTest, archiveMaxDrivesVoInFlightChangeScheduleMount){
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
   }
 
   auto tape = getDefaultTape();
-  catalogue.createTape(s_adminOnAdminHost, tape);
+  catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
 
   const std::string driveName = "tape_drive";
 
-  catalogue.tapeLabelled(s_vid, driveName);
+  catalogue.Tape()->tapeLabelled(s_vid, driveName);
 
 
   log::DummyLogger dl("", "");
@@ -5787,7 +5791,7 @@ TEST_P(SchedulerTest, archiveMaxDrivesVoInFlightChangeScheduleMount){
   scheduler.queueArchiveWithGivenId(archiveFileId, s_diskInstance, request, lc);
   scheduler.waitSchedulerDbSubthreadsComplete();
 
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,0);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,0);
 
   {
     // Emulate a tape server
@@ -5799,7 +5803,7 @@ TEST_P(SchedulerTest, archiveMaxDrivesVoInFlightChangeScheduleMount){
     bool nextMount = scheduler.getNextMountDryRun(s_libraryName, driveName, lc);
     //nextMount should be false as the VO write max drives is 0
     ASSERT_FALSE(nextMount);
-    catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
+    catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
     //Reset the VO write max drives to a positive number should give a new mount
     nextMount = scheduler.getNextMountDryRun(s_libraryName,driveName,lc);
     ASSERT_TRUE(nextMount);
@@ -5833,10 +5837,10 @@ TEST_P(SchedulerTest, retrieveMaxDrivesVoInFlightChangeScheduleMount)
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   auto tape = getDefaultTape();
-  catalogue.createTape(s_adminOnAdminHost, tape);
+  catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
 
   //Create a storage class in the catalogue
   common::dataStructures::StorageClass storageClass;
@@ -5878,7 +5882,7 @@ TEST_P(SchedulerTest, retrieveMaxDrivesVoInFlightChangeScheduleMount)
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Test the queueing of the Retrieve Request and try to mount after having changed the readMaxDrives of the VO
@@ -5896,11 +5900,11 @@ TEST_P(SchedulerTest, retrieveMaxDrivesVoInFlightChangeScheduleMount)
 
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,"drive",lc));
 
-  catalogue.modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,0);
+  catalogue.VO()->modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,0);
 
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,"drive",lc));
 
-  catalogue.modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,1);
+  catalogue.VO()->modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,1);
 
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,"drive",lc));
 }
@@ -5936,43 +5940,43 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
   std::string drive1 = "drive1";
   {
     const auto tapeDrive = getDefaultTapeDrive(drive1);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
   }
   std::string drive2 = "drive2";
   {
     const auto tapeDrive = getDefaultTapeDrive(drive2);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
   }
   std::string drive3 = "drive3";
   {
     const auto tapeDrive = getDefaultTapeDrive(drive3);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
   }
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   //This tape will contain files for triggering a Retrieve
   auto tape1 = getDefaultTape();
-  catalogue.createTape(s_adminOnAdminHost, tape1);
+  catalogue.Tape()->createTape(s_adminOnAdminHost, tape1);
 
   //Two tapes for ArchiveForUser and ArchiveForRepack mounts
   std::string vid2 = "VID_2";
   auto tape2 = tape1;
   tape2.vid = vid2;
-  catalogue.createTape(s_adminOnAdminHost, tape2);
+  catalogue.Tape()->createTape(s_adminOnAdminHost, tape2);
 
   //Create a new tapepool on the same VO
   std::string newTapepool = "new_tapepool";
-  catalogue.createTapePool(s_adminOnAdminHost,newTapepool,s_vo,1,false,std::nullopt,"Test");
+  catalogue.TapePool()->createTapePool(s_adminOnAdminHost,newTapepool,s_vo,1,false,std::nullopt,"Test");
 
   //Create the third tape in the new tapepool
   std::string vid3 = "VID_3";
   auto tape3  = tape1;
   tape3.vid = vid3;
   tape3.tapePoolName = newTapepool;
-  catalogue.createTape(s_adminOnAdminHost,tape3);
+  catalogue.Tape()->createTape(s_adminOnAdminHost,tape3);
 
   //Create a storage class in the catalogue
   common::dataStructures::StorageClass storageClass;
@@ -5980,10 +5984,10 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
   storageClass.nbCopies = 2;
   storageClass.comment = "Create storage class";
 
-  catalogue.modifyStorageClassNbCopies(s_adminOnAdminHost,storageClass.name,storageClass.nbCopies);
+  catalogue.StorageClass()->modifyStorageClassNbCopies(s_adminOnAdminHost,storageClass.name,storageClass.nbCopies);
 
    //Create the new archive routes for the second copy
-  catalogue.createArchiveRoute(s_adminOnAdminHost,storageClass.name,2,newTapepool,"ArchiveRoute2");
+  catalogue.ArchiveRoute()->createArchiveRoute(s_adminOnAdminHost,storageClass.name,2,newTapepool,"ArchiveRoute2");
 
   const std::string tapeDrive = "tape_drive";
   const uint64_t nbArchiveFilesPerTape = 10;
@@ -6019,7 +6023,7 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
       tapeFilesWrittenCopy1.emplace(fileWrittenUP.release());
     }
     //update the DB tape
-    catalogue.filesWrittenToTape(tapeFilesWrittenCopy1);
+    catalogue.TapeFile()->filesWrittenToTape(tapeFilesWrittenCopy1);
     tapeFilesWrittenCopy1.clear();
   }
   //Queue the Retrieve request
@@ -6068,8 +6072,8 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
 
   sorter.flushAll(lc);
 
-  catalogue.modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
-  catalogue.modifyMountPolicyRetrieveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
+  catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
+  catalogue.MountPolicy()->modifyMountPolicyRetrieveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
 
   //Wait 2 second to be sure the minRequestAge will not prevent a mount
   ::sleep(1);
@@ -6077,16 +6081,16 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,drive1,lc));
 
   //No read nor write allowed
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,0);
-  catalogue.modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,0);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,0);
+  catalogue.VO()->modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,0);
 
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive1,lc));
 
   //Allow one drive for write and trigger the mount
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
 
   //Set the tape 1 to disabled state to prevent the mount in it (should be the Retrieve)
-  catalogue.setTapeDisabled(s_adminOnAdminHost,tape1.vid,"test");
+  catalogue.Tape()->setTapeDisabled(s_adminOnAdminHost,tape1.vid,"test");
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,drive1,lc));
   {
     std::unique_ptr<cta::TapeMount> tapeMount = scheduler.getNextMount(s_libraryName,drive1,lc);
@@ -6109,7 +6113,7 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive2,lc));
 
   //Now allocate one more drive for Archival
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,2);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,2);
 
   //A new Archive mount should be triggered
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,drive2,lc));
@@ -6133,11 +6137,12 @@ TEST_P(SchedulerTest, retrieveArchiveAllTypesMaxDrivesVoInFlightChangeScheduleMo
   //As 2 drives are writing and only 2 drives are allowed on this VO, the third drive should not trigger a new mount
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive3,lc));
   //Now allocate one drive for Retrieve
-  catalogue.modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,1);
-  //The retrieve mount should not be triggered as the tape 1 state is broken
+  catalogue.VO()->modifyVirtualOrganizationReadMaxDrives(s_adminOnAdminHost,s_vo,1);
+  //The retrieve mount should not be triggered as the tape 1 is disabled
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive3,lc));
   //Setting the state of the tape back to active
-  catalogue.modifyTapeState(s_adminOnAdminHost,tape1.vid,common::dataStructures::Tape::ACTIVE,std::nullopt,std::nullopt);
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost, tape1.vid, common::dataStructures::Tape::ACTIVE, std::nullopt,
+    std::nullopt);
   //The mount should be triggered on tape 1
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,drive3,lc));
   //The mount should be a Retrieve mount
@@ -6187,15 +6192,15 @@ TEST_P(SchedulerTest, getQueuesAndMountSummariesTest)
 
   //Create a logical library in the catalogue
   const bool logicalLibraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName, logicalLibraryIsDisabled, "Create logical library");
 
   //Create two tapes
   auto tape = getDefaultTape();
-  catalogue.createTape(s_adminOnAdminHost, tape);
+  catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
 
   std::string vid2 = s_vid + "2";
   tape.vid = vid2;
-  catalogue.createTape(s_adminOnAdminHost,tape);
+  catalogue.Tape()->createTape(s_adminOnAdminHost,tape);
 
   //Create a RetrieveQueue with the vid s_vid
   std::string retrieveQueueAddress;
@@ -6343,33 +6348,33 @@ TEST_P(SchedulerTest, getNextMountWithArchiveForUserAndArchiveForRepackShouldRet
   //Create environment for the test
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
 
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
 
   std::string drive0 = "drive0";
   std::string drive1 = "drive1";
   {
     const auto tapeDrive = getDefaultTapeDrive(drive1);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
   }
   std::string drive2 = "drive2";
   {
     const auto tapeDrive = getDefaultTapeDrive(drive2);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
   }
 
   //Create two tapes (ArchiveForRepack and ArchiveForUser)
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   {
     auto tape = getDefaultTape();
     tape.vid = s_vid+"_1";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   uint64_t fileSize = 667;
@@ -6405,13 +6410,13 @@ TEST_P(SchedulerTest, getNextMountWithArchiveForUserAndArchiveForRepackShouldRet
     ar->insert();
   }
 
-  catalogue.modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,100);
+  catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,100);
 
   sorter.flushAll(lc);
 
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive0,lc));
 
-  catalogue.modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
+  catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
 
   //The archiveMinRequestAge should have 1 second to trigger a mount
   ::sleep(1);
@@ -6467,23 +6472,23 @@ TEST_P(SchedulerTest, getNextMountWithArchiveForUserAndArchiveForRepackShouldRet
 
   sorter.flushAll(lc);
 
-  catalogue.modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,100);
+  catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,100);
   //mount should not be triggered
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive0,lc));
 
-  catalogue.modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
+  catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(s_adminOnAdminHost,s_mountPolicyName,0);
 
   //Sleeping one seconds to trigger a mount
   ::sleep(1);
 
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,1);
 
   //Test the per VO writeMaxDrives: no mount should be triggered as only one drive for write
   //has been configured for this VO
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive1,lc));
 
   //Adding a second drive for write for this VO
-  catalogue.modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,2);
+  catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(s_adminOnAdminHost,s_vo,2);
 
   //The next mount should be an ArchiveForUser mount as there is already a mount ongoing with an ArchiveForRepack
   ASSERT_TRUE(scheduler.getNextMountDryRun(s_libraryName,drive1,lc));
@@ -6509,7 +6514,7 @@ TEST_P(SchedulerTest, getNextMountWithArchiveForUserAndArchiveForRepackShouldRet
   {
     auto tape = getDefaultTape();
     tape.vid = s_vid+"_2";
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
   ASSERT_FALSE(scheduler.getNextMountDryRun(s_libraryName,drive2,lc));
 }
@@ -6540,17 +6545,17 @@ TEST_P(SchedulerTestTriggerTapeStateChangeBehaviour, triggerTapeStateChangeValid
   // Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
 
   auto tape = getDefaultTape();
   {
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Setup initial conditions
   schedulerDB.setRetrieveQueueCleanupFlag(tape.vid, false, lc);
-  catalogue.modifyTapeState(s_adminOnAdminHost, tape.vid,triggerTapeStateChangeBehaviour.fromState,std::nullopt,"Test");
+  catalogue.Tape()->modifyTapeState(s_adminOnAdminHost, tape.vid,triggerTapeStateChangeBehaviour.fromState,std::nullopt,"Test");
 
   // Trigger change
   if (triggerTapeStateChangeBehaviour.changeRaisedException) {
@@ -6560,7 +6565,7 @@ TEST_P(SchedulerTestTriggerTapeStateChangeBehaviour, triggerTapeStateChangeValid
   }
 
   // Observe results
-  ASSERT_EQ(catalogue.getTapesByVid(tape.vid).at(tape.vid).state, triggerTapeStateChangeBehaviour.observedState);
+  ASSERT_EQ(catalogue.Tape()->getTapesByVid(tape.vid).at(tape.vid).state, triggerTapeStateChangeBehaviour.observedState);
   ASSERT_EQ(schedulerDB.getRetrieveQueuesCleanupInfo(lc).front().doCleanup, triggerTapeStateChangeBehaviour.cleanupFlagActivated);
 }
 
diff --git a/scheduler/testingMocks/MockArchiveJob.hpp b/scheduler/testingMocks/MockArchiveJob.hpp
index 78179047c0..0479fc95e0 100644
--- a/scheduler/testingMocks/MockArchiveJob.hpp
+++ b/scheduler/testingMocks/MockArchiveJob.hpp
@@ -17,10 +17,12 @@
 
 #pragma once
 
-#include "scheduler/RetrieveMount.hpp"
-#include "scheduler/RetrieveJob.hpp"
 #include <memory>
 
+#include "catalogue/TapeFileWritten.hpp"
+#include "scheduler/RetrieveJob.hpp"
+#include "scheduler/RetrieveMount.hpp"
+
 namespace cta {
   class MockArchiveJob: public cta::ArchiveJob {
   public:
diff --git a/scheduler/testingMocks/MockArchiveMount.hpp b/scheduler/testingMocks/MockArchiveMount.hpp
index dc475c08b7..9e68032a07 100644
--- a/scheduler/testingMocks/MockArchiveMount.hpp
+++ b/scheduler/testingMocks/MockArchiveMount.hpp
@@ -67,7 +67,7 @@ public:
         skippedFiles.pop();
         tapeItemsWritten.emplace(tiwup.release());
       }
-      m_catalogue.filesWrittenToTape(tapeItemsWritten);
+      m_catalogue.TapeFile()->filesWrittenToTape(tapeItemsWritten);
       catalogue_updated = true;
       for (auto &job : validatedSuccessfulArchiveJobs) {
         auto *maj = dynamic_cast<MockArchiveJob *>(job.get());
diff --git a/tapeserver/castor/tape/tapeserver/RAO/FilePositionEstimatorFactory.cpp b/tapeserver/castor/tape/tapeserver/RAO/FilePositionEstimatorFactory.cpp
index 39c203515d..bdd91b7148 100644
--- a/tapeserver/castor/tape/tapeserver/RAO/FilePositionEstimatorFactory.cpp
+++ b/tapeserver/castor/tape/tapeserver/RAO/FilePositionEstimatorFactory.cpp
@@ -26,7 +26,7 @@ namespace castor { namespace tape { namespace tapeserver { namespace rao {
   std::unique_ptr<FilePositionEstimator> FilePositionEstimatorFactory::createInterpolationFilePositionEstimator(const std::string & vid, cta::catalogue::Catalogue *catalogue, drive::DriveInterface *drive, cta::log::TimingList &tl){
     std::unique_ptr<FilePositionEstimator> ret;
     cta::utils::Timer t;
-    cta::catalogue::MediaType tapeMediaType = catalogue->getMediaTypeByVid(vid);
+    cta::catalogue::MediaType tapeMediaType = catalogue->MediaType()->getMediaTypeByVid(vid);
     tl.insertAndReset("catalogueGetMediaTypeByVidTime",t);
     std::vector<drive::endOfWrapPosition> endOfWrapPositions = drive->getEndOfWrapPositions();
     tl.insertAndReset("getEndOfWrapPositionsTime",t);
diff --git a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp
index 1f2d09585f..34f2b9146d 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/CleanerSession.cpp
@@ -159,11 +159,11 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction
 
       try {
         std::string disabledReason = cta::utils::getCurrentLocalTime("%F %T") + ":" + currentExceptionMsg;
-        auto curr_state = m_catalogue.getTapesByVid(m_vid).at(m_vid).state;
+        auto curr_state = m_catalogue.Tape()->getTapesByVid(m_vid).at(m_vid).state;
         if (curr_state == cta::common::dataStructures::Tape::REPACKING) {
-          m_catalogue.setTapeRepackingDisabled(admin, m_vid, disabledReason);
+          m_catalogue.Tape()->setTapeRepackingDisabled(admin, m_vid, disabledReason);
         } else {
-          m_catalogue.setTapeDisabled(admin, m_vid, disabledReason);
+          m_catalogue.Tape()->setTapeDisabled(admin, m_vid, disabledReason);
         }
       } catch(cta::exception::Exception &ex) {
         cta::log::Param param("exceptionMsg", ex.getMessageValue());
@@ -340,7 +340,7 @@ std::string castor::tape::tapeserver::daemon::CleanerSession::checkVolumeLabel(
   params.push_back(cta::log::Param("tapeDrive", m_driveConfig.unitName));
 
   using LabelFormat = cta::common::dataStructures::Label::Format;
-  const LabelFormat labelFormat = m_catalogue.getTapeLabelFormat(m_vid);
+  const LabelFormat labelFormat = m_catalogue.Tape()->getTapeLabelFormat(m_vid);
 
   const std::string volumeLabelVSN = tapeFile::HeaderChecker::checkVolumeLabel(drive, labelFormat);
 
diff --git a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp
index c7ba5d3e4d..017cd5a4da 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp
@@ -45,7 +45,7 @@
 #include "catalogue/CreateTapeAttributes.hpp"
 #include "catalogue/InMemoryCatalogue.hpp"
 #include "catalogue/MediaType.hpp"
-#include "catalogue/OracleCatalogue.hpp"
+#include "catalogue/rdbms/oracle/OracleCatalogue.hpp"
 #include "catalogue/OracleCatalogueSchema.hpp"
 #include "catalogue/TapeItemWrittenPointer.hpp"
 #include "common/dataStructures/DiskInstance.hpp"
@@ -333,13 +333,13 @@ public:
     const uint64_t minRetrieveRequestAge = mountPolicy.minRetrieveRequestAge;
     const std::string mountPolicyComment = "create mount group";
 
-    ASSERT_TRUE(catalogue.getMountPolicies().empty());
+    ASSERT_TRUE(catalogue.MountPolicy()->getMountPolicies().empty());
 
-    catalogue.createMountPolicy(
+    catalogue.MountPolicy()->createMountPolicy(
       s_adminOnAdminHost,
       mountPolicy);
 
-    const std::list<common::dataStructures::MountPolicy> groups = catalogue.getMountPolicies();
+    const std::list<common::dataStructures::MountPolicy> groups = catalogue.MountPolicy()->getMountPolicies();
     ASSERT_EQ(1, groups.size());
     const common::dataStructures::MountPolicy group = groups.front();
     ASSERT_EQ(mountPolicyName, group.name);
@@ -350,12 +350,12 @@ public:
     ASSERT_EQ(mountPolicyComment, group.comment);
 
     const auto di = getDefaultDiskInstance();
-    catalogue.createDiskInstance(s_adminOnAdminHost, di.name, di.comment); 
+    catalogue.DiskInstance()->createDiskInstance(s_adminOnAdminHost, di.name, di.comment); 
 
     const std::string ruleComment = "create requester mount-rule";
-    catalogue.createRequesterMountRule(s_adminOnAdminHost, mountPolicyName, di.name, s_userName, ruleComment);
+    catalogue.RequesterMountRule()->createRequesterMountRule(s_adminOnAdminHost, mountPolicyName, di.name, s_userName, ruleComment);
 
-    const std::list<common::dataStructures::RequesterMountRule> rules = catalogue.getRequesterMountRules();
+    const std::list<common::dataStructures::RequesterMountRule> rules = catalogue.RequesterMountRule()->getRequesterMountRules();
     ASSERT_EQ(1, rules.size());
 
     const common::dataStructures::RequesterMountRule rule = rules.front();
@@ -367,28 +367,27 @@ public:
     ASSERT_EQ(s_adminOnAdminHost.host, rule.creationLog.host);
     ASSERT_EQ(rule.creationLog, rule.lastModificationLog);
 
- 
     cta::common::dataStructures::VirtualOrganization vo = getDefaultVirtualOrganization();
-    catalogue.createVirtualOrganization(s_adminOnAdminHost, vo);
+    catalogue.VO()->createVirtualOrganization(s_adminOnAdminHost, vo);
 
     common::dataStructures::StorageClass storageClass;
     storageClass.name = s_storageClassName;
     storageClass.nbCopies = 1;
     storageClass.vo.name = vo.name;
     storageClass.comment = "create storage class";
-    m_catalogue->createStorageClass(s_adminOnAdminHost, storageClass);
+    m_catalogue->StorageClass()->createStorageClass(s_adminOnAdminHost, storageClass);
 
     const uint16_t nbPartialTapes = 1;
     const std::string tapePoolComment = "Tape-pool comment";
     const bool tapePoolEncryption = false;
     const std::optional<std::string> tapePoolSupply("value for the supply pool mechanism");
 
-    ASSERT_NO_THROW(catalogue.createTapePool(s_adminOnAdminHost, s_tapePoolName, vo.name, nbPartialTapes, tapePoolEncryption,
-                                             tapePoolSupply, tapePoolComment));
+    ASSERT_NO_THROW(catalogue.TapePool()->createTapePool(s_adminOnAdminHost, s_tapePoolName, vo.name, nbPartialTapes,
+      tapePoolEncryption, tapePoolSupply, tapePoolComment));
     const uint32_t copyNb = 1;
     const std::string archiveRouteComment = "Archive-route comment";
-    catalogue.createArchiveRoute(s_adminOnAdminHost, s_storageClassName, copyNb, s_tapePoolName,
-                                 archiveRouteComment);
+    catalogue.ArchiveRoute()->createArchiveRoute(s_adminOnAdminHost, s_storageClassName, copyNb, s_tapePoolName,
+      archiveRouteComment);
 
     cta::catalogue::MediaType mediaType;
     mediaType.name = s_mediaType;
@@ -398,11 +397,11 @@ public:
     mediaType.maxLPos = 171097;
     mediaType.nbWraps = 112;
     mediaType.comment = "comment";
-    catalogue.createMediaType(s_adminOnAdminHost, mediaType);
+    catalogue.MediaType()->createMediaType(s_adminOnAdminHost, mediaType);
 
     const std::string driveName = "T10D6116";
     const auto tapeDrive = getDefaultTapeDrive(driveName);
-    catalogue.createTapeDrive(tapeDrive);
+    catalogue.DriveState()->createTapeDrive(tapeDrive);
   }
 
   /**
@@ -502,10 +501,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayRecall) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -513,7 +512,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayRecall) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // 6) Prepare files for reading by writing them to the mock system
@@ -570,7 +569,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayRecall) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -682,10 +681,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumRecall) {
   // 5) Create the environment for the migration to happen (library + tape)
     const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -693,7 +692,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumRecall) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // 6) Prepare files for reading by writing them to the mock system
@@ -756,7 +755,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumRecall) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance=s_diskInstance;
@@ -882,10 +881,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongRecall) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -893,7 +892,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongRecall) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // 6) Prepare files for reading by writing them to the mock system
@@ -950,7 +949,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongRecall) {
         tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
         tapeFileWritten.storageClassName = s_storageClassName;
         tapeFileWritten.tapeDrive = "drive0";
-        catalogue.filesWrittenToTape(tapeFileWrittenSet);
+        catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
       }
 
       {
@@ -973,7 +972,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongRecall) {
         tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
         tapeFileWritten.storageClassName = s_storageClassName;
         tapeFileWritten.tapeDrive = "drive0";
-        catalogue.filesWrittenToTape(tapeFileWrittenSet);
+        catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
       }
 
       // Schedule the retrieval of the file
@@ -1076,10 +1075,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1087,7 +1086,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   int MAX_RECALLS = 50;
@@ -1150,7 +1149,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -1266,10 +1265,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallLinearAlgorithm) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1277,7 +1276,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallLinearAlgorithm) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   int MAX_RECALLS = 50;
@@ -1341,7 +1340,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallLinearAlgorithm) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -1453,10 +1452,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallRAOAlgoDoesNotExistS
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1464,7 +1463,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallRAOAlgoDoesNotExistS
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   int MAX_RECALLS = 50;
@@ -1528,7 +1527,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallRAOAlgoDoesNotExistS
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -1644,10 +1643,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallSLTFRAOAlgorithm) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1655,7 +1654,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallSLTFRAOAlgorithm) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   int MAX_RECALLS = 30;
@@ -1719,7 +1718,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallSLTFRAOAlgorithm) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -1836,10 +1835,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionNoSuchDrive) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1847,7 +1846,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionNoSuchDrive) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // 6) Prepare files for reading by writing them to the mock system
@@ -1904,7 +1903,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionNoSuchDrive) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -1986,10 +1985,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionFailtoMount) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -1997,7 +1996,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionFailtoMount) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // 6) Prepare files for reading by writing them to the mock system
@@ -2054,7 +2053,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionFailtoMount) {
       tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
       tapeFileWritten.storageClassName = s_storageClassName;
       tapeFileWritten.tapeDrive = "drive0";
-      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+      catalogue.TapeFile()->filesWrittenToTape(tapeFileWrittenSet);
 
       // Schedule the retrieval of the file
       std::string diskInstance = s_diskInstance;
@@ -2146,10 +2145,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayMigration) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -2157,15 +2156,15 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayMigration) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -2179,7 +2178,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayMigration) {
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -2243,7 +2242,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionGooddayMigration) {
   auto afiiter = archiveFileIds.begin();
   for (const auto& sf: sourceFiles) {
     auto afi = *(afiiter++);
-    auto afs = catalogue.getArchiveFileById(afi);
+    auto afs = catalogue.ArchiveFile()->getArchiveFileById(afi);
     ASSERT_EQ(1, afs.tapeFiles.size());
     cta::checksum::ChecksumBlob checksumBlob;
     checksumBlob.insert(cta::checksum::ADLER32, sf->adler32());
@@ -2297,10 +2296,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFileSizeMigration) {
   // 5) Create the environment for the migration to happen (library + tape)
     const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -2308,15 +2307,15 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFileSizeMigration) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
   
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -2330,7 +2329,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFileSizeMigration) {
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -2420,9 +2419,9 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFileSizeMigration) {
   for(auto & sf: sourceFiles) {
     auto afi = *(afiiter++);
     if (afi == 1) {
-      ASSERT_THROW(catalogue.getArchiveFileById(afi), cta::exception::Exception);
+      ASSERT_THROW(catalogue.ArchiveFile()->getArchiveFileById(afi), cta::exception::Exception);
     } else {
-      auto afs = catalogue.getArchiveFileById(afi);
+      auto afs = catalogue.ArchiveFile()->getArchiveFileById(afi);
       ASSERT_EQ(1, afs.tapeFiles.size());
       cta::checksum::ChecksumBlob checksumBlob;
       checksumBlob.insert(cta::checksum::ADLER32, sf->adler32());
@@ -2477,10 +2476,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumMigration) {
   // 5) Create the environment for the migration to happen (library + tape)
     const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -2488,15 +2487,15 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumMigration) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -2510,7 +2509,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumMigration) {
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -2599,7 +2598,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongChecksumMigration) {
   // None of the files made it to the catalogue
   for(__attribute__ ((unused)) auto & sf: sourceFiles) {
     auto afi = *(afiiter++);
-    ASSERT_THROW(catalogue.getArchiveFileById(afi), cta::exception::Exception);
+    ASSERT_THROW(catalogue.ArchiveFile()->getArchiveFileById(afi), cta::exception::Exception);
   }
 
   // Check logs for drive statistics
@@ -2648,10 +2647,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFilesizeInMiddleOfBatchM
   // 5) Create the environment for the migration to happen (library + tape)
     const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
     libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -2659,15 +2658,15 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFilesizeInMiddleOfBatchM
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -2681,7 +2680,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFilesizeInMiddleOfBatchM
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -2792,14 +2791,14 @@ TEST_P(DataTransferSessionTest, DataTransferSessionWrongFilesizeInMiddleOfBatchM
     auto afi = *(afiiter++);
     if (fseq != 5) {
       // Files queued without the wrong file size made it to the catalogue
-      auto afs = catalogue.getArchiveFileById(afi);
+      auto afs = catalogue.ArchiveFile()->getArchiveFileById(afi);
       ASSERT_EQ(1, afs.tapeFiles.size());
       cta::checksum::ChecksumBlob checksumBlob;
       checksumBlob.insert(cta::checksum::ADLER32, sf->adler32());
       ASSERT_EQ(afs.checksumBlob, checksumBlob);
       ASSERT_EQ(1000, afs.fileSize);
     } else {
-      ASSERT_THROW(catalogue.getArchiveFileById(afi), cta::exception::Exception);
+      ASSERT_THROW(catalogue.ArchiveFile()->getArchiveFileById(afi), cta::exception::Exception);
     }
     fseq++;
   }
@@ -2851,10 +2850,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionMissingFilesMigration) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -2862,15 +2861,15 @@ TEST_P(DataTransferSessionTest, DataTransferSessionMissingFilesMigration) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -2884,7 +2883,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionMissingFilesMigration) {
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -2962,7 +2961,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionMissingFilesMigration) {
   ASSERT_EQ(5, count);
   cta::catalogue::TapeSearchCriteria tapeCriteria;
   tapeCriteria.vid = s_vid;
-  auto tapeInfo = catalogue.getTapes(tapeCriteria);
+  auto tapeInfo = catalogue.Tape()->getTapes(tapeCriteria);
   ASSERT_EQ(1, tapeInfo.size());
   // We should have max fseq at least 10. It could be higher is a retry manages to sneak in.
   ASSERT_LE(10, tapeInfo.begin()->lastFSeq);
@@ -3017,10 +3016,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullMigration) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -3029,14 +3028,14 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullMigration) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -3051,7 +3050,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullMigration) {
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -3113,7 +3112,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullMigration) {
   temp += "";
   ASSERT_EQ(s_vid, sess.getVid());
   cta::catalogue::TapeFileSearchCriteria criteria;
-  auto afsItor = catalogue.getArchiveFilesItor(criteria);
+  auto afsItor = catalogue.ArchiveFile()->getArchiveFilesItor(criteria);
   for (size_t i = 1; i <= sourceFiles.size(); ++i) {
     // Only the first files made it through.
     if (i <= 3) {
@@ -3135,7 +3134,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullMigration) {
     // The tape should now be marked as full
     cta::catalogue::TapeSearchCriteria crit;
     crit.vid = s_vid;
-    auto tapes = catalogue.getTapes(crit);
+    auto tapes = catalogue.Tape()->getTapes(crit);
     ASSERT_EQ(1, tapes.size());
     ASSERT_EQ(s_vid, tapes.front().vid);
     ASSERT_EQ(true, tapes.front().full);
@@ -3187,10 +3186,10 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullOnFlushMigration) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -3198,15 +3197,15 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullOnFlushMigration) {
 
   {
     auto tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
@@ -3222,7 +3221,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullOnFlushMigration) {
   {
     // Label the tape
     castor::tape::tapeFile::LabelSession::label(mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false);
-    catalogue.tapeLabelled(s_vid, "T10D6116");
+    catalogue.Tape()->tapeLabelled(s_vid, "T10D6116");
     mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
 
     // Create the files and schedule the archivals
@@ -3284,7 +3283,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullOnFlushMigration) {
   temp += "";
   ASSERT_EQ(s_vid, sess.getVid());
   cta::catalogue::TapeFileSearchCriteria criteria;
-  auto afsItor = catalogue.getArchiveFilesItor(criteria);
+  auto afsItor = catalogue.ArchiveFile()->getArchiveFilesItor(criteria);
   for (size_t i = 1; i <= sourceFiles.size(); ++i) {
     // Only the first files made it through.
     if (i <= 3) {
@@ -3306,7 +3305,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullOnFlushMigration) {
     // The tape should now be marked as full
     cta::catalogue::TapeSearchCriteria crit;
     crit.vid = s_vid;
-    auto tapes = catalogue.getTapes(crit);
+    auto tapes = catalogue.Tape()->getTapes(crit);
     ASSERT_EQ(1, tapes.size());
     ASSERT_EQ(s_vid, tapes.front().vid);
     ASSERT_EQ(true, tapes.front().full);
@@ -3353,10 +3352,10 @@ TEST_P(DataTransferSessionTest, CleanerSessionFailsShouldPutTheDriveDown) {
   // 5) Create the environment for the migration to happen (library + tape)
   const std::string libraryComment = "Library comment";
   const bool libraryIsDisabled = false;
-  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+  catalogue.LogicalLibrary()->createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
                                  libraryIsDisabled, libraryComment);
   {
-    auto libraries = catalogue.getLogicalLibraries();
+    auto libraries = catalogue.LogicalLibrary()->getLogicalLibraries();
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(s_libraryName, libraries.front().name);
     ASSERT_EQ(libraryComment, libraries.front().comment);
@@ -3364,15 +3363,15 @@ TEST_P(DataTransferSessionTest, CleanerSessionFailsShouldPutTheDriveDown) {
 
   {
     cta::catalogue::CreateTapeAttributes tape = getDefaultTape();
-    catalogue.createTape(s_adminOnAdminHost, tape);
+    catalogue.Tape()->createTape(s_adminOnAdminHost, tape);
   }
 
   // Create the mount criteria
   auto mountPolicy = getImmediateMountMountPolicy();
-  catalogue.createMountPolicy(requester, mountPolicy);
+  catalogue.MountPolicy()->createMountPolicy(requester, mountPolicy);
   std::string mountPolicyName = mountPolicy.name;
 
-  catalogue.createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
+  catalogue.RequesterMountRule()->createRequesterMountRule(requester, mountPolicyName, s_diskInstance, requester.username, "Rule comment");
 
   //delete is unnecessary
   //pointer with ownership will be passed to the application,
diff --git a/tapeserver/castor/tape/tapeserver/daemon/DiskWriteTaskTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/DiskWriteTaskTest.cpp
index 76a9376472..977e2244a2 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/DiskWriteTaskTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/DiskWriteTaskTest.cpp
@@ -23,7 +23,7 @@
 #include "castor/tape/tapeserver/daemon/RecallReportPacker.hpp"
 #include "castor/tape/tapeserver/daemon/ReportPackerInterface.hpp"
 #include "common/log/LogContext.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "common/log/StringLogger.hpp"
 #include "castor/tape/tapeserver/daemon/MigrationMemoryManager.hpp"
 #include "castor/tape/tapeserver/daemon/MemBlock.hpp"
diff --git a/tapeserver/castor/tape/tapeserver/daemon/DiskWriteThreadPoolTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/DiskWriteThreadPoolTest.cpp
index 7ef08167e3..bda55f7784 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/DiskWriteThreadPoolTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/DiskWriteThreadPoolTest.cpp
@@ -24,7 +24,7 @@
 #include "castor/tape/tapeserver/daemon/MemBlock.hpp"
 #include "castor/messages/TapeserverProxyDummy.hpp"
 #include "scheduler/TapeMountDummy.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include <gtest/gtest.h>
 
 namespace unitTests{
diff --git a/tapeserver/castor/tape/tapeserver/daemon/MigrationReportPackerTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/MigrationReportPackerTest.cpp
index df1559fb22..5e823e7e20 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/MigrationReportPackerTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/MigrationReportPackerTest.cpp
@@ -71,7 +71,7 @@ const uint32_t TEST_GROUP_2 = 9754;
       mediaType.capacityInBytes = 10;
       mediaType.cartridge = "cartridge";
       mediaType.comment = "comment";
-      m_catalogue->createMediaType(admin,mediaType);
+      m_catalogue->MediaType()->createMediaType(admin,mediaType);
     }
 
     const cta::common::dataStructures::DiskInstance getDefaultDiskInstance() const {
@@ -158,11 +158,11 @@ const uint32_t TEST_GROUP_2 = 9754;
     cta::common::dataStructures::VirtualOrganization vo = getDefaultVo();
     const auto di = getDefaultDiskInstance();
     cta::common::dataStructures::SecurityIdentity admin = cta::common::dataStructures::SecurityIdentity("admin","localhost");
-    m_catalogue->createDiskInstance(admin, di.name, di.comment);
-    m_catalogue->createVirtualOrganization(admin,vo);
-    
-    m_catalogue->createLogicalLibrary(admin, logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
-    m_catalogue->createTapePool(admin, tapePoolName, vo.name, 2, true, supply, "Create tape pool");
+    m_catalogue->DiskInstance()->createDiskInstance(admin, di.name, di.comment);
+    m_catalogue->VO()->createVirtualOrganization(admin,vo);
+
+    m_catalogue->LogicalLibrary()->createLogicalLibrary(admin, logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+    m_catalogue->TapePool()->createTapePool(admin, tapePoolName, vo.name, 2, true, supply, "Create tape pool");
     createMediaType(mediaType);
 
     {
@@ -176,7 +176,7 @@ const uint32_t TEST_GROUP_2 = 9754;
       tape.comment = createTapeComment;
       tape.state = cta::common::dataStructures::Tape::DISABLED;
       tape.stateReason = "Test";
-      m_catalogue->createTape(admin, tape);
+      m_catalogue->Tape()->createTape(admin, tape);
     }
 
     cta::common::dataStructures::StorageClass storageClass;
@@ -185,7 +185,7 @@ const uint32_t TEST_GROUP_2 = 9754;
     storageClass.nbCopies = 1;
     storageClass.vo.name = vo.name;
     storageClass.comment = "Create storage class";
-    m_catalogue->createStorageClass(admin, storageClass);
+    m_catalogue->StorageClass()->createStorageClass(admin, storageClass);
 
     ::testing::InSequence dummy;
     std::unique_ptr<cta::ArchiveJob> job1;
@@ -320,11 +320,11 @@ const uint32_t TEST_GROUP_2 = 9754;
 
     cta::common::dataStructures::VirtualOrganization vo = getDefaultVo();
     const auto &di = getDefaultDiskInstance();
-    m_catalogue->createDiskInstance(admin, di.name, di.comment);
-    m_catalogue->createVirtualOrganization(admin,vo);
-    
-    m_catalogue->createLogicalLibrary(admin, logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
-    m_catalogue->createTapePool(admin, tapePoolName, vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+    m_catalogue->DiskInstance()->createDiskInstance(admin, di.name, di.comment);
+    m_catalogue->VO()->createVirtualOrganization(admin,vo);
+
+    m_catalogue->LogicalLibrary()->createLogicalLibrary(admin, logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+    m_catalogue->TapePool()->createTapePool(admin, tapePoolName, vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
     createMediaType(mediaType);
 
     {
@@ -338,7 +338,7 @@ const uint32_t TEST_GROUP_2 = 9754;
       tape.comment = createTapeComment;
       tape.state = cta::common::dataStructures::Tape::DISABLED;
       tape.stateReason = "test";
-      m_catalogue->createTape(admin, tape);
+      m_catalogue->Tape()->createTape(admin, tape);
     }
 
     cta::common::dataStructures::StorageClass storageClass;
@@ -347,7 +347,7 @@ const uint32_t TEST_GROUP_2 = 9754;
     storageClass.nbCopies = 1;
     storageClass.vo.name = vo.name;
     storageClass.comment = "Create storage class";
-    m_catalogue->createStorageClass(admin, storageClass);
+    m_catalogue->StorageClass()->createStorageClass(admin, storageClass);
 
     ::testing::InSequence dummy;
     std::unique_ptr<cta::ArchiveJob> migratedBigFile;
diff --git a/tapeserver/castor/tape/tapeserver/daemon/RecallReportPackerTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/RecallReportPackerTest.cpp
index a737c1a90c..8987367a6b 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/RecallReportPackerTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/RecallReportPackerTest.cpp
@@ -19,7 +19,7 @@
 #include <gtest/gtest.h>
 
 #include "castor/tape/tapeserver/daemon/RecallReportPacker.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "common/exception/Exception.hpp"
 #include "common/log/StringLogger.hpp"
 #include "scheduler/testingMocks/MockRetrieveJob.hpp"
diff --git a/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjectorTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjectorTest.cpp
index c6de418bec..cd14e6a700 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjectorTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjectorTest.cpp
@@ -24,7 +24,7 @@
 #include "castor/tape/tapeserver/daemon/TapeSessionReporter.hpp"
 #include "castor/tape/tapeserver/daemon/TaskWatchDog.hpp"
 #include "castor/tape/tapeserver/drive/FakeDrive.hpp"
-#include "catalogue/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
 #include "common/log/DummyLogger.hpp"
 #include "common/log/StringLogger.hpp"
 #include "common/processCap/ProcessCapDummy.hpp"
diff --git a/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp b/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp
index 6b456b9c2b..2b570a5d7b 100644
--- a/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/file/OsmReaderTest.cpp
@@ -29,28 +29,36 @@
 #include "castor/tape/tapeserver/file/ReadSessionFactory.hpp"
 #include "castor/tape/tapeserver/SCSI/Device.hpp"
 #include "castor/tape/tapeserver/system/Wrapper.hpp"
-#include "scheduler/RetrieveJob.hpp"
-#include "catalogue/DummyCatalogue.hpp"
-#include "common/processCap/ProcessCapDummy.hpp"
+#include "catalogue/dummy/DummyCatalogue.hpp"
+#include "catalogue/dummy/DummyTapeCatalogue.hpp"
 #include "common/log/StdoutLogger.hpp"
 #include "common/log/StringLogger.hpp"
+#include "common/processCap/ProcessCapDummy.hpp"
 #include "scheduler/OStoreDB/OStoreDBFactory.hpp"
+#include "scheduler/RetrieveJob.hpp"
 
 namespace {
 std::string g_device_name;
 std::string g_device_path;
 }
 
-class OSMCatalogue: public cta::catalogue::DummyCatalogue {
+class OSMTapeCatalogue: public cta::catalogue::DummyTapeCatalogue {
 public:
   using LabelFormat = cta::common::dataStructures::Label::Format;
-  OSMCatalogue() = default;
+  OSMTapeCatalogue() = default;
 
   LabelFormat getTapeLabelFormat(const std::string& vid) const override {
     return LabelFormat::OSM;
   }
 };
 
+class OSMCatalogue: public cta::catalogue::DummyCatalogue {
+public:
+  OSMCatalogue() : DummyCatalogue() {
+    DummyCatalogue::m_tape = std::make_unique<OSMTapeCatalogue>();
+  }
+};
+
 class BasicRetrieveJob: public cta::RetrieveJob {
 public:
   BasicRetrieveJob() : cta::RetrieveJob(nullptr,
@@ -128,7 +136,7 @@ TEST_F(OsmReaderTest, ReadOsmTape) {
     castor::tape::tapeserver::daemon::VolumeInfo m_volInfo;
     m_volInfo.vid = m_vid;
     m_volInfo.nbFiles = 1;
-    m_volInfo.labelFormat = m_catalogue->getTapeLabelFormat(m_volInfo.vid);
+    m_volInfo.labelFormat = static_cast<OSMTapeCatalogue*>(m_catalogue->Tape().get())->getTapeLabelFormat(m_volInfo.vid);
     m_volInfo.mountType = cta::common::dataStructures::MountType::Retrieve;
 
     // Now read a random file
diff --git a/tapeserver/daemon/DriveHandler.cpp b/tapeserver/daemon/DriveHandler.cpp
index 3cf70df3ce..da65077b65 100644
--- a/tapeserver/daemon/DriveHandler.cpp
+++ b/tapeserver/daemon/DriveHandler.cpp
@@ -1205,7 +1205,7 @@ int DriveHandler::setDriveDownForShutdown(const std::string& reason, cta::log::L
   driveInfo.logicalLibrary = m_configLine.logicalLibrary;
   driveInfo.host = cta::utils::getShortHostname();
 
-  auto driveState = m_catalogue->getTapeDrive(driveInfo.driveName);
+  auto driveState = m_catalogue->DriveState()->getTapeDrive(driveInfo.driveName);
   if (!driveState) {
     lc->log(cta::log::WARNING, "In DriveHandler::setDriveDownForShutdown(). TapeDrive to set down doesn't exist.");
     return castor::tape::tapeserver::daemon::Session::MARK_DRIVE_AS_DOWN;
diff --git a/tapeserver/readtp/ReadtpCmd.cpp b/tapeserver/readtp/ReadtpCmd.cpp
index f88e5700ed..3e44487102 100644
--- a/tapeserver/readtp/ReadtpCmd.cpp
+++ b/tapeserver/readtp/ReadtpCmd.cpp
@@ -363,8 +363,8 @@ void ReadtpCmd::readTapeFiles(
 
     catalogue::TapeSearchCriteria searchCriteria;
     searchCriteria.vid = m_vid;
-
-    auto tapeList = m_catalogue->getTapes(searchCriteria);
+    
+    auto tapeList = m_catalogue->Tape()->getTapes(searchCriteria);
     if (tapeList.empty()) {
       std::list<cta::log::Param> params;
         params.push_back(cta::log::Param("userName", getUsername()));
@@ -465,7 +465,7 @@ void ReadtpCmd::readTapeFile(
   catalogue::TapeFileSearchCriteria searchCriteria;
   searchCriteria.vid = m_vid;
   searchCriteria.fSeq = fSeq;
-  auto itor = m_catalogue->getArchiveFilesItor(searchCriteria);
+  auto itor = m_catalogue->ArchiveFile()->getArchiveFilesItor(searchCriteria);
 
   if (!itor.hasMore()) {
     throw tapeserver::readtp::NoSuchFSeqException();
diff --git a/tapeserver/tapelabel/TapeLabelCmd.cpp b/tapeserver/tapelabel/TapeLabelCmd.cpp
index d934fe931a..a73af72ccd 100644
--- a/tapeserver/tapelabel/TapeLabelCmd.cpp
+++ b/tapeserver/tapelabel/TapeLabelCmd.cpp
@@ -87,7 +87,7 @@ int TapeLabelCmd::exceptionThrowingMain(const int argc, char *const *const argv)
   const std::string capabilities("cap_sys_rawio+ep");
   setProcessCapabilities(capabilities);
   
-  m_catalogue->checkTapeForLabel(m_vid);
+  m_catalogue->Tape()->checkTapeForLabel(m_vid);
   
   std::unique_ptr<castor::tape::tapeserver::drive::DriveInterface> drivePtr = createDrive();
   castor::tape::tapeserver::drive::DriveInterface &drive = *drivePtr.get();
@@ -145,7 +145,7 @@ int TapeLabelCmd::exceptionThrowingMain(const int argc, char *const *const argv)
   dismountTape(m_vid);
   drive.disableLogicalBlockProtection();
   if(!returnCode) {
-    m_catalogue->tapeLabelled(m_vid, m_unitName);
+    m_catalogue->Tape()->tapeLabelled(m_vid, m_unitName);
   }
   return returnCode;
 }
diff --git a/xroot_plugins/XrdCtaActivityMountRuleLs.hpp b/xroot_plugins/XrdCtaActivityMountRuleLs.hpp
index eb11a4e7d1..656180c489 100644
--- a/xroot_plugins/XrdCtaActivityMountRuleLs.hpp
+++ b/xroot_plugins/XrdCtaActivityMountRuleLs.hpp
@@ -58,7 +58,7 @@ private:
 
 ActivityMountRuleLsStream::ActivityMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_activityMountRuleList(catalogue.getRequesterActivityMountRules())
+  m_activityMountRuleList(catalogue.RequesterActivityMountRule()->getRequesterActivityMountRules())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaAdminLs.hpp b/xroot_plugins/XrdCtaAdminLs.hpp
index 3063f08a17..e671b6122a 100644
--- a/xroot_plugins/XrdCtaAdminLs.hpp
+++ b/xroot_plugins/XrdCtaAdminLs.hpp
@@ -61,7 +61,7 @@ private:
 
 AdminLsStream::AdminLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_adminList(catalogue.getAdminUsers())
+  m_adminList(catalogue.AdminUser()->getAdminUsers())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaArchiveRouteLs.hpp b/xroot_plugins/XrdCtaArchiveRouteLs.hpp
index 8d424eaf11..0166080712 100644
--- a/xroot_plugins/XrdCtaArchiveRouteLs.hpp
+++ b/xroot_plugins/XrdCtaArchiveRouteLs.hpp
@@ -58,7 +58,7 @@ private:
 
 ArchiveRouteLsStream::ArchiveRouteLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_archiveRouteList(catalogue.getArchiveRoutes())
+  m_archiveRouteList(catalogue.ArchiveRoute()->getArchiveRoutes())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaChangeStorageClass.hpp b/xroot_plugins/XrdCtaChangeStorageClass.hpp
index 1f3f1395ef..5bbdccd197 100644
--- a/xroot_plugins/XrdCtaChangeStorageClass.hpp
+++ b/xroot_plugins/XrdCtaChangeStorageClass.hpp
@@ -28,7 +28,7 @@ public:
   XrdCtaChangeStorageClass(cta::catalogue::Catalogue &catalogue, cta::log::LogContext &lc);
   void updateCatalogue(const std::vector<std::basic_string<char>>& archiveFileIds, const std::string& newStorageClassName);
 private:
-  const cta::catalogue::Catalogue &m_catalogue;
+  cta::catalogue::Catalogue &m_catalogue;
   cta::log::LogContext &m_lc;
 };
 
@@ -37,7 +37,7 @@ XrdCtaChangeStorageClass::XrdCtaChangeStorageClass(cta::catalogue::Catalogue &ca
 void XrdCtaChangeStorageClass::updateCatalogue(const std::vector<std::basic_string<char>>& archiveFileIds, const std::string& newStorageClassName) {
   for (auto & id : archiveFileIds) {
     const uint64_t archiveFileId = std::stoul(id);
-    m_catalogue.modifyArchiveFileStorageClassId(archiveFileId, newStorageClassName);
+    m_catalogue.ArchiveFile()->modifyArchiveFileStorageClassId(archiveFileId, newStorageClassName);
   }
 }
 } // namespace xrd
diff --git a/xroot_plugins/XrdCtaDiskInstanceLs.hpp b/xroot_plugins/XrdCtaDiskInstanceLs.hpp
index d87c290aa5..2e875c3ac9 100644
--- a/xroot_plugins/XrdCtaDiskInstanceLs.hpp
+++ b/xroot_plugins/XrdCtaDiskInstanceLs.hpp
@@ -60,7 +60,7 @@ private:
 
 DiskInstanceLsStream::DiskInstanceLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_diskInstanceList(catalogue.getAllDiskInstances())
+  m_diskInstanceList(catalogue.DiskInstance()->getAllDiskInstances())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaDiskInstanceSpaceLs.hpp b/xroot_plugins/XrdCtaDiskInstanceSpaceLs.hpp
index 2ba9572a88..e12771ad0d 100644
--- a/xroot_plugins/XrdCtaDiskInstanceSpaceLs.hpp
+++ b/xroot_plugins/XrdCtaDiskInstanceSpaceLs.hpp
@@ -60,7 +60,7 @@ private:
 
 DiskInstanceSpaceLsStream::DiskInstanceSpaceLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_diskInstanceSpaceList(catalogue.getAllDiskInstanceSpaces())
+  m_diskInstanceSpaceList(catalogue.DiskInstanceSpace()->getAllDiskInstanceSpaces())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaDiskSystemLs.hpp b/xroot_plugins/XrdCtaDiskSystemLs.hpp
index b5178547e8..4a8cc9b5c2 100644
--- a/xroot_plugins/XrdCtaDiskSystemLs.hpp
+++ b/xroot_plugins/XrdCtaDiskSystemLs.hpp
@@ -60,7 +60,7 @@ private:
 
 DiskSystemLsStream::DiskSystemLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_diskSystemList(catalogue.getAllDiskSystems())
+  m_diskSystemList(catalogue.DiskSystem()->getAllDiskSystems())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaDriveLs.hpp b/xroot_plugins/XrdCtaDriveLs.hpp
index b6a9d618da..7244c4ea01 100644
--- a/xroot_plugins/XrdCtaDriveLs.hpp
+++ b/xroot_plugins/XrdCtaDriveLs.hpp
@@ -22,10 +22,11 @@
 #include <string>
 #include <utility>
 
-#include <common/dataStructures/DriveStatusSerDeser.hpp>
-#include <common/dataStructures/MountTypeSerDeser.hpp>
-#include <xroot_plugins/XrdCtaStream.hpp>
-#include <xroot_plugins/XrdSsiCtaRequestMessage.hpp>
+#include "catalogue/Catalogue.hpp"
+#include "common/dataStructures/DriveStatusSerDeser.hpp"
+#include "common/dataStructures/MountTypeSerDeser.hpp"
+#include "xroot_plugins/XrdCtaStream.hpp"
+#include "xroot_plugins/XrdSsiCtaRequestMessage.hpp"
 
 namespace cta { namespace xrd {
 
@@ -62,7 +63,7 @@ class DriveLsStream: public XrdCtaStream{
   static constexpr const char* const LOG_SUFFIX  = "DriveLsStream";    //!< Identifier for log messages
 
   std::list<common::dataStructures::TapeDrive> m_tapeDrives;
-  std::list<cta::catalogue::Catalogue::DriveConfig> m_tapeDrivesConfigs;
+  std::list<cta::catalogue::DriveConfigCatalogue::DriveConfig> m_tapeDrivesConfigs;
 };
 
 
@@ -71,8 +72,8 @@ DriveLsStream::DriveLsStream(const RequestMessage &requestMsg, cta::catalogue::C
   log::LogContext &lc) :
   XrdCtaStream(catalogue, scheduler),
   m_lc(lc),
-  m_tapeDrives(m_catalogue.getTapeDrives()),
-  m_tapeDrivesConfigs(m_catalogue.getTapeDriveConfigs()) {
+  m_tapeDrives(m_catalogue.DriveState()->getTapeDrives()),
+  m_tapeDrivesConfigs(m_catalogue.DriveConfig()->getTapeDriveConfigs()) {
   XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "DriveLsStream() constructor");
 
   auto driveRegexOpt = requestMsg.getOptional(cta::admin::OptionString::DRIVE);
diff --git a/xroot_plugins/XrdCtaGroupMountRuleLs.hpp b/xroot_plugins/XrdCtaGroupMountRuleLs.hpp
index 8f94f853d3..ad6f9fb497 100644
--- a/xroot_plugins/XrdCtaGroupMountRuleLs.hpp
+++ b/xroot_plugins/XrdCtaGroupMountRuleLs.hpp
@@ -59,7 +59,7 @@ private:
 
 GroupMountRuleLsStream::GroupMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_groupMountRuleList(catalogue.getRequesterGroupMountRules())
+  m_groupMountRuleList(catalogue.RequesterGroupMountRule()->getRequesterGroupMountRules())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaLogicalLibraryLs.hpp b/xroot_plugins/XrdCtaLogicalLibraryLs.hpp
index fdd1e666b4..c68e2af826 100644
--- a/xroot_plugins/XrdCtaLogicalLibraryLs.hpp
+++ b/xroot_plugins/XrdCtaLogicalLibraryLs.hpp
@@ -65,7 +65,7 @@ private:
 LogicalLibraryLsStream::LogicalLibraryLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue,
   cta::Scheduler &scheduler, const std::optional<bool>& disabled) :
   XrdCtaStream(catalogue, scheduler),
-  m_logicalLibraryList(catalogue.getLogicalLibraries()),
+  m_logicalLibraryList(catalogue.LogicalLibrary()->getLogicalLibraries()),
   m_disabled(disabled) {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaMediaTypeLs.hpp b/xroot_plugins/XrdCtaMediaTypeLs.hpp
index d24126da5b..dd6a7a461d 100644
--- a/xroot_plugins/XrdCtaMediaTypeLs.hpp
+++ b/xroot_plugins/XrdCtaMediaTypeLs.hpp
@@ -58,7 +58,7 @@ private:
 
 MediaTypeLsStream::MediaTypeLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_mediaTypeList(catalogue.getMediaTypes())
+  m_mediaTypeList(catalogue.MediaType()->getMediaTypes())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaMountPolicyLs.hpp b/xroot_plugins/XrdCtaMountPolicyLs.hpp
index 0b0ce3019f..1cef92fa98 100644
--- a/xroot_plugins/XrdCtaMountPolicyLs.hpp
+++ b/xroot_plugins/XrdCtaMountPolicyLs.hpp
@@ -58,7 +58,7 @@ private:
 
 MountPolicyLsStream::MountPolicyLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_mountPolicyList(catalogue.getMountPolicies())
+  m_mountPolicyList(catalogue.MountPolicy()->getMountPolicies())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaRecycleTapeFileLs.hpp b/xroot_plugins/XrdCtaRecycleTapeFileLs.hpp
index bcb4f6b38c..272f5771bc 100644
--- a/xroot_plugins/XrdCtaRecycleTapeFileLs.hpp
+++ b/xroot_plugins/XrdCtaRecycleTapeFileLs.hpp
@@ -51,7 +51,7 @@ private:
    */
   virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
 
-  cta::catalogue::Catalogue::FileRecycleLogItor m_fileRecycleLogItor;     //!< List of recycle tape files from the catalogue
+  cta::catalogue::FileRecycleLogItor m_fileRecycleLogItor;     //!< List of recycle tape files from the catalogue
 
   static constexpr const char* const LOG_SUFFIX  = "RecycleTapeFileLsStream";    //!< Identifier for log messages
 };
@@ -95,7 +95,7 @@ RecycleTapeFileLsStream::RecycleTapeFileLsStream(const RequestMessage &requestMs
     throw cta::exception::UserError("Must specify at least one of the following search options: vid, fxid, fxidfile or archiveFileId");
   }
   
-  m_fileRecycleLogItor = catalogue.getFileRecycleLogItor(searchCriteria);
+  m_fileRecycleLogItor = catalogue.FileRecycleLog()->getFileRecycleLogItor(searchCriteria);
           
   XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "RecycleTapeFileLsStream() constructor");
 }
diff --git a/xroot_plugins/XrdCtaRepackLs.hpp b/xroot_plugins/XrdCtaRepackLs.hpp
index d35baf0024..974cfe24c1 100644
--- a/xroot_plugins/XrdCtaRepackLs.hpp
+++ b/xroot_plugins/XrdCtaRepackLs.hpp
@@ -60,7 +60,7 @@ namespace cta { namespace xrd {
                  std::inserter(tapeVids, tapeVids.begin()),
                  [](cta::common::dataStructures::RepackInfo &ri) {return ri.vid;});
       
-      cta::common::dataStructures::VidToTapeMap tapeVidMap = m_catalogue.getTapesByVid(tapeVids); // throws an exception if a vid does not exist
+      cta::common::dataStructures::VidToTapeMap tapeVidMap = m_catalogue.Tape()->getTapesByVid(tapeVids); // throws an exception if a vid does not exist
 
       for(bool is_buffer_full = false; !m_repackList.empty() && !is_buffer_full; m_repackList.pop_front()){
         Data record;
diff --git a/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp b/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp
index 199dc63fcd..3767ce5fbf 100644
--- a/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp
+++ b/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp
@@ -58,7 +58,7 @@ private:
 
 RequesterMountRuleLsStream::RequesterMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_requesterMountRuleList(catalogue.getRequesterMountRules())
+  m_requesterMountRuleList(catalogue.RequesterMountRule()->getRequesterMountRules())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdCtaStorageClassLs.hpp b/xroot_plugins/XrdCtaStorageClassLs.hpp
index a881f809fa..93c522651f 100644
--- a/xroot_plugins/XrdCtaStorageClassLs.hpp
+++ b/xroot_plugins/XrdCtaStorageClassLs.hpp
@@ -68,9 +68,9 @@ StorageClassLsStream::StorageClassLsStream(const RequestMessage &requestMsg, cta
   XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "StorageClassLsStream() constructor");
 
   if(m_storageClassName) {
-    m_storageClassList.push_back(m_catalogue.getStorageClass(m_storageClassName.value()));
+    m_storageClassList.push_back(m_catalogue.StorageClass()->getStorageClass(m_storageClassName.value()));
   } else {
-    for(const auto &storageClass : m_catalogue.getStorageClasses()) {
+    for(const auto &storageClass : m_catalogue.StorageClass()->getStorageClasses()) {
       m_storageClassList.push_back(storageClass);
     }
   }
diff --git a/xroot_plugins/XrdCtaTapeFileLs.hpp b/xroot_plugins/XrdCtaTapeFileLs.hpp
index 43cbc9bf61..6f53c41a9b 100644
--- a/xroot_plugins/XrdCtaTapeFileLs.hpp
+++ b/xroot_plugins/XrdCtaTapeFileLs.hpp
@@ -53,9 +53,9 @@ private:
    */
   virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
 
-  catalogue::Catalogue::ArchiveFileItor m_tapeFileItor;       //!< Iterator across files which have been archived
-  grpc::EndpointMap                     m_endpoints;          //!< List of gRPC endpoints
-  bool                                  m_LookupNamespace;    //!< True if namespace lookup is required
+  catalogue::ArchiveFileItor m_tapeFileItor;       //!< Iterator across files which have been archived
+  grpc::EndpointMap          m_endpoints;          //!< List of gRPC endpoints
+  bool                       m_LookupNamespace;    //!< True if namespace lookup is required
 
   static constexpr const char* const LOG_SUFFIX  = "TapeFileLsStream";    //!< Identifier for log messages
 };
@@ -101,7 +101,7 @@ TapeFileLsStream::TapeFileLsStream(const RequestMessage &requestMsg,
     throw cta::exception::UserError("Must specify at least one of the following search options: vid, fxid, fxidfile or archiveFileId");
   }
 
-  m_tapeFileItor = m_catalogue.getArchiveFilesItor(searchCriteria);
+  m_tapeFileItor = m_catalogue.ArchiveFile()->getArchiveFilesItor(searchCriteria);
 }
 
 
diff --git a/xroot_plugins/XrdCtaTapeLs.hpp b/xroot_plugins/XrdCtaTapeLs.hpp
index df99d6936f..24132dcb7f 100644
--- a/xroot_plugins/XrdCtaTapeLs.hpp
+++ b/xroot_plugins/XrdCtaTapeLs.hpp
@@ -88,7 +88,7 @@ TapeLsStream::TapeLsStream(const RequestMessage &requestMsg, cta::catalogue::Cat
     throw cta::exception::UserError("Cannot specify --all together with other search options");
   }
 
-  m_tapeList = m_catalogue.getTapes(searchCriteria);
+  m_tapeList = m_catalogue.Tape()->getTapes(searchCriteria);
 }
 
 
diff --git a/xroot_plugins/XrdCtaTapePoolLs.hpp b/xroot_plugins/XrdCtaTapePoolLs.hpp
index 6af30eec99..6dca27add3 100644
--- a/xroot_plugins/XrdCtaTapePoolLs.hpp
+++ b/xroot_plugins/XrdCtaTapePoolLs.hpp
@@ -59,7 +59,7 @@ private:
 
 TapePoolLsStream::TapePoolLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_tapePoolList(catalogue.getTapePools())
+  m_tapePoolList(catalogue.TapePool()->getTapePools())
 {
   using namespace cta::admin;
 
@@ -70,7 +70,7 @@ TapePoolLsStream::TapePoolLsStream(const RequestMessage &requestMsg, cta::catalo
   searchCriteria.vo = requestMsg.getOptional(OptionString::VO);
   searchCriteria.encrypted = requestMsg.getOptional(OptionBoolean::ENCRYPTED);
 
-  m_tapePoolList = m_catalogue.getTapePools(searchCriteria);
+  m_tapePoolList = m_catalogue.TapePool()->getTapePools(searchCriteria);
 }
 
 int TapePoolLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
diff --git a/xroot_plugins/XrdCtaVersion.hpp b/xroot_plugins/XrdCtaVersion.hpp
index 0616875bc3..8f6e712a06 100644
--- a/xroot_plugins/XrdCtaVersion.hpp
+++ b/xroot_plugins/XrdCtaVersion.hpp
@@ -68,8 +68,8 @@ VersionStream::VersionStream(const RequestMessage &requestMsg, cta::catalogue::C
   XrdCtaStream(catalogue, scheduler),
   m_client_versions(requestMsg.getClientVersions()),
   m_catalogue_conn_string(catalogueConnString),
-  m_catalogue_version(m_catalogue.getSchemaVersion().getSchemaVersion<std::string>()),
-  m_is_upgrading(m_catalogue.getSchemaVersion().getStatus<catalogue::SchemaVersion::Status>()
+  m_catalogue_version(m_catalogue.Schema()->getSchemaVersion().getSchemaVersion<std::string>()),
+  m_is_upgrading(m_catalogue.Schema()->getSchemaVersion().getStatus<catalogue::SchemaVersion::Status>()
     == catalogue::SchemaVersion::Status::UPGRADING) {
   XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "VersionStream() constructor");
   m_server_versions.ctaVersion = CTA_VERSION;
diff --git a/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp b/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp
index 22c6c90708..1b45e21351 100644
--- a/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp
+++ b/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp
@@ -60,7 +60,7 @@ private:
 
 VirtualOrganizationLsStream::VirtualOrganizationLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
   XrdCtaStream(catalogue, scheduler),
-  m_virtualOrganizationList(catalogue.getVirtualOrganizations())
+  m_virtualOrganizationList(catalogue.VO()->getVirtualOrganizations())
 {
   using namespace cta::admin;
 
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.cpp b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
index d81226f41a..66d64a57e6 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.cpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
@@ -538,7 +538,7 @@ void RequestMessage::processCLOSEW(const cta::eos::Notification &notification, c
       throw cta::exception::UserError("File is in fail_on_closew_test storage class, which always fails.");
    }
 
-   auto storageClass = m_catalogue.getStorageClass(storageClassItor->second);
+   auto storageClass = m_catalogue.StorageClass()->getStorageClass(storageClassItor->second);
 
    // Disallow archival of files above the specified limit
    if(storageClass.vo.maxFileSize && notification.file().size() > storageClass.vo.maxFileSize) {
@@ -784,7 +784,7 @@ void RequestMessage::processDELETE(const cta::eos::Notification &notification, c
    cta::utils::Timer t;
    cta::log::TimingList tl;
    try {
-     request.archiveFile = m_catalogue.getArchiveFileById(request.archiveFileID);
+     request.archiveFile = m_catalogue.ArchiveFile()->getArchiveFileById(request.archiveFileID);
      tl.insertAndReset("catalogueGetArchiveFileByIdTime",t);
    } catch (cta::exception::Exception &ex){
     log::ScopedParamContainer spc(m_lc);
@@ -835,7 +835,7 @@ void RequestMessage::processUPDATE_FID(const cta::eos::Notification &notificatio
 
    // Update the disk file ID
    cta::utils::Timer t;
-   m_catalogue.updateDiskFileId(archiveFileId, diskInstance, diskFileId);
+   m_catalogue.ArchiveFile()->updateDiskFileId(archiveFileId, diskInstance, diskFileId);
 
    // Create a log entry
    cta::log::ScopedParamContainer params(m_lc);
@@ -958,7 +958,7 @@ void RequestMessage::processAdmin_Add(cta::xrd::Response &response)
    auto &username = getRequired(OptionString::USERNAME);
    auto &comment  = getRequired(OptionString::COMMENT);
 
-   m_catalogue.createAdminUser(m_cliIdentity, username, comment);
+   m_catalogue.AdminUser()->createAdminUser(m_cliIdentity, username, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -972,7 +972,7 @@ void RequestMessage::processAdmin_Ch(cta::xrd::Response &response)
    auto &username = getRequired(OptionString::USERNAME);
    auto &comment  = getRequired(OptionString::COMMENT);
 
-   m_catalogue.modifyAdminUserComment(m_cliIdentity, username, comment);
+   m_catalogue.AdminUser()->modifyAdminUserComment(m_cliIdentity, username, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -985,7 +985,7 @@ void RequestMessage::processAdmin_Rm(cta::xrd::Response &response)
 
    auto &username = getRequired(OptionString::USERNAME);
 
-   m_catalogue.deleteAdminUser(username);
+   m_catalogue.AdminUser()->deleteAdminUser(username);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1012,7 +1012,7 @@ void RequestMessage::processArchiveRoute_Add(cta::xrd::Response &response)
    auto &tapepool = getRequired(OptionString::TAPE_POOL);
    auto &comment  = getRequired(OptionString::COMMENT);
 
-   m_catalogue.createArchiveRoute(m_cliIdentity, scn, cn, tapepool, comment);
+   m_catalogue.ArchiveRoute()->createArchiveRoute(m_cliIdentity, scn, cn, tapepool, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1029,10 +1029,10 @@ void RequestMessage::processArchiveRoute_Ch(cta::xrd::Response &response)
    auto  comment  = getOptional(OptionString::COMMENT);
 
    if(comment) {
-      m_catalogue.modifyArchiveRouteComment(m_cliIdentity, scn, cn, comment.value());
+      m_catalogue.ArchiveRoute()->modifyArchiveRouteComment(m_cliIdentity, scn, cn, comment.value());
    }
    if(tapepool) {
-      m_catalogue.modifyArchiveRouteTapePoolName(m_cliIdentity, scn, cn, tapepool.value());
+      m_catalogue.ArchiveRoute()->modifyArchiveRouteTapePoolName(m_cliIdentity, scn, cn, tapepool.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1047,7 +1047,7 @@ void RequestMessage::processArchiveRoute_Rm(cta::xrd::Response &response)
    auto &scn = getRequired(OptionString::STORAGE_CLASS);
    auto &cn  = getRequired(OptionUInt64::COPY_NUMBER);
 
-   m_catalogue.deleteArchiveRoute(scn, cn);
+   m_catalogue.ArchiveRoute()->deleteArchiveRoute(scn, cn);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1146,7 +1146,7 @@ void RequestMessage::processDrive_Rm(cta::xrd::Response &response)
   regex = '^' + regex + '$';
   cta::utils::Regex driveNameRegex(regex.c_str());
 
-  const auto tapeDriveNames = m_catalogue.getTapeDriveNames();
+  const auto tapeDriveNames = m_catalogue.DriveState()->getTapeDriveNames();
   bool drivesFound = false;
 
   for (auto tapeDriveName : tapeDriveNames)
@@ -1154,7 +1154,7 @@ void RequestMessage::processDrive_Rm(cta::xrd::Response &response)
     const auto regexResult = driveNameRegex.exec(tapeDriveName);
     if (!regexResult.empty())
     {
-      const auto tapeDrive = m_catalogue.getTapeDrive(tapeDriveName).value();
+      const auto tapeDrive = m_catalogue.DriveState()->getTapeDrive(tapeDriveName).value();
 
       if (tapeDrive.driveStatus == cta::common::dataStructures::DriveStatus::Down     ||
           tapeDrive.driveStatus == cta::common::dataStructures::DriveStatus::Shutdown ||
@@ -1220,7 +1220,7 @@ void RequestMessage::processGroupMountRule_Add(cta::xrd::Response &response)
    auto &name        = getRequired(OptionString::USERNAME);
    auto &comment     = getRequired(OptionString::COMMENT);
 
-   m_catalogue.createRequesterGroupMountRule(m_cliIdentity, mountpolicy, in, name, comment);
+   m_catalogue.RequesterGroupMountRule()->createRequesterGroupMountRule(m_cliIdentity, mountpolicy, in, name, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1237,10 +1237,10 @@ void RequestMessage::processGroupMountRule_Ch(cta::xrd::Response &response)
    auto  comment     = getOptional(OptionString::COMMENT);
 
    if(comment) {
-      m_catalogue.modifyRequesterGroupMountRuleComment(m_cliIdentity, in, name, comment.value());
+      m_catalogue.RequesterGroupMountRule()->modifyRequesterGroupMountRuleComment(m_cliIdentity, in, name, comment.value());
    }
    if(mountpolicy) {
-      m_catalogue.modifyRequesterGroupMountRulePolicy(m_cliIdentity, in, name, mountpolicy.value());
+      m_catalogue.RequesterGroupMountRule()->modifyRequesterGroupMountRulePolicy(m_cliIdentity, in, name, mountpolicy.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1255,7 +1255,7 @@ void RequestMessage::processGroupMountRule_Rm(cta::xrd::Response &response)
    auto &in   = getRequired(OptionString::INSTANCE);
    auto &name = getRequired(OptionString::USERNAME);
 
-   m_catalogue.deleteRequesterGroupMountRule(in, name);
+   m_catalogue.RequesterGroupMountRule()->deleteRequesterGroupMountRule(in, name);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1280,7 +1280,7 @@ void RequestMessage::processLogicalLibrary_Add(cta::xrd::Response &response)
    auto isDisabled = getOptional(OptionBoolean::DISABLED);
    auto &comment   = getRequired(OptionString::COMMENT);
 
-   m_catalogue.createLogicalLibrary(m_cliIdentity, name, isDisabled ? isDisabled.value() : false, comment);
+   m_catalogue.LogicalLibrary()->createLogicalLibrary(m_cliIdentity, name, isDisabled ? isDisabled.value() : false, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1297,17 +1297,17 @@ void RequestMessage::processLogicalLibrary_Ch(cta::xrd::Response &response)
    auto  disabledReason   = getOptional(OptionString::DISABLED_REASON);
 
    if(disabled) {
-      m_catalogue.setLogicalLibraryDisabled(m_cliIdentity, name, disabled.value());
+      m_catalogue.LogicalLibrary()->setLogicalLibraryDisabled(m_cliIdentity, name, disabled.value());
       if ((!disabled.value()) && (!disabledReason)) {
          //if enabling the tape and the reason is not specified in the command, erase the reason
-         m_catalogue.modifyLogicalLibraryDisabledReason(m_cliIdentity, name, "");
+         m_catalogue.LogicalLibrary()->modifyLogicalLibraryDisabledReason(m_cliIdentity, name, "");
       }
    }
    if(comment) {
-      m_catalogue.modifyLogicalLibraryComment(m_cliIdentity, name, comment.value());
+      m_catalogue.LogicalLibrary()->modifyLogicalLibraryComment(m_cliIdentity, name, comment.value());
    }
    if (disabledReason) {
-      m_catalogue.modifyLogicalLibraryDisabledReason(m_cliIdentity, name, disabledReason.value());
+      m_catalogue.LogicalLibrary()->modifyLogicalLibraryDisabledReason(m_cliIdentity, name, disabledReason.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1321,7 +1321,7 @@ void RequestMessage::processLogicalLibrary_Rm(cta::xrd::Response &response)
 
    auto &name = getRequired(OptionString::LOGICAL_LIBRARY);
 
-   m_catalogue.deleteLogicalLibrary(name);
+   m_catalogue.LogicalLibrary()->deleteLogicalLibrary(name);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1380,7 +1380,7 @@ void RequestMessage::processMediaType_Add(cta::xrd::Response &response)
    mediaType.minLPos              = getOptional(OptionUInt64::MIN_LPOS);
    mediaType.maxLPos              = getOptional(OptionUInt64::MAX_LPOS);
    mediaType.comment              = getRequired(OptionString::COMMENT);
-   m_catalogue.createMediaType(m_cliIdentity, mediaType);
+   m_catalogue.MediaType()->createMediaType(m_cliIdentity, mediaType);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1421,26 +1421,26 @@ void RequestMessage::processMediaType_Ch(cta::xrd::Response &response)
   }
 
   if(cartridge){
-    m_catalogue.modifyMediaTypeCartridge(m_cliIdentity,mediaTypeName,cartridge.value());
+    m_catalogue.MediaType()->modifyMediaTypeCartridge(m_cliIdentity,mediaTypeName,cartridge.value());
   }
   if(primaryDensityCode){
-    m_catalogue.modifyMediaTypePrimaryDensityCode(m_cliIdentity,mediaTypeName,primaryDensityCode.value());
+    m_catalogue.MediaType()->modifyMediaTypePrimaryDensityCode(m_cliIdentity,mediaTypeName,primaryDensityCode.value());
   }
   if(secondaryDensityCode){
-    m_catalogue.modifyMediaTypeSecondaryDensityCode(m_cliIdentity,mediaTypeName,secondaryDensityCode.value());
+    m_catalogue.MediaType()->modifyMediaTypeSecondaryDensityCode(m_cliIdentity,mediaTypeName,secondaryDensityCode.value());
   }
   if(nbWraps){
     std::optional<uint32_t> newNbWraps = nbWraps.value();
-    m_catalogue.modifyMediaTypeNbWraps(m_cliIdentity,mediaTypeName,newNbWraps);
+    m_catalogue.MediaType()->modifyMediaTypeNbWraps(m_cliIdentity,mediaTypeName,newNbWraps);
   }
   if(minlpos){
-    m_catalogue.modifyMediaTypeMinLPos(m_cliIdentity, mediaTypeName, minlpos);
+    m_catalogue.MediaType()->modifyMediaTypeMinLPos(m_cliIdentity, mediaTypeName, minlpos);
   }
   if(maxlpos){
-    m_catalogue.modifyMediaTypeMaxLPos(m_cliIdentity,mediaTypeName,maxlpos);
+    m_catalogue.MediaType()->modifyMediaTypeMaxLPos(m_cliIdentity,mediaTypeName,maxlpos);
   }
   if(comment){
-    m_catalogue.modifyMediaTypeComment(m_cliIdentity,mediaTypeName,comment.value());
+    m_catalogue.MediaType()->modifyMediaTypeComment(m_cliIdentity,mediaTypeName,comment.value());
   }
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1453,7 +1453,7 @@ void RequestMessage::processMediaType_Rm(cta::xrd::Response &response)
 
    const auto &mtn = getRequired(OptionString::MEDIA_TYPE);
 
-   m_catalogue.deleteMediaType(mtn);
+   m_catalogue.MediaType()->deleteMediaType(mtn);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1492,7 +1492,7 @@ void RequestMessage::processMountPolicy_Add(cta::xrd::Response &response)
    mountPolicy.minRetrieveRequestAge = minretrieverequestage;
    mountPolicy.comment = comment;
 
-   m_catalogue.createMountPolicy(m_cliIdentity, mountPolicy);
+   m_catalogue.MountPolicy()->createMountPolicy(m_cliIdentity, mountPolicy);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1511,19 +1511,19 @@ void RequestMessage::processMountPolicy_Ch(cta::xrd::Response &response)
    auto  comment               = getOptional(OptionString::COMMENT);
 
    if(archivepriority) {
-      m_catalogue.modifyMountPolicyArchivePriority(m_cliIdentity, group, archivepriority.value());
+      m_catalogue.MountPolicy()->modifyMountPolicyArchivePriority(m_cliIdentity, group, archivepriority.value());
    }
    if(minarchiverequestage) {
-      m_catalogue.modifyMountPolicyArchiveMinRequestAge(m_cliIdentity, group, minarchiverequestage.value());
+      m_catalogue.MountPolicy()->modifyMountPolicyArchiveMinRequestAge(m_cliIdentity, group, minarchiverequestage.value());
    }
    if(retrievepriority) {
-      m_catalogue.modifyMountPolicyRetrievePriority(m_cliIdentity, group, retrievepriority.value());
+      m_catalogue.MountPolicy()->modifyMountPolicyRetrievePriority(m_cliIdentity, group, retrievepriority.value());
    }
    if(minretrieverequestage) {
-      m_catalogue.modifyMountPolicyRetrieveMinRequestAge(m_cliIdentity, group, minretrieverequestage.value());
+      m_catalogue.MountPolicy()->modifyMountPolicyRetrieveMinRequestAge(m_cliIdentity, group, minretrieverequestage.value());
    }
    if(comment) {
-      m_catalogue.modifyMountPolicyComment(m_cliIdentity, group, comment.value());
+      m_catalogue.MountPolicy()->modifyMountPolicyComment(m_cliIdentity, group, comment.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1537,7 +1537,7 @@ void RequestMessage::processMountPolicy_Rm(cta::xrd::Response &response)
 
    auto &group = getRequired(OptionString::MOUNT_POLICY);
 
-   m_catalogue.deleteMountPolicy(group);
+   m_catalogue.MountPolicy()->deleteMountPolicy(group);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1579,7 +1579,7 @@ void RequestMessage::processRepack_Add(cta::xrd::Response &response)
   //Get the mountpolicy from the catalogue
   common::dataStructures::MountPolicy mountPolicy;
   typedef std::list<common::dataStructures::MountPolicy> MountPolicyList;
-  MountPolicyList mountPolicies = m_catalogue.getMountPolicies();
+  MountPolicyList mountPolicies = m_catalogue.MountPolicy()->getMountPolicies();
   MountPolicyList::const_iterator repackMountPolicyItor = std::find_if(mountPolicies.begin(),mountPolicies.end(),[mountPolicyProvidedByUser](const common::dataStructures::MountPolicy & mp){
     return mp.name == mountPolicyProvidedByUser;
   });
@@ -1678,7 +1678,7 @@ void RequestMessage::processRequesterMountRule_Add(cta::xrd::Response &response)
    auto &name        = getRequired(OptionString::USERNAME);
    auto &comment     = getRequired(OptionString::COMMENT);
 
-   m_catalogue.createRequesterMountRule(m_cliIdentity, mountpolicy, in, name, comment);
+   m_catalogue.RequesterMountRule()->createRequesterMountRule(m_cliIdentity, mountpolicy, in, name, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1695,10 +1695,10 @@ void RequestMessage::processRequesterMountRule_Ch(cta::xrd::Response &response)
    auto  mountpolicy = getOptional(OptionString::MOUNT_POLICY);
 
    if(comment) {
-      m_catalogue.modifyRequesteMountRuleComment(m_cliIdentity, in, name, comment.value());
+      m_catalogue.RequesterMountRule()->modifyRequesteMountRuleComment(m_cliIdentity, in, name, comment.value());
    }
    if(mountpolicy) {
-      m_catalogue.modifyRequesterMountRulePolicy(m_cliIdentity, in, name, mountpolicy.value());
+      m_catalogue.RequesterMountRule()->modifyRequesterMountRulePolicy(m_cliIdentity, in, name, mountpolicy.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1713,7 +1713,7 @@ void RequestMessage::processRequesterMountRule_Rm(cta::xrd::Response &response)
    auto &in   = getRequired(OptionString::INSTANCE);
    auto &name = getRequired(OptionString::USERNAME);
 
-   m_catalogue.deleteRequesterMountRule(in, name);
+   m_catalogue.RequesterMountRule()->deleteRequesterMountRule(in, name);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1743,7 +1743,8 @@ void RequestMessage::processActivityMountRule_Add(cta::xrd::Response &response)
    auto &comment       = getRequired(OptionString::COMMENT);
    auto &activityRegex = getRequired(OptionString::ACTIVITY_REGEX);
 
-   m_catalogue.createRequesterActivityMountRule(m_cliIdentity, mountpolicy, in, name, activityRegex, comment);
+   m_catalogue.RequesterActivityMountRule()->createRequesterActivityMountRule(m_cliIdentity, mountpolicy, in, name,
+      activityRegex, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1762,10 +1763,12 @@ void RequestMessage::processActivityMountRule_Ch(cta::xrd::Response &response)
    auto  mountpolicy   = getOptional(OptionString::MOUNT_POLICY);
 
    if(comment) {
-      m_catalogue.modifyRequesterActivityMountRuleComment(m_cliIdentity, in, name, activityRegex, comment.value());
+      m_catalogue.RequesterActivityMountRule()->modifyRequesterActivityMountRuleComment(m_cliIdentity, in, name,
+         activityRegex, comment.value());
    }
    if(mountpolicy) {
-      m_catalogue.modifyRequesterActivityMountRulePolicy(m_cliIdentity, in, name, activityRegex, mountpolicy.value());
+      m_catalogue.RequesterActivityMountRule()->modifyRequesterActivityMountRulePolicy(m_cliIdentity, in, name,
+         activityRegex, mountpolicy.value());
    }
    
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1781,7 +1784,7 @@ void RequestMessage::processActivityMountRule_Rm(cta::xrd::Response &response)
    auto &name = getRequired(OptionString::USERNAME);
    auto &activityRegex = getRequired(OptionString::ACTIVITY_REGEX);
 
-   m_catalogue.deleteRequesterActivityMountRule(in, name, activityRegex);
+   m_catalogue.RequesterActivityMountRule()->deleteRequesterActivityMountRule(in, name, activityRegex);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1824,7 +1827,7 @@ void RequestMessage::processStorageClass_Add(cta::xrd::Response &response)
    storageClass.comment      = getRequired(OptionString::COMMENT);
    storageClass.vo.name      = getRequired(OptionString::VO);
 
-   m_catalogue.createStorageClass(m_cliIdentity, storageClass);
+   m_catalogue.StorageClass()->createStorageClass(m_cliIdentity, storageClass);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1841,13 +1844,13 @@ void RequestMessage::processStorageClass_Ch(cta::xrd::Response &response)
    auto vo       = getOptional(OptionString::VO);
 
    if(comment) {
-      m_catalogue.modifyStorageClassComment(m_cliIdentity, scn, comment.value());
+      m_catalogue.StorageClass()->modifyStorageClassComment(m_cliIdentity, scn, comment.value());
    }
    if(cn) {
-      m_catalogue.modifyStorageClassNbCopies(m_cliIdentity, scn, cn.value());
+      m_catalogue.StorageClass()->modifyStorageClassNbCopies(m_cliIdentity, scn, cn.value());
    }
    if(vo){
-     m_catalogue.modifyStorageClassVo(m_cliIdentity,scn,vo.value());
+     m_catalogue.StorageClass()->modifyStorageClassVo(m_cliIdentity,scn,vo.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1861,7 +1864,7 @@ void RequestMessage::processStorageClass_Rm(cta::xrd::Response &response)
 
    auto &scn = getRequired(OptionString::STORAGE_CLASS);
 
-   m_catalogue.deleteStorageClass(scn);
+   m_catalogue.StorageClass()->deleteStorageClass(scn);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1913,7 +1916,7 @@ void RequestMessage::processTape_Add(cta::xrd::Response &response)
      tape.state = common::dataStructures::Tape::stringToState(state.value(), true);
    }
    tape.stateReason = stateReason;
-   m_catalogue.createTape(m_cliIdentity, tape);
+   m_catalogue.Tape()->createTape(m_cliIdentity, tape);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -1938,43 +1941,43 @@ void RequestMessage::processTape_Ch(cta::xrd::Response &response)
    auto  verificationStatus = getOptional(OptionString::VERIFICATION_STATUS);
 
    if(mediaType) {
-      if (m_catalogue.getNbFilesOnTape(vid) != 0) {
+      if (m_catalogue.Tape()->getNbFilesOnTape(vid) != 0) {
          response.set_type(cta::xrd::Response::RSP_ERR_CTA);
          return;
       }
-      m_catalogue.modifyTapeMediaType(m_cliIdentity, vid, mediaType.value());
+      m_catalogue.Tape()->modifyTapeMediaType(m_cliIdentity, vid, mediaType.value());
    }
    if(vendor) {
-      m_catalogue.modifyTapeVendor(m_cliIdentity, vid, vendor.value());
+      m_catalogue.Tape()->modifyTapeVendor(m_cliIdentity, vid, vendor.value());
    }
    if(logicallibrary) {
-      m_catalogue.modifyTapeLogicalLibraryName(m_cliIdentity, vid, logicallibrary.value());
+      m_catalogue.Tape()->modifyTapeLogicalLibraryName(m_cliIdentity, vid, logicallibrary.value());
    }
    if(tapepool) {
-      m_catalogue.modifyTapeTapePoolName(m_cliIdentity, vid, tapepool.value());
+      m_catalogue.Tape()->modifyTapeTapePoolName(m_cliIdentity, vid, tapepool.value());
    }
    if(comment) {
       if(comment.value().empty()){
         //If the comment is an empty string, the user meant to delete it
         comment = std::nullopt;
       }
-      m_catalogue.modifyTapeComment(m_cliIdentity, vid, comment);
+      m_catalogue.Tape()->modifyTapeComment(m_cliIdentity, vid, comment);
    }
    if(encryptionkeyName) {
-      m_catalogue.modifyTapeEncryptionKeyName(m_cliIdentity, vid, encryptionkeyName.value());
+      m_catalogue.Tape()->modifyTapeEncryptionKeyName(m_cliIdentity, vid, encryptionkeyName.value());
    }
    if(full) {
-      m_catalogue.setTapeFull(m_cliIdentity, vid, full.value());
+      m_catalogue.Tape()->setTapeFull(m_cliIdentity, vid, full.value());
    }
    if(state){
      auto stateEnumValue = common::dataStructures::Tape::stringToState(state.value(), true);
      m_scheduler.triggerTapeStateChange(m_cliIdentity,vid,stateEnumValue,stateReason, m_lc);
    }
    if (dirty) {
-      m_catalogue.setTapeDirty(m_cliIdentity, vid, dirty.value());
+      m_catalogue.Tape()->setTapeDirty(m_cliIdentity, vid, dirty.value());
    }
    if (verificationStatus) {
-      m_catalogue.modifyTapeVerificationStatus(m_cliIdentity, vid, verificationStatus.value());
+      m_catalogue.Tape()->modifyTapeVerificationStatus(m_cliIdentity, vid, verificationStatus.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -1991,7 +1994,7 @@ void RequestMessage::processTape_Rm(cta::xrd::Response &response)
    if (m_scheduler.isBeingRepacked(vid)) {
      throw cta::exception::UserError("Cannot delete tape " + vid + " because there is a repack for that tape");
    }  
-   m_catalogue.deleteTape(vid);
+   m_catalogue.Tape()->deleteTape(vid);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2004,7 +2007,7 @@ void RequestMessage::processTape_Reclaim(cta::xrd::Response &response)
 
    auto &vid = getRequired(OptionString::VID);
 
-   m_catalogue.reclaimTape(m_cliIdentity, vid, m_lc);
+   m_catalogue.Tape()->reclaimTape(m_cliIdentity, vid, m_lc);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2068,12 +2071,12 @@ void RequestMessage::processTapeFile_Rm(cta::xrd::Response &response)
     searchCriteria.diskInstance = instance.value();
   }
   
-  auto archiveFile = m_catalogue.getArchiveFileForDeletion(searchCriteria);
+  auto archiveFile = m_catalogue.ArchiveFile()->getArchiveFileForDeletion(searchCriteria);
   grpc::EndpointMap endpoints(m_namespaceMap);
   auto diskFilePath = endpoints.getPath(archiveFile.diskInstance, archiveFile.diskFileId);
   archiveFile.diskFileInfo.path = diskFilePath;
 
-  m_catalogue.deleteTapeFileCopy(archiveFile, reason);
+  m_catalogue.TapeFile()->deleteTapeFileCopy(archiveFile, reason);
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
@@ -2089,7 +2092,7 @@ void RequestMessage::processTapePool_Add(cta::xrd::Response &response)
    auto &encrypted = getRequired(OptionBoolean::ENCRYPTED);
    auto  supply    = getOptional(OptionString::SUPPLY);
 
-   m_catalogue.createTapePool(m_cliIdentity, name, vo, ptn, encrypted, supply, comment);
+   m_catalogue.TapePool()->createTapePool(m_cliIdentity, name, vo, ptn, encrypted, supply, comment);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2108,19 +2111,19 @@ void RequestMessage::processTapePool_Ch(cta::xrd::Response &response)
    auto  supply    = getOptional(OptionString::SUPPLY);
 
    if(comment) {
-      m_catalogue.modifyTapePoolComment(m_cliIdentity, name, comment.value());
+      m_catalogue.TapePool()->modifyTapePoolComment(m_cliIdentity, name, comment.value());
    }
    if(vo) {
-      m_catalogue.modifyTapePoolVo(m_cliIdentity, name, vo.value());
+      m_catalogue.TapePool()->modifyTapePoolVo(m_cliIdentity, name, vo.value());
    }
    if(ptn) {
-      m_catalogue.modifyTapePoolNbPartialTapes(m_cliIdentity, name, ptn.value());
+      m_catalogue.TapePool()->modifyTapePoolNbPartialTapes(m_cliIdentity, name, ptn.value());
    }
    if(encrypted) {
-      m_catalogue.setTapePoolEncryption(m_cliIdentity, name, encrypted.value());
+      m_catalogue.TapePool()->setTapePoolEncryption(m_cliIdentity, name, encrypted.value());
    }
    if(supply) {
-      m_catalogue.modifyTapePoolSupply(m_cliIdentity, name, supply.value());
+      m_catalogue.TapePool()->modifyTapePoolSupply(m_cliIdentity, name, supply.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -2134,7 +2137,7 @@ void RequestMessage::processTapePool_Rm(cta::xrd::Response &response)
 
    auto &name = getRequired(OptionString::TAPE_POOL);
 
-   m_catalogue.deleteTapePool(name);
+   m_catalogue.TapePool()->deleteTapePool(name);
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2177,7 +2180,8 @@ void RequestMessage::processDiskSystem_Add(cta::xrd::Response &response)
   const auto &sleepTime         = getRequired(OptionUInt64::SLEEP_TIME);
   const auto &comment           = getRequired(OptionString::COMMENT);
 
-  m_catalogue.createDiskSystem(m_cliIdentity, name,diskInstance, diskInstanceSpace, fileRegexp, targetedFreeSpace, sleepTime, comment);
+  m_catalogue.DiskSystem()->createDiskSystem(m_cliIdentity, name,diskInstance, diskInstanceSpace, fileRegexp,
+   targetedFreeSpace, sleepTime, comment);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2195,22 +2199,22 @@ void RequestMessage::processDiskSystem_Ch(cta::xrd::Response &response)
    const auto  diskInstanceSpaceName = getOptional(OptionString::DISK_INSTANCE_SPACE);
 
    if(comment) {
-      m_catalogue.modifyDiskSystemComment(m_cliIdentity, name, comment.value());
+      m_catalogue.DiskSystem()->modifyDiskSystemComment(m_cliIdentity, name, comment.value());
    }
    if(fileRegexp) {
-      m_catalogue.modifyDiskSystemFileRegexp(m_cliIdentity, name, fileRegexp.value());
+      m_catalogue.DiskSystem()->modifyDiskSystemFileRegexp(m_cliIdentity, name, fileRegexp.value());
    }
    if (sleepTime) {
-     m_catalogue.modifyDiskSystemSleepTime(m_cliIdentity, name, sleepTime.value());
+     m_catalogue.DiskSystem()->modifyDiskSystemSleepTime(m_cliIdentity, name, sleepTime.value());
    }
    if(targetedFreeSpace) {
-      m_catalogue.modifyDiskSystemTargetedFreeSpace(m_cliIdentity, name, targetedFreeSpace.value());
+      m_catalogue.DiskSystem()->modifyDiskSystemTargetedFreeSpace(m_cliIdentity, name, targetedFreeSpace.value());
    }
    if (diskInstanceName) {
-      m_catalogue.modifyDiskSystemDiskInstanceName(m_cliIdentity, name, diskInstanceName.value());
+      m_catalogue.DiskSystem()->modifyDiskSystemDiskInstanceName(m_cliIdentity, name, diskInstanceName.value());
    }
    if (diskInstanceSpaceName) {
-      m_catalogue.modifyDiskSystemDiskInstanceSpaceName(m_cliIdentity, name, diskInstanceSpaceName.value());
+      m_catalogue.DiskSystem()->modifyDiskSystemDiskInstanceSpaceName(m_cliIdentity, name, diskInstanceSpaceName.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -2222,7 +2226,7 @@ void RequestMessage::processDiskSystem_Rm(cta::xrd::Response &response)
 
   const auto &name = getRequired(OptionString::DISK_SYSTEM);
 
-  m_catalogue.deleteDiskSystem(name);
+  m_catalogue.DiskSystem()->deleteDiskSystem(name);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2245,7 +2249,7 @@ void RequestMessage::processDiskInstance_Add(cta::xrd::Response &response)
   const auto &name              = getRequired(OptionString::DISK_INSTANCE);
   const auto &comment           = getRequired(OptionString::COMMENT);
 
-  m_catalogue.createDiskInstance(m_cliIdentity, name, comment);
+  m_catalogue.DiskInstance()->createDiskInstance(m_cliIdentity, name, comment);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2258,7 +2262,7 @@ void RequestMessage::processDiskInstance_Ch(cta::xrd::Response &response)
    const auto comment            = getOptional(OptionString::COMMENT);
 
    if(comment) {
-      m_catalogue.modifyDiskInstanceComment(m_cliIdentity, name, comment.value());
+      m_catalogue.DiskInstance()->modifyDiskInstanceComment(m_cliIdentity, name, comment.value());
    }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -2270,7 +2274,7 @@ void RequestMessage::processDiskInstance_Rm(cta::xrd::Response &response)
 
   const auto &name = getRequired(OptionString::DISK_INSTANCE);
 
-  m_catalogue.deleteDiskInstance(name);
+  m_catalogue.DiskInstance()->deleteDiskInstance(name);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2296,7 +2300,8 @@ void RequestMessage::processDiskInstanceSpace_Add(cta::xrd::Response &response)
   const auto &freeSpaceQueryURL = getRequired(OptionString::FREE_SPACE_QUERY_URL);
   const auto refreshInterval    = getRequired(OptionUInt64::REFRESH_INTERVAL);
    
-  m_catalogue.createDiskInstanceSpace(m_cliIdentity, name, diskInstance, freeSpaceQueryURL, refreshInterval, comment);
+  m_catalogue.DiskInstanceSpace()->createDiskInstanceSpace(m_cliIdentity, name, diskInstance, freeSpaceQueryURL,
+   refreshInterval, comment);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2312,13 +2317,16 @@ void RequestMessage::processDiskInstanceSpace_Ch(cta::xrd::Response &response)
    const auto refreshInterval    = getOptional(OptionUInt64::REFRESH_INTERVAL);
    
    if(comment) {
-      m_catalogue.modifyDiskInstanceSpaceComment(m_cliIdentity, name, diskInstance, comment.value());
+      m_catalogue.DiskInstanceSpace()->modifyDiskInstanceSpaceComment(m_cliIdentity, name, diskInstance,
+         comment.value());
    }
    if(freeSpaceQueryURL) {
-      m_catalogue.modifyDiskInstanceSpaceQueryURL(m_cliIdentity, name, diskInstance, freeSpaceQueryURL.value());
+      m_catalogue.DiskInstanceSpace()->modifyDiskInstanceSpaceQueryURL(m_cliIdentity, name, diskInstance,
+         freeSpaceQueryURL.value());
    }
    if(refreshInterval) {
-      m_catalogue.modifyDiskInstanceSpaceRefreshInterval(m_cliIdentity, name, diskInstance, refreshInterval.value());
+      m_catalogue.DiskInstanceSpace()->modifyDiskInstanceSpaceRefreshInterval(m_cliIdentity, name, diskInstance,
+         refreshInterval.value());
    }
    
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
@@ -2331,7 +2339,7 @@ void RequestMessage::processDiskInstanceSpace_Rm(cta::xrd::Response &response)
   const auto &name         = getRequired(OptionString::DISK_INSTANCE_SPACE);
   const auto &diskInstance = getRequired(OptionString::DISK_INSTANCE);
 
-  m_catalogue.deleteDiskInstanceSpace(name, diskInstance);
+  m_catalogue.DiskInstanceSpace()->deleteDiskInstanceSpace(name, diskInstance);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2359,7 +2367,7 @@ void RequestMessage::processVirtualOrganization_Add(cta::xrd::Response &response
     vo.maxFileSize = m_archiveFileMaxSize;
   }
 
-  m_catalogue.createVirtualOrganization(m_cliIdentity,vo);
+  m_catalogue.VO()->createVirtualOrganization(m_cliIdentity,vo);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2375,19 +2383,19 @@ void RequestMessage::processVirtualOrganization_Ch(cta::xrd::Response &response)
   const auto diskInstanceName = getOptional(OptionString::DISK_INSTANCE);
 
   if(comment)
-    m_catalogue.modifyVirtualOrganizationComment(m_cliIdentity,name,comment.value());
+    m_catalogue.VO()->modifyVirtualOrganizationComment(m_cliIdentity,name,comment.value());
 
   if(readMaxDrives)
-    m_catalogue.modifyVirtualOrganizationReadMaxDrives(m_cliIdentity,name,readMaxDrives.value());
+    m_catalogue.VO()->modifyVirtualOrganizationReadMaxDrives(m_cliIdentity,name,readMaxDrives.value());
 
   if(writeMaxDrives)
-    m_catalogue.modifyVirtualOrganizationWriteMaxDrives(m_cliIdentity,name,writeMaxDrives.value());
+    m_catalogue.VO()->modifyVirtualOrganizationWriteMaxDrives(m_cliIdentity,name,writeMaxDrives.value());
 
   if(maxFileSize)
-    m_catalogue.modifyVirtualOrganizationMaxFileSize(m_cliIdentity,name,maxFileSize.value());
+    m_catalogue.VO()->modifyVirtualOrganizationMaxFileSize(m_cliIdentity,name,maxFileSize.value());
 
-  if(diskInstanceName) 
-    m_catalogue.modifyVirtualOrganizationDiskInstanceName(m_cliIdentity, name, diskInstanceName.value());
+  if(diskInstanceName)
+    m_catalogue.VO()->modifyVirtualOrganizationDiskInstanceName(m_cliIdentity, name, diskInstanceName.value());
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2397,7 +2405,7 @@ void RequestMessage::processVirtualOrganization_Rm(cta::xrd::Response& response)
 
   const auto &name = getRequired(OptionString::VO);
 
-  m_catalogue.deleteVirtualOrganization(name);
+  m_catalogue.VO()->deleteVirtualOrganization(name);
 
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
@@ -2421,7 +2429,7 @@ std::string RequestMessage::setDriveState(const std::string &regex, const cta::c
 
   cta::utils::Regex driveNameRegex(regex.c_str());
 
-  const auto tapeDriveNames = m_catalogue.getTapeDriveNames();
+  const auto tapeDriveNames = m_catalogue.DriveState()->getTapeDriveNames();
   bool is_found = false;
 
   for(auto tapeDriveName: tapeDriveNames)
@@ -2523,7 +2531,7 @@ void RequestMessage::processRecycleTapeFile_Restore(cta::xrd::Response& response
   if(!has_any){
     throw cta::exception::UserError("Must specify at least one of the following search options: vid, fxid, fxidfile or archiveFileId");
   }
-  m_catalogue.restoreFileInRecycleLog(searchCriteria, std::to_string(fid));
+  m_catalogue.FileRecycleLog()->restoreFileInRecycleLog(searchCriteria, std::to_string(fid));
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
@@ -2545,7 +2553,8 @@ void RequestMessage::processModifyArchiveFile(cta::xrd::Response& response) {
       }
       // call is from cta-eos-namespace-inject
       if(fxId && diskInstance) {
-         m_catalogue.modifyArchiveFileFxIdAndDiskInstance(cta::utils::toUint64(archiveFileIds[0]), fxId.value(), diskInstance.value());
+         m_catalogue.ArchiveFile()->modifyArchiveFileFxIdAndDiskInstance(cta::utils::toUint64(archiveFileIds[0]),
+            fxId.value(), diskInstance.value());
       }
       response.set_type(cta::xrd::Response::RSP_SUCCESS);
    } catch(exception::UserError &ue) {
-- 
GitLab