From 5a82646b184960428257c35f8680ae61dcd76afb Mon Sep 17 00:00:00 2001
From: Steven Murray <steven.murray@cern.ch>
Date: Mon, 13 Jun 2016 11:35:33 +0200
Subject: [PATCH] Added delete functionality to the Catalogue

---
 catalogue/CMakeLists.txt                      |   6 +-
 catalogue/Catalogue.hpp                       |  47 +-
 catalogue/DbStmt.hpp                          |   8 +
 catalogue/InMemoryCatalogueTest.cpp           | 999 ++++++++++++++++--
 catalogue/OcciStmt.cpp                        |   7 +
 catalogue/OcciStmt.hpp                        |   8 +
 catalogue/OracleCatalogue.cpp                 |   7 +
 catalogue/OracleCatalogue.hpp                 |  10 +
 catalogue/RdbmsCatalogue.cpp                  | 450 ++++++--
 catalogue/RdbmsCatalogue.hpp                  |  81 +-
 catalogue/RequesterAndGroupMountPolicies.hpp  |  47 +
 catalogue/SqliteCatalogue.cpp                 | 107 +-
 catalogue/SqliteCatalogue.hpp                 |  10 +
 catalogue/SqliteConn.cpp                      |  17 +-
 catalogue/SqliteConn.hpp                      |   6 +
 catalogue/SqliteStmt.cpp                      |  17 +-
 catalogue/SqliteStmt.hpp                      |  23 +-
 catalogue/sqlite_catalogue_schema_header.sql  |   1 -
 catalogue/sqlite_catalogue_schema_trailer.sql |   2 +
 scheduler/SchedulerTest.cpp                   |   4 +-
 xroot_plugins/XrdCtaFile.cpp                  |   4 +-
 21 files changed, 1644 insertions(+), 217 deletions(-)
 create mode 100644 catalogue/RequesterAndGroupMountPolicies.hpp
 create mode 100644 catalogue/sqlite_catalogue_schema_trailer.sql

diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt
index 7a33b8136b..4b1a8caacb 100644
--- a/catalogue/CMakeLists.txt
+++ b/catalogue/CMakeLists.txt
@@ -61,9 +61,9 @@ target_link_libraries (ctacatalogue
   ${SQLITE_LIBRARIES})
 
 add_custom_command(OUTPUT sqlite_catalogue_schema.sql oracle_catalogue_schema.sql
-  COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql ${CMAKE_CURRENT_SOURCE_DIR}/catalogue_common_schema.sql > sqlite_catalogue_schema.sql
+  COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql ${CMAKE_CURRENT_SOURCE_DIR}/catalogue_common_schema.sql ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_trailer.sql > sqlite_catalogue_schema.sql
   COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/catalogue_common_schema.sql > oracle_catalogue_schema.sql
-  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql catalogue_common_schema.sql)
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql catalogue_common_schema.sql ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_trailer.sql)
 
 install (FILES ${CMAKE_CURRENT_BINARY_DIR}/sqlite_catalogue_schema.sql
   DESTINATION usr/share/cta-${CTA_VERSION}/sql
@@ -74,7 +74,7 @@ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/oracle_catalogue_schema.sql
   PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
 
 add_custom_command(OUTPUT in_memory_catalogue_schema.cpp
-  COMMAND sed 's/^/\ \ \ \ \"/' sqlite_catalogue_schema.sql | sed 's/$$/\"/' > in_memory_catalogue_schema.cpp
+  COMMAND sed 's/^/\ \ \"/' sqlite_catalogue_schema.sql | sed 's/$$/\"/' > in_memory_catalogue_schema.cpp
   DEPENDS sqlite_catalogue_schema.sql)
 
 add_custom_command(OUTPUT InMemoryCatalogueSchema.cpp
diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index f2d354f9ba..e99fdc86fb 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -138,11 +138,11 @@ public:
 
   virtual void createRequester(
     const common::dataStructures::SecurityIdentity &cliIdentity,
-    const common::dataStructures::UserIdentity &user,
+    const std::string &requesterName,
     const std::string &mountPolicy,
     const std::string &comment) = 0;
 
-  virtual void deleteRequester(const common::dataStructures::UserIdentity &user) = 0;
+  virtual void deleteRequester(const std::string &requesterName) = 0;
   virtual std::list<common::dataStructures::Requester> getRequesters() const = 0;
   virtual void modifyRequesterMountPolicy(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &mountPolicy) = 0;
   virtual void modifyRequesterComment(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &comment) = 0;
@@ -157,6 +157,35 @@ public:
     const uint64_t maxDrivesAllowed, 
     const std::string &comment) = 0;
 
+  /**
+   * Assigns the specified mount policy to the specified requester.
+   *
+   * Please note that requester mount-policies overrule requester-group
+   * mount-policies.
+   *
+   * @param cliIdentity The user of the command-line tool.
+   * @param mountPolicyName The name of the mount policy.
+   * @param requesterName The name of the requester.
+   * @param comment Comment.
+   */
+  virtual void assignMountPolicyToRequester(
+    const common::dataStructures::SecurityIdentity &cliIdentity,
+    const std::string &mountPolicyName,
+    const std::string &requesterName,
+    const std::string &comment) = 0;
+
+  /**
+   * Assigns the specified mount policy to the specified requester group.
+   *
+   * Please note that requester mount-policies overrule requester-group
+   * mount-policies.
+   *
+   * @param mountPolicyName The name of the mount policy.
+   * @param requesterGrouprName The name of the requester group.
+   */
+  virtual void assignMountPolicyToRequesterGroup(const std::string &mountPolicyName,
+    const std::string &requesterGroupName) = 0;
+
   virtual void deleteMountPolicy(const std::string &name) = 0;
   virtual std::list<common::dataStructures::MountPolicy> getMountPolicies() const = 0;
   virtual void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &name, const uint64_t archivePriority) = 0;
@@ -222,6 +251,16 @@ public:
    */
   virtual void fileWrittenToTape(const TapeFileWritten &event) = 0;
 
+  /**
+   * Deletes the specified archive file and its associated tape copies from the
+   * catalogue.
+   *
+   * @param archiveFileId The unique identifier of the archive file.
+   * @return The metadata of the deleted archive file including the metadata of
+   * the associated and also deleted tape copies.
+   */
+  virtual common::dataStructures::ArchiveFile deleteArchiveFile(const uint64_t archiveFileId) = 0;
+
   /**
    * Prepares for a file retrieval by returning the information required to
    * queue the associated retrieve request(s).
@@ -243,10 +282,10 @@ public:
   /**
    * Returns the mount policy for the specified end user.
    *
-   * @param user The name of the end user.
+   * @param username The name of the end user.
    * @return The mount policy.
    */
-  virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const common::dataStructures::UserIdentity &user) const = 0;
+  virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const std::string &username) const = 0;
 
   virtual bool isAdmin(const common::dataStructures::SecurityIdentity &cliIdentity) const = 0;
 
diff --git a/catalogue/DbStmt.hpp b/catalogue/DbStmt.hpp
index 1ab5b09af0..039cbe17fe 100644
--- a/catalogue/DbStmt.hpp
+++ b/catalogue/DbStmt.hpp
@@ -78,6 +78,14 @@ public:
    */
   virtual void executeNonQuery() = 0;
 
+  /**
+   * Returns the number of rows affected by the last execution of this
+   * statement.
+   *
+   * @return The number of affected rows.
+   */
+  virtual uint64_t getNbAffectedRows() const = 0;
+
 }; // class DbStmt
 
 } // namespace catalogue
diff --git a/catalogue/InMemoryCatalogueTest.cpp b/catalogue/InMemoryCatalogueTest.cpp
index 353f67a033..c34ca7ede7 100644
--- a/catalogue/InMemoryCatalogueTest.cpp
+++ b/catalogue/InMemoryCatalogueTest.cpp
@@ -47,10 +47,73 @@ public:
 protected:
 
   virtual void SetUp() {
+    using namespace cta;
     using namespace cta::catalogue;
 
     const DbLogin catalogueLogin(DbLogin::DBTYPE_IN_MEMORY, "", "", "");
     m_catalogue.reset(dynamic_cast<RdbmsCatalogue*>(CatalogueFactory::create(catalogueLogin)));
+
+    {
+      const std::list<common::dataStructures::AdminUser> adminUsers = m_catalogue->getAdminUsers();
+      for(auto &adminUser: adminUsers) {
+        m_catalogue->deleteAdminUser(adminUser.name);
+      }
+    }
+    {
+      const std::list<common::dataStructures::AdminHost> adminHosts = m_catalogue->getAdminHosts();
+      for(auto &adminHost: adminHosts) {
+        m_catalogue->deleteAdminHost(adminHost.name);
+      }
+    }
+    {
+      const std::list<common::dataStructures::ArchiveRoute> archiveRoutes = m_catalogue->getArchiveRoutes();
+      for(auto &archiveRoute: archiveRoutes) {
+        m_catalogue->deleteArchiveRoute(archiveRoute.storageClassName, archiveRoute.copyNb);
+      }
+    }
+    {
+      const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters();
+      for(auto &requester: requesters) {
+        m_catalogue->deleteRequester(requester.name);
+      }
+    }
+    {
+      const std::list<common::dataStructures::ArchiveFile> archiveFiles =
+        m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "");
+      for(auto &archiveFile: archiveFiles) {
+        m_catalogue->deleteArchiveFile(archiveFile.archiveFileID);
+      }
+    }
+    {
+      const std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes("", "", "", "", "", "", "", "");
+      for(auto &tape: tapes) {
+        m_catalogue->deleteTape(tape.vid);
+      }
+    }
+    {
+      const std::list<common::dataStructures::StorageClass> storageClasses = m_catalogue->getStorageClasses();
+      for(auto &storageClass: storageClasses) {
+        m_catalogue->deleteStorageClass(storageClass.name);
+      }
+    }
+    {
+      const std::list<common::dataStructures::TapePool> tapePools = m_catalogue->getTapePools();
+      for(auto &tapePool: tapePools) {
+        m_catalogue->deleteTapePool(tapePool.name);
+      }
+    }
+    {
+      const std::list<common::dataStructures::LogicalLibrary> logicalLibraries = m_catalogue->getLogicalLibraries();
+      for(auto &logicalLibrary: logicalLibraries) {
+        m_catalogue->deleteLogicalLibrary(logicalLibrary.name);
+      }
+    }
+    {
+      const std::list<common::dataStructures::MountPolicy> mountPolicies = m_catalogue->getMountPolicies();
+      for(auto &mountPolicy: mountPolicies) {
+        m_catalogue->deleteMountPolicy(mountPolicy.name);
+      }
+    }
   }
 
   virtual void TearDown() {
@@ -210,6 +273,8 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminUser) {
 TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminUser_same_twice) {
   using namespace cta;
 
+  ASSERT_TRUE(m_catalogue->getAdminUsers().empty());
+
   m_catalogue->createBootstrapAdminAndHostNoAuth(
     m_cliSI, m_bootstrapAdminSI.username, m_bootstrapAdminSI.host, m_bootstrapComment);
 
@@ -236,6 +301,43 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminUser_same_twice) {
     "comment 2"), catalogue::UserError);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminUser) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getAdminUsers().empty());
+
+  m_catalogue->createBootstrapAdminAndHostNoAuth(
+    m_cliSI, m_bootstrapAdminSI.username, m_bootstrapAdminSI.host, m_bootstrapComment);
+
+  {
+    std::list<common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const common::dataStructures::AdminUser admin = admins.front();
+    ASSERT_EQ(m_bootstrapComment, admin.comment);
+
+    const common::dataStructures::EntryLog creationLog = admin.creationLog;
+    ASSERT_EQ(m_cliSI.username, creationLog.username);
+    ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+    const common::dataStructures::EntryLog lastModificationLog =
+      admin.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->deleteAdminUser(m_bootstrapAdminSI.username);
+
+  ASSERT_TRUE(m_catalogue->getAdminUsers().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminUser_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getAdminUsers().empty());
+  ASSERT_THROW(m_catalogue->deleteAdminUser("non_existant_sdmin_user"), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminHost) {
   using namespace cta;
 
@@ -345,6 +447,43 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminHost_same_twice) {
     anotherAdminHost, "comment 2"), catalogue::UserError);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminHost) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getAdminHosts().empty());
