From 6728bc280eff55088343315fdc7d78e431630e42 Mon Sep 17 00:00:00 2001
From: Victor Kotlyar <Victor.Kotlyar@cern.ch>
Date: Fri, 21 Jun 2019 10:42:42 +0200
Subject: [PATCH] Add verifyColumns to the cta-catalogue-schema-verify

---
 catalogue/CatalogueSchema.cpp |  2 +-
 catalogue/VerifySchemaCmd.cpp | 66 ++++++++++++++++++++++++++++++-----
 catalogue/VerifySchemaCmd.hpp | 18 ++++++++++
 3 files changed, 76 insertions(+), 10 deletions(-)

diff --git a/catalogue/CatalogueSchema.cpp b/catalogue/CatalogueSchema.cpp
index 02a1cbfd68..fe4573b81b 100644
--- a/catalogue/CatalogueSchema.cpp
+++ b/catalogue/CatalogueSchema.cpp
@@ -58,7 +58,7 @@ std::map<std::string, std::string> CatalogueSchema::getSchemaColumns(const std::
       searchPos = findResult + 1;
 
       if(0 < sqlStmt.size()) { // Ignore empty statements
-        const std::string createTableSQL = "CREATE TABLE " + tableName + "\\(([a-zA-Z0-9_, \\)\\(]+)\\)";
+        const std::string createTableSQL = "CREATE TABLE " + tableName + "\\(([a-zA-Z0-9_, '\\)\\(]+)\\)";
         cta::utils::Regex tableSqlRegex(createTableSQL.c_str());
         auto tableSql = tableSqlRegex.exec(sqlStmt);
         if (2 == tableSql.size()) {
diff --git a/catalogue/VerifySchemaCmd.cpp b/catalogue/VerifySchemaCmd.cpp
index a049137f82..56425918c6 100644
--- a/catalogue/VerifySchemaCmd.cpp
+++ b/catalogue/VerifySchemaCmd.cpp
@@ -116,14 +116,8 @@ int VerifySchemaCmd::exceptionThrowingMain(const int argc, char *const *const ar
   const auto dbTableNames = conn.getTableNames();
   const VerifyStatus verifyTablesStatus = verifyTableNames(schemaTableNames, dbTableNames);
   
-  for (const auto &table: schemaTableNames) {
-    std::cerr << table << std::endl;
-    const auto columns = schema->getSchemaColumns(table);
-    const auto dbColumns = conn.getColumns(table);
-    for (const auto &column : dbColumns) {
-      std::cerr << "  " << column.first << " "  << column.second << std::endl;
-    }  
-  }
+  std::cerr << "Checking columns in tables..." << std::endl;
+  const VerifyStatus verifyColumnsStatus = verifyColumns(schemaTableNames, dbTableNames, *schema, conn);
 
   std::cerr << "Checking index names..." << std::endl;
   const auto schemaIndexNames = schema->getSchemaIndexNames();
@@ -144,7 +138,8 @@ int VerifySchemaCmd::exceptionThrowingMain(const int argc, char *const *const ar
       verifyTablesStatus    == VerifyStatus::ERROR || 
       verifyIndexesStatus   == VerifyStatus::ERROR ||
       verifySequencesStatus == VerifyStatus::ERROR || 
-      verifyTriggersStatus  == VerifyStatus::ERROR ) {
+      verifyTriggersStatus  == VerifyStatus::ERROR ||
+      verifyColumnsStatus   == VerifyStatus::ERROR ) {
     return 1;
   }
   return 0;
@@ -202,6 +197,59 @@ VerifySchemaCmd::VerifyStatus VerifySchemaCmd::verifySchemaVersion(const std::ma
   }
 }
 
+//------------------------------------------------------------------------------
+// verifyColumns
+//------------------------------------------------------------------------------
+VerifySchemaCmd::VerifyStatus VerifySchemaCmd::verifyColumns(const std::list<std::string> &schemaTableNames,
+  const std::list<std::string> &dbTableNames, const CatalogueSchema &schema,
+  const rdbms::Conn &conn) const {
+  try {
+    VerifyStatus status = VerifyStatus::UNKNOWN;
+    for(auto &tableName : schemaTableNames) {
+      const bool schemaTableIsInDb = dbTableNames.end() != std::find(dbTableNames.begin(), dbTableNames.end(), tableName);
+      if (schemaTableIsInDb) {
+        const auto schemaColumns = schema.getSchemaColumns(tableName);
+        const auto dbColumns = conn.getColumns(tableName);
+        // first check database columns are present the schema catalogue
+        for (const auto &dbColumn : dbColumns) {
+          if (!schemaColumns.count(dbColumn.first)) {
+            std::cerr << "  ERROR: the DB column " << dbColumn.first
+                      <<" not found in the catalogue schema" << std::endl;
+            status = VerifyStatus::ERROR;
+          }
+        }
+        // second check schema columns against the database catalogue
+        for (const auto &schemaColumn : schemaColumns) {
+            if (dbColumns.count(schemaColumn.first)) {
+            if (schemaColumn.second != dbColumns.at(schemaColumn.first)) {
+              std::cerr << "  ERROR: type mismatch for the column: DB[" 
+                        << schemaColumn.first << "] = "  << dbColumns.at(schemaColumn.first) 
+                        << ", SCHEMA[" << schemaColumn.first << "] = " 
+                        << schemaColumn.second << std::endl;
+              status = VerifyStatus::ERROR;
+            }
+          } else {
+            std::cerr << "  ERROR: the schema column " << schemaColumn.first 
+                      <<" not found in the DB" << std::endl;
+            status = VerifyStatus::ERROR;
+          }
+        }
+      } else {
+        std::cerr << "  ERROR: the schema table " << tableName 
+                  <<" not found in the DB" << std::endl;
+        status = VerifyStatus::ERROR; 
+      }
+    }
+    if (status != VerifyStatus::INFO && status != VerifyStatus::ERROR) {
+      std::cerr << "  OK" << std::endl;
+      status = VerifyStatus::OK;
+    }
+    return status;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
 //------------------------------------------------------------------------------
 // verifyTableNames
 //------------------------------------------------------------------------------
diff --git a/catalogue/VerifySchemaCmd.hpp b/catalogue/VerifySchemaCmd.hpp
index e65fb49f3d..509015f299 100644
--- a/catalogue/VerifySchemaCmd.hpp
+++ b/catalogue/VerifySchemaCmd.hpp
@@ -19,6 +19,7 @@
 #pragma once
 
 #include "catalogue/CmdLineTool.hpp"
+#include "catalogue/CatalogueSchema.hpp"
 #include "rdbms/Conn.hpp"
 
 namespace cta {
@@ -86,6 +87,23 @@ private:
   VerifyStatus verifySchemaVersion(const std::map<std::string, uint64_t> &schemaVersion, 
     const std::map<std::string, uint64_t> &schemaDbVersion) const;
   
+  /**
+   * Verifies columns in the database according their description in
+   * the catalogue schema. This method verifies tables in the database which
+   * are present in the catalogue schema and also checks if some tables are
+   * missed in the database.
+   * Returns verification status as result.
+   * 
+   * @param schemaTableNames The list of the catalogue schema table names.
+   * @param dbTableNames The list of the database table names.
+   * @param schema The reference to the catalogue schema.
+   * @param conn The reference to the database connection.
+   * @return The verification status code.
+   */
+  VerifyStatus verifyColumns(const std::list<std::string> &schemaTableNames, 
+    const std::list<std::string> &dbTableNames, const CatalogueSchema &schema,
+    const rdbms::Conn &conn) const;
+  
   /**
    * Verifies table names in the database against the catalogue schema table names.
    * Returns verification status as result.
-- 
GitLab