From 0f11e728a3d127d18761c3bfa1174bb50e8a4ebf Mon Sep 17 00:00:00 2001
From: Steven Murray <Steven.Murray@cern.ch>
Date: Tue, 7 May 2019 00:14:12 +0200
Subject: [PATCH] cta/CTA#490 Deleting non empty pool gives unfriendly message:

Fixed.
---
 catalogue/CatalogueTest.cpp  | 85 ++++++++++++++++++++++++++++++++++++
 catalogue/RdbmsCatalogue.cpp | 44 ++++++++++++++++---
 catalogue/RdbmsCatalogue.hpp | 11 +++++
 3 files changed, 134 insertions(+), 6 deletions(-)

diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index bfed77f490..fbe77e6b42 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -879,6 +879,91 @@ TEST_P(cta_catalogue_CatalogueTest, deleteTapePool) {
   ASSERT_TRUE(m_catalogue->getTapePools().empty());
 }
 
+TEST_P(cta_catalogue_CatalogueTest, deleteTapePool_notEmpty) {
+  using namespace cta;
+
+  ASSERT_TRUE(m_catalogue->getTapes().empty());
+
+  const std::string vid = "vid";
+
+  ASSERT_FALSE(m_catalogue->tapeExists(vid));
+
+  const std::string mediaType = "media_type";
+  const std::string vendor = "vendor";
+  const std::string logicalLibraryName = "logical_library_name";
+  const std::string tapePoolName = "tape_pool_name";
+  const std::string vo = "vo";
+  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_admin, logicalLibraryName,
+    "Create logical library");
+
+  m_catalogue->createTapePool(m_admin, tapePoolName, vo, 2, true, "Create tape pool");
+  {
+    const auto pools = m_catalogue->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(vo, pool.vo);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  m_catalogue->createTape(m_admin, vid, mediaType, vendor, logicalLibraryName, tapePoolName, capacityInBytes,
+    disabledValue, fullValue, comment);
+
+  ASSERT_TRUE(m_catalogue->tapeExists(vid));
+
+  const auto tapes = m_catalogue->getTapes();
+
+  ASSERT_EQ(1, tapes.size());
+
+  {
+    const auto tape = tapes.front();
+    ASSERT_EQ(vid, tape.vid);
+    ASSERT_EQ(mediaType, tape.mediaType);
+    ASSERT_EQ(vendor, tape.vendor);
+    ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(vo, tape.vo);
+    ASSERT_EQ(capacityInBytes, tape.capacityInBytes);
+    ASSERT_TRUE(disabledValue == tape.disabled);
+    ASSERT_TRUE(fullValue == tape.full);
+    ASSERT_EQ(comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const auto creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const auto lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+  }
+
+  {
+    const auto pools = m_catalogue->getTapePools();
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(vo, pool.vo);
+    ASSERT_EQ(1, pool.nbTapes);
+    ASSERT_EQ(capacityInBytes, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+  }
+
+  ASSERT_THROW(m_catalogue->deleteTapePool(tapePoolName), exception::UserError);
+}
+
 TEST_P(cta_catalogue_CatalogueTest, createTapePool_emptyStringTapePoolName) {
   using namespace cta;
       
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 461cb707c7..fadc2e849c 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -858,14 +858,20 @@ bool RdbmsCatalogue::archiveRouteExists(rdbms::Conn &conn, const std::string &di
 //------------------------------------------------------------------------------
 void RdbmsCatalogue::deleteTapePool(const std::string &name) {
   try {
-    const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
     auto conn = m_connPool.getConn();
-    auto stmt = conn.createStmt(sql);
-    stmt.bindString(":TAPE_POOL_NAME", name);
-    stmt.executeNonQuery();
+    const uint64_t nbTapesInPool = getNbTapesInPool(conn, name);
 
-    if(0 == stmt.getNbAffectedRows()) {
-      throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
+    if(0 == nbTapesInPool) {
+      const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME";
+      auto stmt = conn.createStmt(sql);
+      stmt.bindString(":TAPE_POOL_NAME", name);
+      stmt.executeNonQuery();
+
+      if(0 == stmt.getNbAffectedRows()) {
+        throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist");
+      }
+    } else {
+      throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it is not empty");
     }
   } catch(exception::UserError &) {
     throw;
@@ -5338,6 +5344,32 @@ void RdbmsCatalogue::checkTapeItemWrittenFieldsAreSet(const std::string& calling
   }
 }
 
+//------------------------------------------------------------------------------
+// getNbTapesInPool
+//------------------------------------------------------------------------------
+uint64_t RdbmsCatalogue::getNbTapesInPool(rdbms::Conn &conn, const std::string &name) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "COUNT(*) AS NB_TAPES "
+      "FROM "
+        "TAPE "
+      "WHERE "
+        "TAPE.TAPE_POOL_NAME = :TAPE_POOL_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":TAPE_POOL_NAME", name);
+    auto rset = stmt.executeQuery();
+    if(!rset.next()) {
+      throw exception::Exception("Result set of SELECT COUNT(*) is empty");
+    }
+    return rset.columnUint64("NB_TAPES");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
 
 } // namespace catalogue
 } // namespace cta
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index b08176540d..902fd78689 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -1209,6 +1209,17 @@ protected:
    */
   bool isNonCachedAdmin(const common::dataStructures::SecurityIdentity &admin) const;
 
+  /**
+   * Returns the number of tapes in the specified tape pool.
+   *
+   * If the tape pool does not exist then this method returns 0.
+   *
+   * @param conn The database connection.
+   * @param name The name of the tape pool.
+   * @return The number of tapes in the specified tape pool.
+   */
+  uint64_t getNbTapesInPool(rdbms::Conn &conn, const std::string &name) const;
+
   /**
    * Cached versions of tape copy to tape tape pool mappings for specific
    * storage classes.
-- 
GitLab