+
+  m_catalogue->createBootstrapAdminAndHostNoAuth(
+    m_cliSI, m_bootstrapAdminSI.username, m_bootstrapAdminSI.host, m_bootstrapComment);
+
+  {
+    std::list<common::dataStructures::AdminUser> admins;
+    admins = m_catalogue->getAdminUsers();
+    ASSERT_EQ(1, admins.size());
+
+    const common::dataStructures::AdminUser admin = admins.front();
+    ASSERT_EQ(m_bootstrapComment, admin.comment);
+
+    const common::dataStructures::EntryLog creationLog = admin.creationLog;
+    ASSERT_EQ(m_cliSI.username, creationLog.username);
+    ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+    const common::dataStructures::EntryLog lastModificationLog =
+      admin.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  m_catalogue->deleteAdminHost(m_bootstrapAdminSI.host);
+
+  ASSERT_TRUE(m_catalogue->getAdminHosts().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminHost_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getAdminHosts().empty());
+  ASSERT_THROW(m_catalogue->deleteAdminHost("non_exstant_admin_host"), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, isAdmin_false) {
   using namespace cta;
 
@@ -401,6 +540,46 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createStorageClass_same_twice) {
     storageClassName, nbCopies, comment), catalogue::UserError);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteStorageClass) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getStorageClasses().empty());
+
+  const std::string storageClassName = "storage_class";
+  const uint64_t nbCopies = 2;
+  const std::string comment = "create storage class";
+  m_catalogue->createStorageClass(m_cliSI, storageClassName, nbCopies, comment);
+
+  const std::list<common::dataStructures::StorageClass> storageClasses =
+    m_catalogue->getStorageClasses();
+
+  ASSERT_EQ(1, storageClasses.size());
+
+  const common::dataStructures::StorageClass storageClass =
+    storageClasses.front();
+  ASSERT_EQ(storageClassName, storageClass.name);
+  ASSERT_EQ(nbCopies, storageClass.nbCopies);
+  ASSERT_EQ(comment, storageClass.comment);
+
+  const common::dataStructures::EntryLog creationLog = storageClass.creationLog;
+  ASSERT_EQ(m_cliSI.username, creationLog.username);
+  ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+  const common::dataStructures::EntryLog lastModificationLog =
+    storageClass.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->deleteStorageClass(storageClass.name);
+  ASSERT_TRUE(m_catalogue->getStorageClasses().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteStorageClass_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getStorageClasses().empty());
+  ASSERT_THROW(m_catalogue->deleteStorageClass("non_existant_storage_class"), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, createTapePool) {
   using namespace cta;
       
@@ -446,6 +625,48 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createTapePool_same_twice) {
     catalogue::UserError);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTapePool) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getTapePools().empty());
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool is_encrypted = true;
+  const std::string comment = "create tape pool";
+  m_catalogue->createTapePool(m_cliSI, tapePoolName, nbPartialTapes, is_encrypted,
+    comment);
+
+  const std::list<common::dataStructures::TapePool> pools =
+    m_catalogue->getTapePools();
+
+  ASSERT_EQ(1, pools.size());
+
+  const common::dataStructures::TapePool pool = pools.front();
+  ASSERT_EQ(tapePoolName, pool.name);
+  ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes);
+  ASSERT_EQ(is_encrypted, pool.encryption);
+  ASSERT_EQ(comment, pool.comment);
+
+  const common::dataStructures::EntryLog creationLog = pool.creationLog;
+  ASSERT_EQ(m_cliSI.username, creationLog.username);
+  ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+  const common::dataStructures::EntryLog lastModificationLog =
+    pool.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->deleteTapePool(pool.name);
+  ASSERT_TRUE(m_catalogue->getTapePools().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTapePool_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getTapePools().empty());
+  ASSERT_THROW(m_catalogue->deleteTapePool("non_existant_tape_pool"), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, createArchiveRoute) {
   using namespace cta;
       
@@ -514,6 +735,63 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createArchiveRouteTapePool_same_twic
     exception::Exception);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveRoute) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getArchiveRoutes().empty());
+
+  const std::string storageClassName = "storage_class";
+  const uint64_t nbCopies = 2;
+  m_catalogue->createStorageClass(m_cliSI, storageClassName, nbCopies, "create storage class");
+
+  const std::string tapePoolName = "tape_pool";
+  const uint64_t nbPartialTapes = 2;
+  const bool is_encrypted = true;
+  m_catalogue->createTapePool(m_cliSI, tapePoolName, nbPartialTapes, is_encrypted, "create tape pool");
+
+  const uint64_t copyNb = 1;
+  const std::string comment = "create archive route";
+  m_catalogue->createArchiveRoute(m_cliSI, storageClassName, copyNb, tapePoolName,
+    comment);
+
+  const std::list<common::dataStructures::ArchiveRoute> routes =
+    m_catalogue->getArchiveRoutes();
+
+  ASSERT_EQ(1, routes.size());
+
+  const common::dataStructures::ArchiveRoute route = routes.front();
+  ASSERT_EQ(storageClassName, route.storageClassName);
+  ASSERT_EQ(copyNb, route.copyNb);
+  ASSERT_EQ(tapePoolName, route.tapePoolName);
+  ASSERT_EQ(comment, route.comment);
+
+  const common::dataStructures::EntryLog creationLog = route.creationLog;
+  ASSERT_EQ(m_cliSI.username, creationLog.username);
+  ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+  const common::dataStructures::EntryLog lastModificationLog =
+    route.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  common::dataStructures::TapeCopyToPoolMap copyToPoolMap =
+    m_catalogue->getTapeCopyToPoolMap(storageClassName);
+  ASSERT_EQ(1, copyToPoolMap.size());
+  std::pair<uint64_t, std::string> maplet = *(copyToPoolMap.begin());
+  ASSERT_EQ(copyNb, maplet.first);
+  ASSERT_EQ(tapePoolName, maplet.second);
+
+  m_catalogue->deleteArchiveRoute(storageClassName, copyNb);
+
+  ASSERT_TRUE(m_catalogue->getArchiveRoutes().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveRoute_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getArchiveRoutes().empty());
+  ASSERT_THROW(m_catalogue->deleteArchiveRoute("non_existant_storage_class", 1234), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, createArchiveRoute_deleteStorageClass) {
   using namespace cta;
 
@@ -598,6 +876,43 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createLogicalLibrary_same_twice) {
   ASSERT_THROW(m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName, comment), catalogue::UserError);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteLogicalLibrary) {
+  using namespace cta;
+      
+  ASSERT_TRUE(m_catalogue->getLogicalLibraries().empty());
+      
+  const std::string logicalLibraryName = "logical_library";
+  const std::string comment = "create logical library";
+  m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName, comment);
+      
+  const std::list<common::dataStructures::LogicalLibrary> libs =
+    m_catalogue->getLogicalLibraries();
+      
+  ASSERT_EQ(1, libs.size());
+      
+  const common::dataStructures::LogicalLibrary lib = libs.front();
+  ASSERT_EQ(logicalLibraryName, lib.name);
+  ASSERT_EQ(comment, lib.comment);
+
+  const common::dataStructures::EntryLog creationLog = lib.creationLog;
+  ASSERT_EQ(m_cliSI.username, creationLog.username);
+  ASSERT_EQ(m_cliSI.host, creationLog.host);
+  
+  const common::dataStructures::EntryLog lastModificationLog =
+    lib.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->deleteLogicalLibrary(logicalLibraryName);
+  ASSERT_TRUE(m_catalogue->getLogicalLibraries().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteLogicalLibrary_non_existant) {
+  using namespace cta;
+      
+  ASSERT_TRUE(m_catalogue->getLogicalLibraries().empty());
+  ASSERT_THROW(m_catalogue->deleteLogicalLibrary("non_existant_logical_library"), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, createTape) {
   using namespace cta;
 
@@ -665,6 +980,61 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createTape_same_twice) {
     comment), catalogue::UserError);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTape) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getTapes("", "", "", "", "", "", "", "").empty());
+
+  const std::string vid = "vid";
+  const std::string logicalLibraryName = "logical_library_name";
+  const std::string tapePoolName = "tape_pool_name";
+  const std::string encryptionKey = "encryption_key";
+  const uint64_t capacityInBytes = (uint64_t)10 * 1000 * 1000 * 1000 * 1000;
+  const bool disabledValue = true;
+  const bool fullValue = false;
+  const std::string comment = "create tape";
+
+  m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName,
+    "create logical library");
+  m_catalogue->createTapePool(m_cliSI, tapePoolName, 2, true, "create tape pool");
+  m_catalogue->createTape(m_cliSI, vid, logicalLibraryName, tapePoolName,
+    encryptionKey, capacityInBytes, disabledValue, fullValue,
+    comment);
+
+  const std::list<common::dataStructures::Tape> tapes =
+    m_catalogue->getTapes("", "", "", "", "", "", "", "");
+
+  ASSERT_EQ(1, tapes.size());
+
+  const common::dataStructures::Tape tape = tapes.front();
+  ASSERT_EQ(vid, tape.vid);
+  ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName);
+  ASSERT_EQ(tapePoolName, tape.tapePoolName);
+  ASSERT_EQ(encryptionKey, tape.encryptionKey);
+  ASSERT_EQ(capacityInBytes, tape.capacityInBytes);
+  ASSERT_TRUE(disabledValue == tape.disabled);
+  ASSERT_TRUE(fullValue == tape.full);
+  ASSERT_EQ(comment, tape.comment);
+
+  const common::dataStructures::EntryLog creationLog = tape.creationLog;
+  ASSERT_EQ(m_cliSI.username, creationLog.username);
+  ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+  const common::dataStructures::EntryLog lastModificationLog =
+    tape.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->deleteTape(tape.vid);
+  ASSERT_TRUE(m_catalogue->getTapes("", "", "", "", "", "", "", "").empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTape_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getTapes("", "", "", "", "", "", "", "").empty());
+  ASSERT_THROW(m_catalogue->deleteTape("non_exsitant_tape"), catalogue::UserError);
+}
+
 TEST_F(cta_catalogue_InMemoryCatalogueTest, getTapesForWriting) {
   using namespace cta;
 
@@ -703,13 +1073,13 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy) {
 
   ASSERT_TRUE(m_catalogue->getMountPolicies().empty());
 
-  const std::string name = "mount_group";
+  const std::string name = "mount_policy";
   const uint64_t archivePriority = 1;
   const uint64_t minArchiveRequestAge = 2;
   const uint64_t retrievePriority = 3;
   const uint64_t minRetrieveRequestAge = 4;
   const uint64_t maxDrivesAllowed = 5;
-  const std::string &comment = "create mount group";
+  const std::string &comment = "create mount policy";
 
   m_catalogue->createMountPolicy(
     m_cliSI,
@@ -721,31 +1091,31 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy) {
     maxDrivesAllowed,
     comment);
 
-  const std::list<common::dataStructures::MountPolicy> groups =
+  const std::list<common::dataStructures::MountPolicy> mountPolicies =
     m_catalogue->getMountPolicies();
 
-  ASSERT_EQ(1, groups.size());
+  ASSERT_EQ(1, mountPolicies.size());
 
-  const common::dataStructures::MountPolicy group = groups.front();
+  const common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
 
-  ASSERT_EQ(name, group.name);
+  ASSERT_EQ(name, mountPolicy.name);
 
-  ASSERT_EQ(archivePriority, group.archivePriority);
-  ASSERT_EQ(minArchiveRequestAge, group.archiveMinRequestAge);
+  ASSERT_EQ(archivePriority, mountPolicy.archivePriority);
+  ASSERT_EQ(minArchiveRequestAge, mountPolicy.archiveMinRequestAge);
 
-  ASSERT_EQ(retrievePriority, group.retrievePriority);
-  ASSERT_EQ(minRetrieveRequestAge, group.retrieveMinRequestAge);
+  ASSERT_EQ(retrievePriority, mountPolicy.retrievePriority);
+  ASSERT_EQ(minRetrieveRequestAge, mountPolicy.retrieveMinRequestAge);
 
-  ASSERT_EQ(maxDrivesAllowed, group.maxDrivesAllowed);
+  ASSERT_EQ(maxDrivesAllowed, mountPolicy.maxDrivesAllowed);
 
-  ASSERT_EQ(comment, group.comment);
+  ASSERT_EQ(comment, mountPolicy.comment);
 
-  const common::dataStructures::EntryLog creationLog = group.creationLog;
+  const common::dataStructures::EntryLog creationLog = mountPolicy.creationLog;
   ASSERT_EQ(m_cliSI.username, creationLog.username);
   ASSERT_EQ(m_cliSI.host, creationLog.host);
 
   const common::dataStructures::EntryLog lastModificationLog =
-    group.lastModificationLog;
+    mountPolicy.lastModificationLog;
   ASSERT_EQ(creationLog, lastModificationLog);
 }
 
@@ -754,13 +1124,13 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy_same_twice) {
 
   ASSERT_TRUE(m_catalogue->getMountPolicies().empty());
 
-  const std::string name = "mount_group";
+  const std::string name = "mount_policy";
   const uint64_t archivePriority = 1;
   const uint64_t minArchiveRequestAge = 4;
   const uint64_t retrievePriority = 5;
   const uint64_t minRetrieveRequestAge = 8;
   const uint64_t maxDrivesAllowed = 9;
-  const std::string &comment = "create mount group";
+  const std::string &comment = "create mount policy";
 
   m_catalogue->createMountPolicy(
     m_cliSI,
@@ -772,23 +1142,218 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy_same_twice) {
     maxDrivesAllowed,
     comment);
 
-  ASSERT_THROW(m_catalogue->createMountPolicy(
-    m_cliSI,
-    name,
-    archivePriority,
-    minArchiveRequestAge,
-    retrievePriority,
-    minRetrieveRequestAge,
-    maxDrivesAllowed,
-    comment), catalogue::UserError);
+  ASSERT_THROW(m_catalogue->createMountPolicy(
+    m_cliSI,
+    name,
+    archivePriority,
+    minArchiveRequestAge,
+    retrievePriority,
+    minRetrieveRequestAge,
+    maxDrivesAllowed,
+    comment), catalogue::UserError);
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteMountPolicy) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getMountPolicies().empty());
+
+  const std::string name = "mount_policy";
+  const uint64_t archivePriority = 1;
+  const uint64_t minArchiveRequestAge = 2;
+  const uint64_t retrievePriority = 3;
+  const uint64_t minRetrieveRequestAge = 4;
+  const uint64_t maxDrivesAllowed = 5;
+  const std::string &comment = "create mount policy";
+
+  m_catalogue->createMountPolicy(
+    m_cliSI,
+    name,
+    archivePriority,
+    minArchiveRequestAge,
+    retrievePriority,
+    minRetrieveRequestAge,
+    maxDrivesAllowed,
+    comment);
+
+  const std::list<common::dataStructures::MountPolicy> mountPolicies =
+    m_catalogue->getMountPolicies();
+
+  ASSERT_EQ(1, mountPolicies.size());
+
+  const common::dataStructures::MountPolicy mountPolicy = mountPolicies.front();
+
+  ASSERT_EQ(name, mountPolicy.name);
+
+  ASSERT_EQ(archivePriority, mountPolicy.archivePriority);
+  ASSERT_EQ(minArchiveRequestAge, mountPolicy.archiveMinRequestAge);
+
+  ASSERT_EQ(retrievePriority, mountPolicy.retrievePriority);
+  ASSERT_EQ(minRetrieveRequestAge, mountPolicy.retrieveMinRequestAge);
+
+  ASSERT_EQ(maxDrivesAllowed, mountPolicy.maxDrivesAllowed);
+
+  ASSERT_EQ(comment, mountPolicy.comment);
+
+  const common::dataStructures::EntryLog creationLog = mountPolicy.creationLog;
+  ASSERT_EQ(m_cliSI.username, creationLog.username);
+  ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+  const common::dataStructures::EntryLog lastModificationLog =
+    mountPolicy.lastModificationLog;
+  ASSERT_EQ(creationLog, lastModificationLog);
+
+  m_catalogue->deleteMountPolicy(name);
+
+  ASSERT_TRUE(m_catalogue->getMountPolicies().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteMountPolicy_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getMountPolicies().empty());
+  ASSERT_THROW(m_catalogue->deleteMountPolicy("non_existant_mount_policy"), catalogue::UserError);
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, createRequester) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getRequesters().empty());
+
+  const std::string mountPolicyName = "mount_policy";
+  const uint64_t archivePriority = 1;
+  const uint64_t minArchiveRequestAge = 4;
+  const uint64_t retrievePriority = 5;
+  const uint64_t minRetrieveRequestAge = 8;
+  const uint64_t maxDrivesAllowed = 9;
+
+  m_catalogue->createMountPolicy(
+    m_cliSI,
+    mountPolicyName,
+    archivePriority,
+    minArchiveRequestAge,
+    retrievePriority,
+    minRetrieveRequestAge,
+    maxDrivesAllowed,
+    "create mount group");
+
+  const std::string comment = "create requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, comment);
+
+  const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters();
+  ASSERT_EQ(1, requesters.size());
+
+  const common::dataStructures::Requester requester = requesters.front();
+
+  ASSERT_EQ(requesterName, requester.name);
+  ASSERT_EQ(mountPolicyName, requester.mountPolicy);
+  ASSERT_EQ(comment, requester.comment);
+  ASSERT_EQ(m_cliSI.username, requester.creationLog.username);
+  ASSERT_EQ(m_cliSI.host, requester.creationLog.host);
+  ASSERT_EQ(requester.creationLog, requester.lastModificationLog);
+
+  const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName);
+
+  ASSERT_EQ(archivePriority, policy.archivePriority);
+  ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge);
+  ASSERT_EQ(maxDrivesAllowed, policy.maxDrivesAllowed);
+  ASSERT_EQ(retrievePriority, policy.retrievePriority);
+  ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge);
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, createRequester_same_twice) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getRequesters().empty());
+
+  const std::string mountPolicyName = "mount_policy";
+  const uint64_t archivePriority = 1;
+  const uint64_t minArchiveRequestAge = 4;
+  const uint64_t retrievePriority = 5;
+  const uint64_t minRetrieveRequestAge = 8;
+  const uint64_t maxDrivesAllowed = 9;
+
+  m_catalogue->createMountPolicy(
+    m_cliSI,
+    mountPolicyName,
+    archivePriority,
+    minArchiveRequestAge,
+    retrievePriority,
+    minRetrieveRequestAge,
+    maxDrivesAllowed,
+    "create mount group");
+  
+  const std::string comment = "create requester";
+  const std::string requesterName = "requester_name";
+  const std::string mountPolicy = "mount_policy";
+  m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, comment);
+  ASSERT_THROW(m_catalogue->createRequester(m_cliSI, requesterName, mountPolicy, comment), catalogue::UserError);
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteRequester) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getRequesters().empty());
+
+  const std::string mountPolicyName = "mount_policy";
+  const uint64_t archivePriority = 1;
+  const uint64_t minArchiveRequestAge = 4;
+  const uint64_t retrievePriority = 5;
+  const uint64_t minRetrieveRequestAge = 8;
+  const uint64_t maxDrivesAllowed = 9;
+
+  m_catalogue->createMountPolicy(
+    m_cliSI,
+    mountPolicyName,
+    archivePriority,
+    minArchiveRequestAge,
+    retrievePriority,
+    minRetrieveRequestAge,
+    maxDrivesAllowed,
+    "create mount group");
+
+  const std::string comment = "create requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, comment);
+
+  const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters();
+  ASSERT_EQ(1, requesters.size());
+
+  const common::dataStructures::Requester requester = requesters.front();
+
+  ASSERT_EQ(requesterName, requester.name);
+  ASSERT_EQ(mountPolicyName, requester.mountPolicy);
+  ASSERT_EQ(comment, requester.comment);
+  ASSERT_EQ(m_cliSI.username, requester.creationLog.username);
+  ASSERT_EQ(m_cliSI.host, requester.creationLog.host);
+  ASSERT_EQ(requester.creationLog, requester.lastModificationLog);
+
+  const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName);
+
+  ASSERT_EQ(archivePriority, policy.archivePriority);
+  ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge);
+  ASSERT_EQ(maxDrivesAllowed, policy.maxDrivesAllowed);
+  ASSERT_EQ(retrievePriority, policy.retrievePriority);
+  ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge);
+
+  m_catalogue->deleteRequester(requesterName);
+  ASSERT_TRUE(m_catalogue->getRequesters().empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteRequester_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getRequesters().empty());
+  ASSERT_THROW(m_catalogue->deleteRequester("non_existant_requester"), catalogue::UserError);
 }
 
-TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser) {
+TEST_F(cta_catalogue_InMemoryCatalogueTest, assignMountPolicyToRequester) {
   using namespace cta;
 
   ASSERT_TRUE(m_catalogue->getRequesters().empty());
 
-  const std::string mountPolicyName = "mount_group";
+  const std::string mountPolicyName = "mount_policy";
   const uint64_t archivePriority = 1;
   const uint64_t minArchiveRequestAge = 4;
   const uint64_t retrievePriority = 5;
@@ -805,28 +1370,23 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser) {
     maxDrivesAllowed,
     "create mount group");
 
-  const std::string comment = "create user";
-  const std::string userName = "user_name";
-  const std::string group = "group";
-  cta::common::dataStructures::UserIdentity userIdentity;
-  userIdentity.name=userName;
-  userIdentity.group=group;
-  m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, comment);
+  const std::string comment = "create requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment);
 
-  const std::list<common::dataStructures::Requester> users = m_catalogue->getRequesters();
-  ASSERT_EQ(1, users.size());
+  const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters();
+  ASSERT_EQ(1, requesters.size());
 
-  const common::dataStructures::Requester user = users.front();
+  const common::dataStructures::Requester requester = requesters.front();
 
-  ASSERT_EQ(userName, user.name);
-  ASSERT_EQ(mountPolicyName, user.mountPolicy);
-  ASSERT_EQ(comment, user.comment);
-  ASSERT_EQ(m_cliSI.username, user.creationLog.username);
-  ASSERT_EQ(m_cliSI.host, user.creationLog.host);
-  ASSERT_EQ(user.creationLog, user.lastModificationLog);
+  ASSERT_EQ(requesterName, requester.name);
+  ASSERT_EQ(mountPolicyName, requester.mountPolicy);
+  ASSERT_EQ(comment, requester.comment);
+  ASSERT_EQ(m_cliSI.username, requester.creationLog.username);
+  ASSERT_EQ(m_cliSI.host, requester.creationLog.host);
+  ASSERT_EQ(requester.creationLog, requester.lastModificationLog);
 
-  const common::dataStructures::MountPolicy policy =
-    m_catalogue->getMountPolicyForAUser(userIdentity);
+  const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName);
 
   ASSERT_EQ(archivePriority, policy.archivePriority);
   ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge);
@@ -835,12 +1395,12 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser) {
   ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge);
 }
 
-TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser_same_twice) {
+TEST_F(cta_catalogue_InMemoryCatalogueTest, assignMountPolicyToRequester_same_twice) {
   using namespace cta;
 
   ASSERT_TRUE(m_catalogue->getRequesters().empty());
 
-  const std::string mountPolicyName = "mount_group";
+  const std::string mountPolicyName = "mount_policy";
   const uint64_t archivePriority = 1;
   const uint64_t minArchiveRequestAge = 4;
   const uint64_t retrievePriority = 5;
@@ -856,17 +1416,24 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser_same_twice) {
     minRetrieveRequestAge,
     maxDrivesAllowed,
     "create mount group");
-  
-  const std::string comment = "create user";
-  const std::string name = "name";
-  const std::string mountPolicy = "mount_group";
-  const std::string group = "group";
-  cta::common::dataStructures::UserIdentity userIdentity;
-  userIdentity.name=name;
-  userIdentity.group=group;
-  m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, comment);
-  ASSERT_THROW(m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicy, comment),
-    exception::Exception);
+
+  const std::string comment = "create requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment);
+  ASSERT_THROW(m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment),
+    catalogue::UserError);
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, assignMountPolicyToRequester_non_existant_mount_policy) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getRequesters().empty());
+
+  const std::string comment = "create requester";
+  const std::string mountPolicyName = "non_existant_mount_policy";
+  const std::string requesterName = "requsterName";
+  ASSERT_THROW(m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment),
+    catalogue::UserError);
 }
 
 TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) {
@@ -874,7 +1441,7 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) {
 
   ASSERT_TRUE(m_catalogue->getRequesters().empty());
 
-  const std::string mountPolicyName = "mount_group";
+  const std::string mountPolicyName = "mount_policy";
   const uint64_t archivePriority = 1;
   const uint64_t minArchiveRequestAge = 2;
   const uint64_t retrievePriority = 3;
@@ -891,28 +1458,23 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) {
     maxDrivesAllowed,
     "create mount group");
 
-  const std::string userComment = "create user";
-  const std::string userName = "user_name";
-  const std::string group = "group";
-  cta::common::dataStructures::UserIdentity userIdentity;
-  userIdentity.name=userName;
-  userIdentity.group=group;
-  m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, userComment);
+  const std::string userComment = "create requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, userComment);
 
-  const std::list<common::dataStructures::Requester> users = m_catalogue->getRequesters();
-  ASSERT_EQ(1, users.size());
+  const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters();
+  ASSERT_EQ(1, requesters.size());
 
-  const common::dataStructures::Requester user = users.front();
+  const common::dataStructures::Requester requester = requesters.front();
 
-  ASSERT_EQ(userName, user.name);
-  ASSERT_EQ(mountPolicyName, user.mountPolicy);
-  ASSERT_EQ(userComment, user.comment);
-  ASSERT_EQ(m_cliSI.username, user.creationLog.username);
-  ASSERT_EQ(m_cliSI.host, user.creationLog.host);
-  ASSERT_EQ(user.creationLog, user.lastModificationLog);
+  ASSERT_EQ(requesterName, requester.name);
+  ASSERT_EQ(mountPolicyName, requester.mountPolicy);
+  ASSERT_EQ(userComment, requester.comment);
+  ASSERT_EQ(m_cliSI.username, requester.creationLog.username);
+  ASSERT_EQ(m_cliSI.host, requester.creationLog.host);
+  ASSERT_EQ(requester.creationLog, requester.lastModificationLog);
 
-  const common::dataStructures::MountPolicy policy =
-    m_catalogue->getMountPolicyForAUser(userIdentity);
+  const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName);
 
   ASSERT_EQ(archivePriority, policy.archivePriority);
   ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge);
@@ -964,11 +1526,20 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) {
   ASSERT_EQ(copyNb, maplet.first);
   ASSERT_EQ(tapePoolName, maplet.second);
 
-  for(uint64_t i = 1; i<=10; i++) {
+  common::dataStructures::UserIdentity userIdentity;
+  userIdentity.name = requesterName;
+  userIdentity.group = "group";
+  uint64_t expectedArchiveFileId = 0;
+  for(uint64_t i = 0; i<10; i++) {
     const common::dataStructures::ArchiveFileQueueCriteria queueCriteria =
       m_catalogue->prepareForNewFile(storageClassName, userIdentity);
 
-    ASSERT_EQ(i, queueCriteria.fileId);
+    if(0 == i) {
+      expectedArchiveFileId = queueCriteria.fileId;
+    } else {
+      expectedArchiveFileId++;
+    }
+    ASSERT_EQ(expectedArchiveFileId, queueCriteria.fileId);
 
     ASSERT_EQ(1, queueCriteria.copyToPoolMap.size());
     ASSERT_EQ(copyNb, queueCriteria.copyToPoolMap.begin()->first);
@@ -1148,7 +1719,7 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareToRetrieveFile) {
     ASSERT_EQ(file2Written.compressedSize, tapeFile2.compressedSize);
   }
 
-  const std::string mountPolicyName = "mount_group";
+  const std::string mountPolicyName = "mount_policy";
   const uint64_t archivePriority = 1;
   const uint64_t minArchiveRequestAge = 2;
   const uint64_t retrievePriority = 3;
@@ -1165,27 +1736,23 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareToRetrieveFile) {
     maxDrivesAllowed,
     "create mount group");
 
-  const std::string userComment = "create user";
-  const std::string userName = "user_name";
-  const std::string group = "group";
-  cta::common::dataStructures::UserIdentity userIdentity;
-  userIdentity.name=userName;
-  userIdentity.group=group;
-  m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, userComment);
+  const std::string userComment = "create requester";
+  const std::string requesterName = "requester_name";
+  m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, userComment);
 
-  const std::list<common::dataStructures::Requester> users = m_catalogue->getRequesters();
-  ASSERT_EQ(1, users.size());
+  const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters();
+  ASSERT_EQ(1, requesters.size());
 
-  const common::dataStructures::Requester user = users.front();
+  const common::dataStructures::Requester requester = requesters.front();
 
-  ASSERT_EQ(userName, user.name);
-  ASSERT_EQ(mountPolicyName, user.mountPolicy);
-  ASSERT_EQ(userComment, user.comment);
-  ASSERT_EQ(m_cliSI.username, user.creationLog.username);
-  ASSERT_EQ(m_cliSI.host, user.creationLog.host);
-  ASSERT_EQ(user.creationLog, user.lastModificationLog);  
+  ASSERT_EQ(requesterName, requester.name);
+  ASSERT_EQ(mountPolicyName, requester.mountPolicy);
+  ASSERT_EQ(userComment, requester.comment);
+  ASSERT_EQ(m_cliSI.username, requester.creationLog.username);
+  ASSERT_EQ(m_cliSI.host, requester.creationLog.host);
+  ASSERT_EQ(requester.creationLog, requester.lastModificationLog);
 
-  const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(userIdentity);
+  const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName);
 
   ASSERT_EQ(archivePriority, policy.archivePriority);
   ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge);
@@ -1193,6 +1760,9 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareToRetrieveFile) {
   ASSERT_EQ(retrievePriority, policy.retrievePriority);
   ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge);
 
+  common::dataStructures::UserIdentity userIdentity;
+  userIdentity.name = requesterName;
+  userIdentity.group = "group";
   const common::dataStructures::RetrieveFileQueueCriteria queueCriteria =
     m_catalogue->prepareToRetrieveFile(archiveFileId, userIdentity);
 
@@ -1632,4 +2202,239 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, fileWrittenToTape_2_tape_files_corru
   ASSERT_THROW(m_catalogue->fileWrittenToTape(file2Written), exception::Exception);
 }
 
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveFile) {
+  using namespace cta;
+
+  const std::string vid1 = "VID123";
+  const std::string vid2 = "VID456";
+  const std::string logicalLibraryName = "logical_library_name";
+  const std::string tapePoolName = "tape_pool_name";
+  const std::string encryptionKey = "encryption_key";
+  const uint64_t capacityInBytes = (uint64_t)10 * 1000 * 1000 * 1000 * 1000;
+  const bool disabledValue = true;
+  const bool fullValue = false;
+  const std::string comment = "create tape";
+
+  m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName,
+    "create logical library");
+  m_catalogue->createTapePool(m_cliSI, tapePoolName, 2, true, "create tape pool");
+  m_catalogue->createTape(m_cliSI, vid1, logicalLibraryName, tapePoolName,
+    encryptionKey, capacityInBytes, disabledValue, fullValue,
+    comment);
+  m_catalogue->createTape(m_cliSI, vid2, logicalLibraryName, tapePoolName,
+    encryptionKey, capacityInBytes, disabledValue, fullValue,
+    comment);
+
+  const std::list<common::dataStructures::Tape> tapes =
+    m_catalogue->getTapes("", "", "", "", "", "", "", "");
+
+  ASSERT_EQ(2, tapes.size());
+
+  const std::map<std::string, common::dataStructures::Tape> vidToTape = tapeListToMap(tapes);
+  {
+    auto it = vidToTape.find(vid1);
+    const common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(vid1, tape.vid);
+    ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(encryptionKey, tape.encryptionKey);
+    ASSERT_EQ(capacityInBytes, tape.capacityInBytes);
+    ASSERT_TRUE(disabledValue == tape.disabled);
+    ASSERT_TRUE(fullValue == tape.full);
+    ASSERT_EQ(comment, tape.comment);
+
+    const common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_cliSI.username, creationLog.username);
+    ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+    const common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+  {
+    auto it = vidToTape.find(vid2);
+    const common::dataStructures::Tape &tape = it->second;
+    ASSERT_EQ(vid2, tape.vid);
+    ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(encryptionKey, tape.encryptionKey);
+    ASSERT_EQ(capacityInBytes, tape.capacityInBytes);
+    ASSERT_TRUE(disabledValue == tape.disabled);
+    ASSERT_TRUE(fullValue == tape.full);
+    ASSERT_EQ(comment, tape.comment);
+
+    const common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_cliSI.username, creationLog.username);
+    ASSERT_EQ(m_cliSI.host, creationLog.host);
+
+    const common::dataStructures::EntryLog lastModificationLog =
+      tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  const uint64_t archiveFileId = 1234;
+
+  ASSERT_TRUE(m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "").empty());
+  ASSERT_THROW(m_catalogue->getArchiveFileById(archiveFileId), exception::Exception);
+
+  const std::string storageClassName = "storage_class";
+  const uint64_t nbCopies = 2;
+  m_catalogue->createStorageClass(m_cliSI, storageClassName, nbCopies, "create storage class");
+
+  const uint64_t archiveFileSize = 1;
+
+  catalogue::TapeFileWritten file1Written;
+  file1Written.archiveFileId        = archiveFileId;
+  file1Written.diskInstance         = "PUBLIC";
+  file1Written.diskFileId           = "5678";
+  file1Written.diskFilePath         = "/public_dir/public_file";
+  file1Written.diskFileUser         = "public_disk_user";
+  file1Written.diskFileGroup        = "public_disk_group";
+  file1Written.diskFileRecoveryBlob = "opaque_disk_file_recovery_contents";
+  file1Written.size                 = archiveFileSize;
+  file1Written.storageClassName     = storageClassName;
+  file1Written.vid                  = vid1;
+  file1Written.fSeq                 = 1;
+  file1Written.blockId              = 4321;
+  file1Written.compressedSize       = 1;
+  file1Written.copyNb               = 1;
+  m_catalogue->fileWrittenToTape(file1Written);
+
+  {
+    std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes(file1Written.vid, "", "", "", "", "", "", "");
+    ASSERT_EQ(1, tapes.size());
+    const common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const common::dataStructures::ArchiveFile archiveFile = m_catalogue->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileID);
+    ASSERT_EQ(file1Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file1Written.diskFilePath, archiveFile.drData.drPath);
+    ASSERT_EQ(file1Written.diskFileUser, archiveFile.drData.drOwner);
+    ASSERT_EQ(file1Written.diskFileGroup, archiveFile.drData.drGroup);
+    ASSERT_EQ(file1Written.diskFileRecoveryBlob, archiveFile.drData.drBlob);
+
+    ASSERT_EQ(1, archiveFile.tapeFiles.size());
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_FALSE(copyNbToTapeFile1Itor == archiveFile.tapeFiles.end());
+    const common::dataStructures::TapeFile &tapeFile1 = copyNbToTapeFile1Itor->second;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.compressedSize, tapeFile1.compressedSize);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+  }
+
+  catalogue::TapeFileWritten file2Written;
+  file2Written.archiveFileId        = file1Written.archiveFileId;
+  file2Written.diskInstance         = file1Written.diskInstance;
+  file2Written.diskFileId           = file1Written.diskFileId;
+  file2Written.diskFilePath         = file1Written.diskFilePath;
+  file2Written.diskFileUser         = file1Written.diskFileUser;
+  file2Written.diskFileGroup        = file1Written.diskFileGroup;
+  file2Written.diskFileRecoveryBlob = file1Written.diskFileRecoveryBlob;
+  file2Written.size                 = archiveFileSize;
+  file2Written.storageClassName     = storageClassName;
+  file2Written.vid                  = vid2;
+  file2Written.fSeq                 = 1;
+  file2Written.blockId              = 4331;
+  file2Written.compressedSize       = 1;
+  file2Written.copyNb               = 2;
+  m_catalogue->fileWrittenToTape(file2Written);
+
+  {
+    ASSERT_EQ(2, m_catalogue->getTapes("", "", "", "", "", "", "", "").size());
+    std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes(file2Written.vid, "", "", "", "", "", "", "");
+    ASSERT_EQ(1, tapes.size());
+    const common::dataStructures::Tape &tape = tapes.front();
+    ASSERT_EQ(1, tape.lastFSeq);
+  }
+
+  {
+    const common::dataStructures::ArchiveFile archiveFile = m_catalogue->getArchiveFileById(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileID);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file2Written.diskFilePath, archiveFile.drData.drPath);
+    ASSERT_EQ(file2Written.diskFileUser, archiveFile.drData.drOwner);
+    ASSERT_EQ(file2Written.diskFileGroup, archiveFile.drData.drGroup);
+    ASSERT_EQ(file2Written.diskFileRecoveryBlob, archiveFile.drData.drBlob);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_FALSE(copyNbToTapeFile1Itor == archiveFile.tapeFiles.end());
+    const common::dataStructures::TapeFile &tapeFile1 = copyNbToTapeFile1Itor->second;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.compressedSize, tapeFile1.compressedSize);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_FALSE(copyNbToTapeFile2Itor == archiveFile.tapeFiles.end());
+    const common::dataStructures::TapeFile &tapeFile2 = copyNbToTapeFile2Itor->second;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.compressedSize, tapeFile2.compressedSize);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+
+  {
+    const common::dataStructures::ArchiveFile archiveFile = m_catalogue->deleteArchiveFile(archiveFileId);
+
+    ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID);
+    ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileID);
+    ASSERT_EQ(file2Written.size, archiveFile.fileSize);
+    ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass);
+
+    ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance);
+    ASSERT_EQ(file2Written.diskFilePath, archiveFile.drData.drPath);
+    ASSERT_EQ(file2Written.diskFileUser, archiveFile.drData.drOwner);
+    ASSERT_EQ(file2Written.diskFileGroup, archiveFile.drData.drGroup);
+    ASSERT_EQ(file2Written.diskFileRecoveryBlob, archiveFile.drData.drBlob);
+
+    ASSERT_EQ(2, archiveFile.tapeFiles.size());
+
+    auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1);
+    ASSERT_FALSE(copyNbToTapeFile1Itor == archiveFile.tapeFiles.end());
+    const common::dataStructures::TapeFile &tapeFile1 = copyNbToTapeFile1Itor->second;
+    ASSERT_EQ(file1Written.vid, tapeFile1.vid);
+    ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq);
+    ASSERT_EQ(file1Written.blockId, tapeFile1.blockId);
+    ASSERT_EQ(file1Written.compressedSize, tapeFile1.compressedSize);
+    ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb);
+
+    auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2);
+    ASSERT_FALSE(copyNbToTapeFile2Itor == archiveFile.tapeFiles.end());
+    const common::dataStructures::TapeFile &tapeFile2 = copyNbToTapeFile2Itor->second;
+    ASSERT_EQ(file2Written.vid, tapeFile2.vid);
+    ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq);
+    ASSERT_EQ(file2Written.blockId, tapeFile2.blockId);
+    ASSERT_EQ(file2Written.compressedSize, tapeFile2.compressedSize);
+    ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb);
+  }
+
+  ASSERT_TRUE(m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "").empty());
+}
+
+TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveFile_non_existant) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "").empty());
+  ASSERT_THROW(m_catalogue->deleteArchiveFile(12345678), catalogue::UserError);
+}
+
 } // namespace unitTests
diff --git a/catalogue/OcciStmt.cpp b/catalogue/OcciStmt.cpp
index 432d1df19d..1f90f3fa49 100644
--- a/catalogue/OcciStmt.cpp
+++ b/catalogue/OcciStmt.cpp
@@ -143,6 +143,13 @@ void OcciStmt::executeNonQuery() {
   }
 }
 
+//------------------------------------------------------------------------------
+// getNbAffectedRows
+//------------------------------------------------------------------------------
+uint64_t OcciStmt::getNbAffectedRows() const {
+  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+}
+
 //------------------------------------------------------------------------------
 // get
 //------------------------------------------------------------------------------
diff --git a/catalogue/OcciStmt.hpp b/catalogue/OcciStmt.hpp
index d355b67494..fcf76840bd 100644
--- a/catalogue/OcciStmt.hpp
+++ b/catalogue/OcciStmt.hpp
@@ -107,6 +107,14 @@ public:
    */
   virtual void executeNonQuery();
 
+  /**
+   * Returns the number of rows affected by the last execution of this
+   * statement.
+   *
+   * @return The number of affected rows.
+   */
+  virtual uint64_t getNbAffectedRows() const;
+
   /**
    * Returns the underlying OCCI result set.
    *
diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp
index d068a1f7fe..ebd8e198f0 100644
--- a/catalogue/OracleCatalogue.cpp
+++ b/catalogue/OracleCatalogue.cpp
@@ -40,6 +40,13 @@ OracleCatalogue::OracleCatalogue(
 OracleCatalogue::~OracleCatalogue() {
 }
 
+//------------------------------------------------------------------------------
+// deleteArchiveFile
+//------------------------------------------------------------------------------
+common::dataStructures::ArchiveFile OracleCatalogue::deleteArchiveFile(const uint64_t archiveFileId) {
+  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+}
+
 //------------------------------------------------------------------------------
 // getNextArchiveFileId
 //------------------------------------------------------------------------------
diff --git a/catalogue/OracleCatalogue.hpp b/catalogue/OracleCatalogue.hpp
index 0710ad4cd4..a785a4cbe7 100644
--- a/catalogue/OracleCatalogue.hpp
+++ b/catalogue/OracleCatalogue.hpp
@@ -58,6 +58,16 @@ public:
    */
   virtual ~OracleCatalogue();
 
+  /**
+   * Deletes the specified archive file and its associated tape copies from the
+   * catalogue.
+   *
+   * @param archiveFileId The unique identifier of the archive file.
+   * @return The metadata of the deleted archive file including the metadata of
+   * the associated and also deleted tape copies.
+   */
+  virtual common::dataStructures::ArchiveFile deleteArchiveFile(const uint64_t archiveFileId);
+
   /**
    * Returns a unique archive ID that can be used by a new archive file within
    * the catalogue.
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 32dee6abac..2dfab1c6ad 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -147,7 +147,20 @@ bool RdbmsCatalogue::adminUserExists(const std::string adminUsername) const {
 // deleteAdminUser
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteAdminUser(const std::string &username) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql = "DELETE FROM ADMIN_USER WHERE ADMIN_USER_NAME = :ADMIN_USER_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":ADMIN_USER_NAME", username);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete admin-user ") + username + " because they do not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -220,7 +233,7 @@ void RdbmsCatalogue::createAdminHost(
   const std::string &hostName,
   const std::string &comment) {
   try {
-    if (adminHostExists(hostName)) {
+    if(adminHostExists(hostName)) {
       throw UserError(std::string("Cannot create admin host " + hostName +
         " because an admin host with the same name already exists"));
     }
@@ -297,7 +310,20 @@ bool RdbmsCatalogue::adminHostExists(const std::string adminHost) const {
 // deleteAdminHost
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteAdminHost(const std::string &hostName) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql = "DELETE FROM ADMIN_HOST WHERE ADMIN_HOST_NAME = :ADMIN_HOST_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":ADMIN_HOST_NAME", hostName);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete admin-host ") + hostName + " because it does not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -319,7 +345,7 @@ std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() con
         "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 "
+      "FROM "
         "ADMIN_HOST";
     std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
     std::unique_ptr<DbRset> rset(stmt->executeQuery());
@@ -329,9 +355,6 @@ std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() con
       host.name = rset->columnText("ADMIN_HOST_NAME");
       host.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -339,9 +362,6 @@ std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() con
 
       host.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -375,7 +395,7 @@ void RdbmsCatalogue::createStorageClass(
   const uint64_t nbCopies,
   const std::string &comment) {
   try {
-    if (storageClassExists(name)) {
+    if(storageClassExists(name)) {
       throw UserError(std::string("Cannot create storage class ") + name +
         " because a storage class with the same name already exists");
     }
@@ -460,12 +480,17 @@ void RdbmsCatalogue::deleteStorageClass(const std::string &name) {
       "DELETE FROM "
         "STORAGE_CLASS "
       "WHERE "
-        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME";
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME;";
     std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
 
     stmt->bindString(":STORAGE_CLASS_NAME", name);
 
     stmt->executeNonQuery();
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete storage-class ") + name + " because it does not exist");
+    }
+  } catch(UserError &) {
+    throw;
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
   }
@@ -503,9 +528,6 @@ std::list<common::dataStructures::StorageClass>
       storageClass.nbCopies = rset->columnUint64("NB_COPIES");
       storageClass.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -513,9 +535,6 @@ std::list<common::dataStructures::StorageClass>
 
       storageClass.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -639,7 +658,20 @@ bool RdbmsCatalogue::tapePoolExists(const std::string &tapePoolName) const {
 // deleteTapePool
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteTapePool(const std::string &name) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":TAPE_POOL_NAME", name);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -677,9 +709,6 @@ std::list<common::dataStructures::TapePool>
 
       pool.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -687,9 +716,6 @@ std::list<common::dataStructures::TapePool>
 
       pool.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -793,7 +819,29 @@ void RdbmsCatalogue::createArchiveRoute(
 // deleteArchiveRoute
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteArchiveRoute(const std::string &storageClassName, const uint64_t copyNb) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql =
+      "DELETE FROM "
+        "ARCHIVE_ROUTE "
+      "WHERE "
+        "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND "
+        "COPY_NB = :COPY_NB;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":STORAGE_CLASS_NAME", storageClassName);
+    stmt->bindUint64(":COPY_NB", copyNb);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      UserError ue;
+      ue.getMessage() << "Cannot delete archive route for storage-class " << storageClassName + " and copy number " <<
+        copyNb << " because it does not exist";
+      throw ue;
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -831,9 +879,6 @@ std::list<common::dataStructures::ArchiveRoute>
 
       route.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -841,9 +886,6 @@ std::list<common::dataStructures::ArchiveRoute>
 
       route.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -882,7 +924,7 @@ void RdbmsCatalogue::createLogicalLibrary(
   const std::string &name,
   const std::string &comment) {
   try {
-    if (logicalLibraryExists(name)) {
+    if(logicalLibraryExists(name)) {
       throw UserError(std::string("Cannot create logical library ") + name +
         " because a logical library with the same name already exists");
     }
@@ -959,7 +1001,20 @@ bool RdbmsCatalogue::logicalLibraryExists(const std::string &logicalLibraryName)
 // deleteLogicalLibrary
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteLogicalLibrary(const std::string &name) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql = "DELETE FROM LOGICAL_LIBRARY WHERE LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":LOGICAL_LIBRARY_NAME", name);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete logical-library ") + name + " because it does not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -993,9 +1048,6 @@ std::list<common::dataStructures::LogicalLibrary>
 
       lib.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -1003,9 +1055,6 @@ std::list<common::dataStructures::LogicalLibrary>
 
       lib.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -1043,7 +1092,7 @@ void RdbmsCatalogue::createTape(
   const bool fullValue,
   const std::string &comment) {
   try {
-    if (tapeExists(vid)) {
+    if(tapeExists(vid)) {
       throw UserError(std::string("Cannot create tape ") + vid +
         " because a tape with the same volume identifier already exists");
     }
@@ -1174,7 +1223,20 @@ bool RdbmsCatalogue::tapeExists(const std::string &vid) const {
 // deleteTape
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteTape(const std::string &vid) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql = "DELETE FROM TAPE WHERE VID = :VID;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":VID", vid);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete tape ") + vid + " because it does not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -1306,9 +1368,6 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(
 
       tape.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -1316,9 +1375,6 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(
 
       tape.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -1431,10 +1487,14 @@ void RdbmsCatalogue::modifyTapeComment(const common::dataStructures::SecurityIde
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::createRequester(
   const common::dataStructures::SecurityIdentity &cliIdentity,
-  const common::dataStructures::UserIdentity &user,
+  const std::string &requesterName,
   const std::string &mountPolicy,
   const std::string &comment) {
   try {
+    if(requesterExists(requesterName)) {
+      throw UserError(std::string("Cannot create requester ") + requesterName +
+        " because a requester with the same username already exists");
+    }
     const uint64_t now = time(NULL);
     const char *const sql =
       "INSERT INTO REQUESTER("
@@ -1465,7 +1525,7 @@ void RdbmsCatalogue::createRequester(
         ":LAST_UPDATE_TIME)";
     std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
 
-    stmt->bindString(":REQUESTER_NAME", user.name);
+    stmt->bindString(":REQUESTER_NAME", requesterName);
     stmt->bindString(":MOUNT_POLICY_NAME", mountPolicy);
 
     stmt->bindString(":USER_COMMENT", comment);
@@ -1479,6 +1539,8 @@ void RdbmsCatalogue::createRequester(
     stmt->bindUint64(":LAST_UPDATE_TIME", now);
 
     stmt->executeNonQuery();
+  } catch(UserError &) {
+    throw;
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
   }
@@ -1487,8 +1549,21 @@ void RdbmsCatalogue::createRequester(
 //------------------------------------------------------------------------------
 // deleteRequester
 //------------------------------------------------------------------------------
-void RdbmsCatalogue::deleteRequester(const common::dataStructures::UserIdentity &user) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+void RdbmsCatalogue::deleteRequester(const std::string &requesterName) {
+  try {
+    const char *const sql = "DELETE FROM REQUESTER WHERE REQUESTER_NAME = :REQUESTER_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":REQUESTER_NAME", requesterName);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete requester ") + requesterName + " because they do not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -1525,9 +1600,6 @@ std::list<common::dataStructures::Requester>
 
       user.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
-
       common::dataStructures::EntryLog creationLog;
       creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
       creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
@@ -1535,9 +1607,6 @@ std::list<common::dataStructures::Requester>
 
       user.creationLog = creationLog;
 
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
       common::dataStructures::EntryLog updateLog;
       updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
       updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
@@ -1581,7 +1650,7 @@ void RdbmsCatalogue::createMountPolicy(
   const uint64_t maxDrivesAllowed,
   const std::string &comment) {
   try {
-    if (mountPolicyExists(name)) {
+    if(mountPolicyExists(name)) {
       throw UserError(std::string("Cannot create mount policy ") + name +
         " because a mount policy with the same name already exists");
     }
@@ -1657,6 +1726,83 @@ void RdbmsCatalogue::createMountPolicy(
   }
 }
 
+//------------------------------------------------------------------------------
+// assignMountPolicyToRequester
+//------------------------------------------------------------------------------
+void RdbmsCatalogue::assignMountPolicyToRequester(
+  const common::dataStructures::SecurityIdentity &cliIdentity,
+  const std::string &mountPolicyName,
+  const std::string &requesterName,
+  const std::string &comment) {
+  try {
+    std::unique_ptr<common::dataStructures::MountPolicy> requesterMountPolicy(getRequesterMountPolicy(requesterName));
+    if(NULL != requesterMountPolicy.get()) {
+      throw UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester " + requesterName +
+        " because the requester is already assigned mount-policy " + requesterMountPolicy->name);
+    }
+    if(!mountPolicyExists(mountPolicyName)) {
+      throw UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester " + requesterName +
+        " because mount-policy " + mountPolicyName + " does not exist");
+    }
+    const uint64_t now = time(NULL);
+    const char *const sql =
+      "INSERT INTO REQUESTER("
+        "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("
+        ":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)";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+
+    stmt->bindString(":REQUESTER_NAME", requesterName);
+    stmt->bindString(":MOUNT_POLICY_NAME", mountPolicyName);
+
+    stmt->bindString(":USER_COMMENT", comment);
+
+    stmt->bindString(":CREATION_LOG_USER_NAME", cliIdentity.username);
+    stmt->bindString(":CREATION_LOG_HOST_NAME", cliIdentity.host);
+    stmt->bindUint64(":CREATION_LOG_TIME", now);
+
+    stmt->bindString(":LAST_UPDATE_USER_NAME", cliIdentity.username);
+    stmt->bindString(":LAST_UPDATE_HOST_NAME", cliIdentity.host);
+    stmt->bindUint64(":LAST_UPDATE_TIME", now);
+
+    stmt->executeNonQuery();
+  } catch(UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// assignMountPolicyToRequesterGroup
+//------------------------------------------------------------------------------
+void RdbmsCatalogue::assignMountPolicyToRequesterGroup(const std::string &mountPolicyName,
+  const std::string &requesterGroupName) {
+  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+}
+
 //------------------------------------------------------------------------------
 // mountPolicyExists
 //------------------------------------------------------------------------------
@@ -1678,11 +1824,136 @@ bool RdbmsCatalogue::mountPolicyExists(const std::string &mountPolicyName) const
   }
 }
 
+//------------------------------------------------------------------------------
+// requesterExists
+//------------------------------------------------------------------------------
+bool RdbmsCatalogue::requesterExists(const std::string &requesterName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "REQUESTER_NAME AS REQUESTER_NAME "
+      "FROM "
+        "REQUESTER "
+      "WHERE "
+        "REQUESTER_NAME = :REQUESTER_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":REQUESTER_NAME", requesterName);
+    std::unique_ptr<DbRset> rset(stmt->executeQuery());
+    return rset->next();
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// getRequesterMountPolicy
+//------------------------------------------------------------------------------
+common::dataStructures::MountPolicy *RdbmsCatalogue::getRequesterMountPolicy(const std::string &requesterName) 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.MAX_DRIVES_ALLOWED AS MAX_DRIVES_ALLOWED,"
+
+        "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 "
+      "ON "
+        "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER.MOUNT_POLICY_NAME "
+      "WHERE "
+        "REQUESTER.REQUESTER_NAME = :REQUESTER_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":REQUESTER_NAME", requesterName);
+    std::unique_ptr<DbRset> rset(stmt->executeQuery());
+    if(rset->next()) {
+      std::unique_ptr<common::dataStructures::MountPolicy> policy(new common::dataStructures::MountPolicy);
+
+      policy->name = rset->columnText("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->maxDrivesAllowed = rset->columnUint64("MAX_DRIVES_ALLOWED");
+
+      policy->comment = rset->columnText("USER_COMMENT");
+
+      policy->creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
+      policy->creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
+      policy->creationLog.time = rset->columnUint64("CREATION_LOG_TIME");
+
+      common::dataStructures::EntryLog updateLog;
+      policy->lastModificationLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
+      policy->lastModificationLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
+      policy->lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME");
+
+      return policy.release();
+    } else {
+      return NULL;
+    }
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// requesterGroupExists
+//------------------------------------------------------------------------------
+bool RdbmsCatalogue::requesterGroupExists(const std::string &requesterGroupName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME "
+      "FROM "
+        "REQUESTER_GROUP "
+      "WHERE "
+        "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName);
+    std::unique_ptr<DbRset> rset(stmt->executeQuery());
+    return rset->next();
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
 //------------------------------------------------------------------------------
 // deleteMountPolicy
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteMountPolicy(const std::string &name) {
-  throw exception::Exception(std::string(__FUNCTION__) + " not implemented");
+  try {
+    const char *const sql = "DELETE FROM MOUNT_POLICY WHERE MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME;";
+    std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+    stmt->bindString(":MOUNT_POLICY_NAME", name);
+    stmt->executeNonQuery();
+
+    if(0 == stmt->getNbAffectedRows()) {
+      throw UserError(std::string("Cannot delete mount policy ") + name + " because it does not exist");
+    }
+  } catch(UserError &) {
+    throw;
+  } catch (exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
 }
 
 //------------------------------------------------------------------------------
@@ -1723,38 +1994,22 @@ std::list<common::dataStructures::MountPolicy>
       policy.name = rset->columnText("MOUNT_POLICY_NAME");
 
       policy.archivePriority = rset->columnUint64("ARCHIVE_PRIORITY");
-      policy.archiveMinRequestAge =
-        rset->columnUint64("ARCHIVE_MIN_REQUEST_AGE");
+      policy.archiveMinRequestAge = rset->columnUint64("ARCHIVE_MIN_REQUEST_AGE");
 
-      policy.retrievePriority =
-        rset->columnUint64("RETRIEVE_PRIORITY");
-      policy.retrieveMinRequestAge =
-        rset->columnUint64("RETRIEVE_MIN_REQUEST_AGE");
+      policy.retrievePriority = rset->columnUint64("RETRIEVE_PRIORITY");
+      policy.retrieveMinRequestAge = rset->columnUint64("RETRIEVE_MIN_REQUEST_AGE");
 
-      policy.maxDrivesAllowed =
-        rset->columnUint64("MAX_DRIVES_ALLOWED");
+      policy.maxDrivesAllowed = rset->columnUint64("MAX_DRIVES_ALLOWED");
 
       policy.comment = rset->columnText("USER_COMMENT");
 
-      common::dataStructures::UserIdentity creatorUI;
-      creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME");
+      policy.creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
+      policy.creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
+      policy.creationLog.time = rset->columnUint64("CREATION_LOG_TIME");
 
-      common::dataStructures::EntryLog creationLog;
-      creationLog.username = rset->columnText("CREATION_LOG_USER_NAME");
-      creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME");
-      creationLog.time = rset->columnUint64("CREATION_LOG_TIME");
-
-      policy.creationLog = creationLog;
-
-      common::dataStructures::UserIdentity updaterUI;
-      updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME");
-
-      common::dataStructures::EntryLog updateLog;
-      updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
-      updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
-      updateLog.time = rset->columnUint64("LAST_UPDATE_TIME");
-
-      policy.lastModificationLog = updateLog;
+      policy.lastModificationLog.username = rset->columnText("LAST_UPDATE_USER_NAME");
+      policy.lastModificationLog.host = rset->columnText("LAST_UPDATE_HOST_NAME");
+      policy.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME");
 
       policies.push_back(policy);
     }
@@ -2063,7 +2318,7 @@ common::dataStructures::ArchiveFile RdbmsCatalogue::getArchiveFileById(const uin
     std::unique_ptr<common::dataStructures::ArchiveFile> file(getArchiveFile(id));
 
     // Throw an exception if the archive file does not exist
-    if (NULL == file.get()) {
+    if(NULL == file.get()) {
       exception::Exception ex;
       ex.getMessage() << "No such archive file with ID " << id;
       throw (ex);
@@ -2107,7 +2362,7 @@ common::dataStructures::ArchiveFileQueueCriteria
     throw ex;
   }
 
-  common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user);
+  common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user.name);
 
   // Now that we have both the archive routes and the mount policy it's safe to
   // consume an archive file identifier
@@ -2175,8 +2430,7 @@ uint64_t RdbmsCatalogue::getExpectedNbArchiveRoutes(const std::string &storageCl
 //------------------------------------------------------------------------------
 // getArchiveMountPolicy
 //------------------------------------------------------------------------------
-common::dataStructures::MountPolicy RdbmsCatalogue::
-  getMountPolicyForAUser(const common::dataStructures::UserIdentity &user) const {
+common::dataStructures::MountPolicy RdbmsCatalogue::getMountPolicyForAUser(const std::string &username) const {
   const char *const sql =
     "SELECT "
       "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME,"
@@ -2204,7 +2458,7 @@ common::dataStructures::MountPolicy RdbmsCatalogue::
     "WHERE "
       "REQUESTER.REQUESTER_NAME = :REQUESTER_NAME";
   std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
-  stmt->bindString(":REQUESTER_NAME", user.name);
+  stmt->bindString(":REQUESTER_NAME", username);
   std::unique_ptr<DbRset> rset(stmt->executeQuery());
   if(rset->next()) {
     common::dataStructures::MountPolicy policy;
@@ -2238,8 +2492,7 @@ common::dataStructures::MountPolicy RdbmsCatalogue::
     return policy;
   } else {
     exception::Exception ex;
-    ex.getMessage() << "Failed to find a mount policy for user " <<
-      user;
+    ex.getMessage() << "Failed to find a mount policy for user " << username;
     throw ex;
   }
 }
@@ -2379,7 +2632,7 @@ common::dataStructures::RetrieveFileQueueCriteria RdbmsCatalogue::prepareToRetri
   const uint64_t archiveFileId,
   const common::dataStructures::UserIdentity &user) {
   const std::map<uint64_t, common::dataStructures::TapeFile> tapeFiles = getTapeFiles(archiveFileId);
-  const common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user);
+  const common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user.name);
 
   return common::dataStructures::RetrieveFileQueueCriteria(tapeFiles, mountPolicy);
 }
@@ -2424,6 +2677,15 @@ std::map<uint64_t, common::dataStructures::TapeFile> RdbmsCatalogue::getTapeFile
   }
 }
 
+//------------------------------------------------------------------------------
+// getMountPolicies
+//------------------------------------------------------------------------------
+RequesterAndGroupMountPolicies RdbmsCatalogue::getMountPolicies(const std::string &requesterName,
+  const std::string &requesterGroupName) {
+  RequesterAndGroupMountPolicies policies;
+  return policies;
+}
+
 //------------------------------------------------------------------------------
 // isAdmin
 //------------------------------------------------------------------------------
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index 3b669e096a..b206837e50 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -20,6 +20,7 @@
 
 #include "catalogue/Catalogue.hpp"
 #include "catalogue/DbConn.hpp"
+#include "catalogue/RequesterAndGroupMountPolicies.hpp"
 
 #include <memory>
 #include <mutex>
@@ -118,8 +119,13 @@ public:
   virtual void setTapeLbp(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &vid, const bool lbpValue); // internal function (noCLI)
   virtual void modifyTapeComment(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &vid, const std::string &comment);
 
-  virtual void createRequester(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &mountPolicy, const std::string &comment);
-  virtual void deleteRequester(const common::dataStructures::UserIdentity &user);
+  virtual void createRequester(
+    const common::dataStructures::SecurityIdentity &cliIdentity,
+    const std::string &requesterName,
+    const std::string &mountPolicy,
+    const std::string &comment);
+
+  virtual void deleteRequester(const std::string &requesterName);
   virtual std::list<common::dataStructures::Requester> getRequesters() const;
   virtual void modifyRequesterMountPolicy(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &mountPolicy);
   virtual void modifyRequesterComment(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &comment);
@@ -134,6 +140,35 @@ public:
     const uint64_t maxDrivesAllowed,
     const std::string &comment);
 
+  /**
+   * Assigns the specified mount policy to the specified requester.
+   *
+   * Please note that requester mount-policies overrule requester-group
+   * mount-policies.
+   *
+   * @param cliIdentity The user of the command-line tool.
+   * @param mountPolicyName The name of the mount policy.
+   * @param requesterName The name of the requester.
+   * @param comment Comment.
+   */
+  virtual void assignMountPolicyToRequester(
+    const common::dataStructures::SecurityIdentity &cliIdentity,
+    const std::string &mountPolicyName,
+    const std::string &requesterName,
+    const std::string &comment);
+
+  /**
+   * Assigns the specified mount policy to the specified requester group.
+   *
+   * Please note that requester mount-policies overrule requester-group
+   * mount-policies.
+   *
+   * @param mountPolicyName The name of the mount policy.
+   * @param requesterGrouprName The name of the requester group.
+   */
+  virtual void assignMountPolicyToRequesterGroup(const std::string &mountPolicyName,
+    const std::string &requesterGroupName);
+
   virtual void deleteMountPolicy(const std::string &name);
   virtual std::list<common::dataStructures::MountPolicy> getMountPolicies() const;
   virtual void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &name, const uint64_t archivePriority);
@@ -219,15 +254,15 @@ public:
   /**
    * Returns the mount policy for the specified end user.
    *
-   * @param user The name of the end user.
-   * @return The archive mount policy.
+   * @param username The name of the end user.
+   * @return The mount policy.
    */
-  virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const common::dataStructures::UserIdentity &user) const;
+  virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const std::string &username) const;
 
   /**
    * Returns true if the specified user has admin privileges.
    *
-   * @param cliIdentity The user.
+   * @param cliIdentity The user of the command-line tool.
    * @return True if the specified user has admin privileges.:w
    */
   virtual bool isAdmin(const common::dataStructures::SecurityIdentity &cliIdentity) const;
@@ -298,6 +333,30 @@ protected:
    */
   bool mountPolicyExists(const std::string &mountPolicyName) const;
 
+  /**
+   * Returns true if the specified requester exists.
+   *
+   * @param requesterName The username of the requester.
+   * @return True if the requester exists.
+   */
+  bool requesterExists(const std::string &requesterName) const;
+
+  /**
+   * Returns the specified requester mount-policy or NULL if one does not exist.
+   *
+   * @param requesterName The name of the requester.
+   * @return The mount policy.
+   */
+  common::dataStructures::MountPolicy *getRequesterMountPolicy(const std::string &requesterName) const;
+
+  /**
+   * Returns true if the specified requester group exists.
+   *
+   * @param requesterGroupName The name of the requester group.
+   * @return True if the requester group exists.
+   */
+  bool requesterGroupExists(const std::string &requesterGroupName) const;
+
   /**
    * An RdbmsCatalogue specific method that inserts the specified row into the
    * ArchiveFile table.
@@ -409,6 +468,16 @@ protected:
    */
   std::map<uint64_t, common::dataStructures::TapeFile>getTapeFiles(const uint64_t archiveFileId) const;
 
+  /**
+   * Returns the mount policies for the specified requester and requester group.
+   *
+   * @param requesterName The name of the requester.
+   * @param requesterGroupName The name of the requester group.
+   * @return The mount policies.
+   */
+  RequesterAndGroupMountPolicies getMountPolicies(const std::string &requesterName,
+    const std::string &requesterGroupName);
+
   /**
    * Returns a unique archive ID that can be used by a new archive file within
    * the catalogue.
diff --git a/catalogue/RequesterAndGroupMountPolicies.hpp b/catalogue/RequesterAndGroupMountPolicies.hpp
new file mode 100644
index 0000000000..f5aff6aeac
--- /dev/null
+++ b/catalogue/RequesterAndGroupMountPolicies.hpp
@@ -0,0 +1,47 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/dataStructures/MountPolicy.hpp"
+
+#include <list>
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * Structure containing a list of requester mount-polices and a list of
+ * requester-group mount-policies.
+ */
+struct RequesterAndGroupMountPolicies {
+
+  /**
+   * List of requester mount-policies.
+   */
+  std::list<common::dataStructures::MountPolicy> requesterMountPolicies;
+
+  /**
+   * List of requester-group mount-policies.
+   */
+  std::list<common::dataStructures::MountPolicy> requesterGroupMountPolicies;
+
+}; // struct RequesterAndGroupMountPolicies
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/SqliteCatalogue.cpp b/catalogue/SqliteCatalogue.cpp
index 3f16202984..eb7a799e4d 100644
--- a/catalogue/SqliteCatalogue.cpp
+++ b/catalogue/SqliteCatalogue.cpp
@@ -16,9 +16,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "catalogue/SqliteCatalogue.hpp"
+#include "catalogue/AutoRollback.hpp"
 #include "catalogue/RdbmsCatalogueSchema.hpp"
+#include "catalogue/SqliteCatalogue.hpp"
 #include "catalogue/SqliteConn.hpp"
+#include "catalogue/UserError.hpp"
 #include "common/exception/Exception.hpp"
 
 namespace cta {
@@ -44,12 +46,115 @@ SqliteCatalogue::SqliteCatalogue() {
 SqliteCatalogue::~SqliteCatalogue() {
 }
 
+//------------------------------------------------------------------------------
+// deleteArchiveFile
+//------------------------------------------------------------------------------
+common::dataStructures::ArchiveFile SqliteCatalogue::deleteArchiveFile(const uint64_t archiveFileId) {
+  try {
+    std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile;
+
+    m_conn->executeNonQuery("BEGIN EXCLUSIVE;");
+    const char *selectSql =
+      "SELECT "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID,"
+        "ARCHIVE_FILE.DISK_INSTANCE AS DISK_INSTANCE,"
+       "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID,"
+        "ARCHIVE_FILE.DISK_FILE_PATH AS DISK_FILE_PATH,"
+        "ARCHIVE_FILE.DISK_FILE_USER AS DISK_FILE_USER,"
+        "ARCHIVE_FILE.DISK_FILE_GROUP AS DISK_FILE_GROUP,"
+        "ARCHIVE_FILE.DISK_FILE_RECOVERY_BLOB AS DISK_FILE_RECOVERY_BLOB,"
+        "ARCHIVE_FILE.FILE_SIZE AS FILE_SIZE,"
+        "ARCHIVE_FILE.CHECKSUM_TYPE AS CHECKSUM_TYPE,"
+        "ARCHIVE_FILE.CHECKSUM_VALUE AS CHECKSUM_VALUE,"
+        "ARCHIVE_FILE.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.COMPRESSED_SIZE AS COMPRESSED_SIZE,"
+        "TAPE_FILE.COPY_NB AS COPY_NB,"
+        "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME "
+      "FROM "
+        "ARCHIVE_FILE "
+      "LEFT OUTER JOIN TAPE_FILE ON "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID "
+      "WHERE "
+        "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID";
+    std::unique_ptr<DbStmt> selectStmt(m_conn->createStmt(selectSql));
+    selectStmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+    std::unique_ptr<DbRset> selectRset(selectStmt->executeQuery());
+    while(selectRset->next()) {
+      if(NULL == archiveFile.get()) {
+        archiveFile.reset(new common::dataStructures::ArchiveFile);
+
+        archiveFile->archiveFileID = selectRset->columnUint64("ARCHIVE_FILE_ID");
+        archiveFile->diskInstance = selectRset->columnText("DISK_INSTANCE");
+        archiveFile->diskFileID = selectRset->columnText("DISK_FILE_ID");
+        archiveFile->drData.drPath = selectRset->columnText("DISK_FILE_PATH");
+        archiveFile->drData.drOwner = selectRset->columnText("DISK_FILE_USER");
+        archiveFile->drData.drGroup = selectRset->columnText("DISK_FILE_GROUP");
+        archiveFile->drData.drBlob = selectRset->columnText("DISK_FILE_RECOVERY_BLOB");
+        archiveFile->fileSize = selectRset->columnUint64("FILE_SIZE");
+        archiveFile->checksumType = selectRset->columnText("CHECKSUM_TYPE");
+        archiveFile->checksumValue = selectRset->columnText("CHECKSUM_VALUE");
+        archiveFile->storageClass = selectRset->columnText("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->columnText("VID");
+        tapeFile.fSeq = selectRset->columnUint64("FSEQ");
+        tapeFile.blockId = selectRset->columnUint64("BLOCK_ID");
+        tapeFile.compressedSize = selectRset->columnUint64("COMPRESSED_SIZE");
+        tapeFile.copyNb = selectRset->columnUint64("COPY_NB");
+        tapeFile.creationTime = selectRset->columnUint64("TAPE_FILE_CREATION_TIME");
+        archiveFile->tapeFiles[selectRset->columnUint64("COPY_NB")] = tapeFile;
+      }
+    }
+
+    if(NULL == archiveFile.get()) {
+      UserError ue;
+      ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because it does not exist";
+      throw ue;
+    }
+
+    {
+      const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;";
+      std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+      stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt->executeNonQuery();
+    }
+
+    {
+      const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;";
+      std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql));
+      stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId);
+      stmt->executeNonQuery();
+    }
+
+    m_conn->commit();
+
+    return *archiveFile;
+  } catch(UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
 //------------------------------------------------------------------------------
 // getNextArchiveFileId
 //------------------------------------------------------------------------------
 uint64_t SqliteCatalogue::getNextArchiveFileId() {
   try {
     m_conn->executeNonQuery("BEGIN EXCLUSIVE;");
+    AutoRollback autoRollback(m_conn.get());
+
     m_conn->executeNonQuery("UPDATE ARCHIVE_FILE_ID SET ID = ID + 1;");
     uint64_t archiveFileId = 0;
     {
diff --git a/catalogue/SqliteCatalogue.hpp b/catalogue/SqliteCatalogue.hpp
index 526b08ec14..be79f94387 100644
--- a/catalogue/SqliteCatalogue.hpp
+++ b/catalogue/SqliteCatalogue.hpp
@@ -63,6 +63,16 @@ public:
    */
   virtual ~SqliteCatalogue();
 
+  /**
+   * Deletes the specified archive file and its associated tape copies from the
+   * catalogue.
+   *
+   * @param archiveFileId The unique identifier of the archive file.
+   * @return The metadata of the deleted archive file including the metadata of
+   * the associated and also deleted tape copies.
+   */
+  virtual common::dataStructures::ArchiveFile deleteArchiveFile(const uint64_t archiveFileId);
+
 protected:
 
   /**
diff --git a/catalogue/SqliteConn.cpp b/catalogue/SqliteConn.cpp
index 1114ffb837..013dee73f6 100644
--- a/catalogue/SqliteConn.cpp
+++ b/catalogue/SqliteConn.cpp
@@ -33,11 +33,24 @@ namespace catalogue {
 SqliteConn::SqliteConn(const std::string &filename) {
   try {
     m_conn = NULL;
-    if (sqlite3_open_v2(filename.c_str(), &m_conn, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, NULL)) {
+    if(sqlite3_open_v2(filename.c_str(), &m_conn, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_URI, NULL)) {
       std::string msg = sqlite3_errmsg(m_conn);
       sqlite3_close(m_conn);
       throw exception::Exception(msg);
     }
+    {
+      char *errMsg = NULL;
+      if(SQLITE_OK != sqlite3_exec(m_conn, "PRAGMA foreign_keys = ON;", NULL, NULL, &errMsg)) {
+        exception::Exception ex;
+        ex.getMessage() << "Failed to to set PRAGMA foreign_keys = ON";
+        if(NULL != errMsg) {
+          ex.getMessage() << ": " << errMsg;
+          sqlite3_free(errMsg);
+        }
+        sqlite3_close(m_conn);
+        throw ex;
+      }
+    }
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
   }
@@ -78,7 +91,7 @@ DbStmt *SqliteConn::createStmt(const std::string &sql) {
       throw exception::Exception(msg);
     }
 
-    return new SqliteStmt(sql, stmt);
+    return new SqliteStmt(*this, sql, stmt);
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + sql + ": " +
       ex.getMessage().str());
diff --git a/catalogue/SqliteConn.hpp b/catalogue/SqliteConn.hpp
index 6ca4b1f1cf..931dfb1287 100644
--- a/catalogue/SqliteConn.hpp
+++ b/catalogue/SqliteConn.hpp
@@ -38,6 +38,12 @@ class SqliteStmt;
 class SqliteConn: public DbConn {
 public:
 
+  /**
+   * The SqliteStmt class can lock the m_mutex member of the SqliteConn class
+   * and it can read the pointer to the SQLite connection.
+   */
+  friend SqliteStmt;
+
   /**
    * Constructor.
    *
diff --git a/catalogue/SqliteStmt.cpp b/catalogue/SqliteStmt.cpp
index 7cb608e963..e1f466d09a 100644
--- a/catalogue/SqliteStmt.cpp
+++ b/catalogue/SqliteStmt.cpp
@@ -32,10 +32,12 @@ namespace catalogue {
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-SqliteStmt::SqliteStmt(const std::string &sql, sqlite3_stmt *const stmt):
+SqliteStmt::SqliteStmt(SqliteConn &conn, const std::string &sql, sqlite3_stmt *const stmt):
+  m_conn(conn),
   m_sql(sql),
   m_paramNameToIdx(sql),
-  m_stmt(stmt) {
+  m_stmt(stmt),
+  m_nbAffectedRows(0) {
 
   if (NULL == stmt) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + sql + ": stmt is NULL");
@@ -116,6 +118,8 @@ DbRset *SqliteStmt::executeQuery() {
 // executeNonQuery
 //------------------------------------------------------------------------------
 void SqliteStmt::executeNonQuery() {
+  std::lock_guard<std::mutex> connLock(m_conn.m_mutex);
+
   const int stepRc = sqlite3_step(m_stmt);
 
   // Throw an exception if the call to sqlite3_step() failed
@@ -124,6 +128,8 @@ void SqliteStmt::executeNonQuery() {
       Sqlite::rcToStr(stepRc));
   }
 
+  m_nbAffectedRows = sqlite3_changes(m_conn.m_conn);
+
   // Throw an exception if the SQL statement returned a result set
   if(SQLITE_ROW == stepRc) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() +
@@ -131,5 +137,12 @@ void SqliteStmt::executeNonQuery() {
   }
 }
 
+//------------------------------------------------------------------------------
+// getNbAffectedRows
+//------------------------------------------------------------------------------
+uint64_t SqliteStmt::getNbAffectedRows() const {
+  return m_nbAffectedRows;
+}
+
 } // namespace catalogue
 } // namespace cta
diff --git a/catalogue/SqliteStmt.hpp b/catalogue/SqliteStmt.hpp
index 3b3ba0cd49..2ce22d5149 100644
--- a/catalogue/SqliteStmt.hpp
+++ b/catalogue/SqliteStmt.hpp
@@ -41,11 +41,12 @@ public:
 
   /**
    * Constructor.
-   *  
+   *
+   * @param conn The database connection.
    * @param sql The SQL statement.
    * @param stmt The prepared statement.
    */
-  SqliteStmt(const std::string &sql, sqlite3_stmt *const stmt);
+  SqliteStmt(SqliteConn &conn, const std::string &sql, sqlite3_stmt *const stmt);
 
   /**
    * Destructor.
@@ -103,6 +104,14 @@ public:
    */
   virtual void executeNonQuery();
 
+  /**
+   * Returns the number of rows affected by the last execution of this
+   * statement.
+   *
+   * @return The number of affected rows.
+   */
+  virtual uint64_t getNbAffectedRows() const;
+
 private:
 
   /**
@@ -110,6 +119,11 @@ private:
    */
   std::mutex m_mutex;
 
+  /**
+   * The SQL connection.
+   */
+  SqliteConn &m_conn;
+
   /**
    * The SQL statement.
    */
@@ -125,6 +139,11 @@ private:
    */
   sqlite3_stmt *m_stmt;
 
+  /**
+   * The number of rows affected by the last execution of this statement.
+   */
+  uint64_t m_nbAffectedRows;
+
 }; // class SqlLiteStmt
 
 } // namespace catalogue
diff --git a/catalogue/sqlite_catalogue_schema_header.sql b/catalogue/sqlite_catalogue_schema_header.sql
index bbb9f67ad2..4bced1513c 100644
--- a/catalogue/sqlite_catalogue_schema_header.sql
+++ b/catalogue/sqlite_catalogue_schema_header.sql
@@ -1,4 +1,3 @@
-PRAGMA foreign_keys = ON;
 CREATE TABLE ARCHIVE_FILE_ID(
   ID INTEGER,
   CONSTRAINT ARCHIVE_FILE_ID_PK PRIMARY KEY(ID)
diff --git a/catalogue/sqlite_catalogue_schema_trailer.sql b/catalogue/sqlite_catalogue_schema_trailer.sql
new file mode 100644
index 0000000000..0be4ee2b60
--- /dev/null
+++ b/catalogue/sqlite_catalogue_schema_trailer.sql
@@ -0,0 +1,2 @@
+.quit
+
diff --git a/scheduler/SchedulerTest.cpp b/scheduler/SchedulerTest.cpp
index 903a4d7fe5..45468a4738 100644
--- a/scheduler/SchedulerTest.cpp
+++ b/scheduler/SchedulerTest.cpp
@@ -169,9 +169,7 @@ TEST_P(SchedulerTest, archive_to_new_file) {
   const std::string userName = "user_name";
   const std::string userGroup = "group";
   cta::common::dataStructures::UserIdentity userIdentity;
-  userIdentity.name=userName;
-  userIdentity.group=userGroup;
-  catalogue.createRequester(s_adminOnAdminHost, userIdentity, mountPolicyName, userComment);
+  catalogue.createRequester(s_adminOnAdminHost, userName, mountPolicyName, userComment);
 
   std::list<common::dataStructures::Requester> users;
   users = catalogue.getRequesters();
diff --git a/xroot_plugins/XrdCtaFile.cpp b/xroot_plugins/XrdCtaFile.cpp
index 03792bdc52..6f0227bc9f 100644
--- a/xroot_plugins/XrdCtaFile.cpp
+++ b/xroot_plugins/XrdCtaFile.cpp
@@ -1125,7 +1125,7 @@ int XrdCtaFile::xCom_user(const std::vector<std::string> &tokens, const cta::com
         m_rc = cta::common::dataStructures::FrontendReturnCode::userErrorNoRetry;        
         return SFS_OK;
       }
-      m_catalogue->createRequester(cliIdentity, userIdentity, mountpolicy, comment);
+      m_catalogue->createRequester(cliIdentity, userIdentity.name, mountpolicy, comment);
     }
     else if("ch" == tokens[2]) { //ch
       std::string mountpolicy = getOptionValue(tokens, "-u", "--mountpolicy", false);
@@ -1143,7 +1143,7 @@ int XrdCtaFile::xCom_user(const std::vector<std::string> &tokens, const cta::com
       }
     }
     else { //rm
-      m_catalogue->deleteRequester(userIdentity);
+      m_catalogue->deleteRequester(userIdentity.name);
     }
   }
   else if("ls" == tokens[2]) { //ls
-- 
GitLab