From e4b1895a2455da1c542f5f6ef230023dc723dcd5 Mon Sep 17 00:00:00 2001
From: Steven Murray <Steven.Murray@cern.ch>
Date: Thu, 5 Dec 2019 12:04:36 +0100
Subject: [PATCH] Added Stmt::bindUint8()

---
 catalogue/CMakeLists.txt       |   4 +
 rdbms/Rset.cpp                 |  15 +++
 rdbms/Rset.hpp                 |  11 +++
 rdbms/Stmt.cpp                 |  32 +++++++
 rdbms/Stmt.hpp                 |  16 ++++
 rdbms/StmtTest.cpp             | 163 +++++++++++++++++++++++++++++++++
 rdbms/wrapper/Mysql.hpp        | 107 ++++++++++++++++++++++
 rdbms/wrapper/MysqlRset.cpp    |  19 ++++
 rdbms/wrapper/MysqlRset.hpp    |  10 ++
 rdbms/wrapper/MysqlStmt.cpp    |  49 ++++++++++
 rdbms/wrapper/MysqlStmt.hpp    |  16 ++++
 rdbms/wrapper/OcciRset.cpp     |  26 ++++++
 rdbms/wrapper/OcciRset.hpp     |  10 ++
 rdbms/wrapper/OcciStmt.cpp     |  24 +++++
 rdbms/wrapper/OcciStmt.hpp     |  16 ++++
 rdbms/wrapper/PostgresRset.cpp |  29 ++++++
 rdbms/wrapper/PostgresRset.hpp |  10 ++
 rdbms/wrapper/PostgresStmt.cpp |  25 +++++
 rdbms/wrapper/PostgresStmt.hpp |  16 ++++
 rdbms/wrapper/RsetWrapper.hpp  |  10 ++
 rdbms/wrapper/SqliteRset.cpp   |  16 ++++
 rdbms/wrapper/SqliteRset.hpp   |  10 ++
 rdbms/wrapper/SqliteStmt.cpp   |  33 +++++++
 rdbms/wrapper/SqliteStmt.hpp   |  16 ++++
 rdbms/wrapper/StmtWrapper.hpp  |  16 ++++
 25 files changed, 699 insertions(+)

diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt
index 4b77cb7163..e053625f0f 100644
--- a/catalogue/CMakeLists.txt
+++ b/catalogue/CMakeLists.txt
@@ -101,6 +101,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
     ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql
     ${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
     ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_trailer.sql
+    | sed 's/UINT8TYPE/INTEGER/g'
     | sed 's/UINT16TYPE/INTEGER/g'
     | sed 's/UINT32TYPE/INTEGER/g'
     | sed 's/UINT64TYPE/INTEGER/g'
@@ -111,6 +112,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
     ${CMAKE_CURRENT_SOURCE_DIR}/mysql_catalogue_schema_header.sql 
     ${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql 
     ${CMAKE_CURRENT_SOURCE_DIR}/mysql_catalogue_schema_trailer.sql
+    | sed 's/UINT8TYPE/TINYINT UNSIGNED/g'
     | sed 's/UINT16TYPE/SMALLINT UNSIGNED/g'
     | sed 's/UINT32TYPE/INT UNSIGNED/g'
     | sed 's/UINT64TYPE/BIGINT UNSIGNED/g'
@@ -121,6 +123,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
     ${CMAKE_CURRENT_SOURCE_DIR}/oracle_catalogue_schema_header.sql
     ${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
     ${CMAKE_CURRENT_SOURCE_DIR}/oracle_catalogue_schema_trailer.sql
+    | sed 's/UINT8TYPE/NUMERIC\(3, 0\)/g'
     | sed 's/UINT16TYPE/NUMERIC\(5, 0\)/g'
     | sed 's/UINT32TYPE/NUMERIC\(10, 0\)/g'
     | sed 's/UINT64TYPE/NUMERIC\(20, 0\)/g'
@@ -131,6 +134,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
     ${CMAKE_CURRENT_SOURCE_DIR}/postgres_catalogue_schema_header.sql
     ${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
     ${CMAKE_CURRENT_SOURCE_DIR}/postgres_catalogue_schema_trailer.sql
+    | sed 's/UINT8TYPE/NUMERIC\(3, 0\)/g'
     | sed 's/UINT16TYPE/NUMERIC\(5, 0\)/g'
     | sed 's/UINT32TYPE/NUMERIC\(10, 0\)/g'
     | sed 's/UINT64TYPE/NUMERIC\(20, 0\)/g'
diff --git a/rdbms/Rset.cpp b/rdbms/Rset.cpp
index 1e62622372..b90497df13 100644
--- a/rdbms/Rset.cpp
+++ b/rdbms/Rset.cpp
@@ -254,6 +254,21 @@ optional<std::string> Rset::columnOptionalString(const std::string &colName) con
   }
 }
 
+//------------------------------------------------------------------------------
+// columnOptionalUint8
+//------------------------------------------------------------------------------
+optional<uint8_t> Rset::columnOptionalUint8(const std::string &colName) const {
+  try {
+    if(nullptr == m_impl) {
+      throw InvalidResultSet("This result set is invalid");
+    }
+    return m_impl->columnOptionalUint8(colName);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // columnOptionalUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/Rset.hpp b/rdbms/Rset.hpp
index 815db48183..dd70e53faf 100644
--- a/rdbms/Rset.hpp
+++ b/rdbms/Rset.hpp
@@ -180,6 +180,17 @@ public:
    */
   bool columnBool(const std::string &colName) const;
 
+  /**
+   * Returns the value of the specified column as an integer.
+   *
+   * This method will return a null column value as an optional with no value.
+   *
+   * @param colName The name of the column.
+   * @return The value of the specified column.
+   * @throw InvalidResultSet if the result is invalid.
+   */
+  optional<uint8_t> columnOptionalUint8(const std::string &colName) const;
+
   /**
    * Returns the value of the specified column as an integer.
    *
diff --git a/rdbms/Stmt.cpp b/rdbms/Stmt.cpp
index 1e576bcf78..cd949b6993 100644
--- a/rdbms/Stmt.cpp
+++ b/rdbms/Stmt.cpp
@@ -116,6 +116,38 @@ uint32_t Stmt::getParamIdx(const std::string &paramName) const {
   }
 }
 
+//-----------------------------------------------------------------------------
+// bindUint8
+//-----------------------------------------------------------------------------
+void Stmt::bindUint8(const std::string &paramName, const uint8_t paramValue) {
+  try {
+    if(nullptr != m_stmt) {
+      return m_stmt->bindUint8(paramName, paramValue);
+    } else {
+      throw exception::Exception("Stmt does not contain a cached statement");
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//-----------------------------------------------------------------------------
+// bindOptionalUint8
+//-----------------------------------------------------------------------------
+void Stmt::bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) {
+  try {
+    if(nullptr != m_stmt) {
+      return m_stmt->bindOptionalUint8(paramName, paramValue);
+    } else {
+      throw exception::Exception("Stmt does not contain a cached statement");
+    }
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //-----------------------------------------------------------------------------
 // bindUint16
 //-----------------------------------------------------------------------------
diff --git a/rdbms/Stmt.hpp b/rdbms/Stmt.hpp
index 8e0c1e2571..46ca376768 100644
--- a/rdbms/Stmt.hpp
+++ b/rdbms/Stmt.hpp
@@ -103,6 +103,22 @@ public:
    */
   uint32_t getParamIdx(const std::string &paramName) const;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindUint8(const std::string &paramName, const uint8_t paramValue);
+
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue);
+
   /**
    * Binds an SQL parameter.
    *
diff --git a/rdbms/StmtTest.cpp b/rdbms/StmtTest.cpp
index 8b4ec4775c..42a58c39ab 100644
--- a/rdbms/StmtTest.cpp
+++ b/rdbms/StmtTest.cpp
@@ -66,6 +66,7 @@ std::string cta_rdbms_StmtTest::getCreateStmtTestTableSql() {
       "CREATE TABLE STMT_TEST("                                      "\n"
       "  ID         UINT64TYPE CONSTRAINT STMT_TEST_ID_NN NOT NULL," "\n"
       "  DOUBLE_COL FLOAT,"                                          "\n"
+      "  UINT8_COL  UINT8TYPE,"                                      "\n"
       "  UINT16_COL UINT16TYPE,"                                     "\n"
       "  UINT32_COL UINT32TYPE,"                                     "\n"
       "  UINT64_COL UINT64TYPE,"                                     "\n"
@@ -79,22 +80,26 @@ std::string cta_rdbms_StmtTest::getCreateStmtTestTableSql() {
     case Login::DBTYPE_IN_MEMORY:
       break;
     case Login::DBTYPE_ORACLE:
+      utils::searchAndReplace(sql, "UINT8TYPE", "NUMERIC(3, 0)");
       utils::searchAndReplace(sql, "UINT16TYPE", "NUMERIC(5, 0)");
       utils::searchAndReplace(sql, "UINT32TYPE", "NUMERIC(10, 0)");
       utils::searchAndReplace(sql, "UINT64TYPE", "NUMERIC(20, 0)");
       utils::searchAndReplace(sql, "VARCHAR", "VARCHAR2");
       break;
     case Login::DBTYPE_SQLITE:
+      utils::searchAndReplace(sql, "UINT8TYPE", "INTEGER");
       utils::searchAndReplace(sql, "UINT16TYPE", "INTEGER");
       utils::searchAndReplace(sql, "UINT32TYPE", "INTEGER");
       utils::searchAndReplace(sql, "UINT64TYPE", "INTEGER");
       break;
     case Login::DBTYPE_MYSQL:
+      utils::searchAndReplace(sql, "UINT8TYPE", "TINYINT UNSIGNED");
       utils::searchAndReplace(sql, "UINT16TYPE", "SMALLINT UNSIGNED");
       utils::searchAndReplace(sql, "UINT32TYPE", "INT UNSIGNED");
       utils::searchAndReplace(sql, "UINT64TYPE", "BIGINT UNSIGNED");
       break;
     case Login::DBTYPE_POSTGRESQL:
+      utils::searchAndReplace(sql, "UINT8TYPE", "NUMERIC(3, 0)");
       utils::searchAndReplace(sql, "UINT16TYPE", "NUMERIC(5, 0)");
       utils::searchAndReplace(sql, "UINT32TYPE", "NUMERIC(10, 0)");
       utils::searchAndReplace(sql, "UINT64TYPE", "NUMERIC(20, 0)");
@@ -173,6 +178,164 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindDouble) {
   }
 }
 
+TEST_P(cta_rdbms_StmtTest, insert_with_bindUint8) {
+  using namespace cta::rdbms;
+
+  const uint8_t insertValue = 123;
+
+  // Insert a row into the test table
+  {
+    const char *const sql =
+      "INSERT INTO STMT_TEST(" "\n"
+      "  ID,"                  "\n"
+      "  UINT8_COL) "         "\n"
+      "VALUES("                "\n"
+      "  1,"                   "\n"
+      "  :UINT8_COL)";
+    auto stmt = m_conn.createStmt(sql);
+    stmt.bindUint8(":UINT8_COL", insertValue);
+    stmt.executeNonQuery();
+  }
+
+  // Select the row back from the table
+  {
+    const char *const sql =
+      "SELECT"                     "\n"
+      "  UINT8_COL AS UINT8_COL" "\n"
+      "FROM"                       "\n"
+      "  STMT_TEST";
+    auto stmt = m_conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    ASSERT_TRUE(rset.next());
+
+    const auto selectValue = rset.columnOptionalUint8("UINT8_COL");
+
+    ASSERT_TRUE((bool)selectValue);
+
+    ASSERT_EQ(insertValue,selectValue.value());
+
+    ASSERT_FALSE(rset.next());
+  }
+}
+
+TEST_P(cta_rdbms_StmtTest, insert_with_bindUint8_2_pow_8_minus_1) {
+  using namespace cta::rdbms;
+
+  const uint8_t insertValue = 255U;
+
+  // Insert a row into the test table
+  {
+    const char *const sql =
+      "INSERT INTO STMT_TEST(" "\n"
+      "  ID,"                  "\n"
+      "  UINT8_COL)"          "\n"
+      "VALUES("                "\n"
+      "  1,"                   "\n"
+      "  :UINT8_COL)";
+    auto stmt = m_conn.createStmt(sql);
+    stmt.bindUint8(":UINT8_COL", insertValue);
+    stmt.executeNonQuery();
+  }
+
+  // Select the row back from the table
+  {
+    const char *const sql =
+      "SELECT"                     "\n"
+      "  UINT8_COL AS UINT8_COL" "\n"
+      "FROM"                       "\n"
+      "  STMT_TEST";
+    auto stmt = m_conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    ASSERT_TRUE(rset.next());
+
+    const auto selectValue = rset.columnOptionalUint8("UINT8_COL");
+
+    ASSERT_TRUE((bool)selectValue);
+
+    ASSERT_EQ(insertValue,selectValue.value());
+
+    ASSERT_FALSE(rset.next());
+  }
+}
+
+TEST_P(cta_rdbms_StmtTest, insert_with_bindOptionalUint8_null) {
+  using namespace cta::rdbms;
+
+  const cta::optional<uint8_t> insertValue; // Null value
+
+  // Insert a row into the test table
+  {
+    const char *const sql =
+      "INSERT INTO STMT_TEST(" "\n"
+      "  ID,"                  "\n"
+      "  UINT8_COL) "         "\n"
+      "VALUES("                "\n"
+      "  1,"                   "\n"
+      "  :UINT8_COL)";
+    auto stmt = m_conn.createStmt(sql);
+    stmt.bindOptionalUint8(":UINT8_COL", insertValue);
+    stmt.executeNonQuery();
+  }
+
+  // Select the row back from the table
+  {
+    const char *const sql =
+      "SELECT"                     "\n"
+      "  UINT8_COL AS UINT8_COL" "\n"
+      "FROM"                       "\n"
+      "  STMT_TEST";
+    auto stmt = m_conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    ASSERT_TRUE(rset.next());
+
+    const auto selectValue = rset.columnOptionalUint8("UINT8_COL");
+
+    ASSERT_FALSE((bool)selectValue);
+
+    ASSERT_FALSE(rset.next());
+  }
+}
+
+TEST_P(cta_rdbms_StmtTest, insert_with_bindOptionalUint8) {
+  using namespace cta::rdbms;
+
+  const cta::optional<uint8_t> insertValue = 123;
+
+  // Insert a row into the test table
+  {
+    const char *const sql =
+      "INSERT INTO STMT_TEST(" "\n"
+      "  ID,"                  "\n"
+      "  UINT8_COL) "         "\n"
+      "VALUES("                "\n"
+      "  1,"                   "\n"
+      "  :UINT8_COL)";
+    auto stmt = m_conn.createStmt(sql);
+    stmt.bindOptionalUint8(":UINT8_COL", insertValue);
+    stmt.executeNonQuery();
+  }
+
+  // Select the row back from the table
+  {
+    const char *const sql =
+      "SELECT"                     "\n"
+      "  UINT8_COL AS UINT8_COL" "\n"
+      "FROM"                       "\n"
+      "  STMT_TEST";
+    auto stmt = m_conn.createStmt(sql);
+    auto rset = stmt.executeQuery();
+    ASSERT_TRUE(rset.next());
+
+    const auto selectValue = rset.columnOptionalUint8("UINT8_COL");
+
+    ASSERT_TRUE((bool)selectValue);
+
+    ASSERT_EQ(insertValue,selectValue.value());
+
+    ASSERT_FALSE(rset.next());
+  }
+}
+
 TEST_P(cta_rdbms_StmtTest, insert_with_bindUint16) {
   using namespace cta::rdbms;
 
diff --git a/rdbms/wrapper/Mysql.hpp b/rdbms/wrapper/Mysql.hpp
index 01b4731a33..98d60d2a5b 100644
--- a/rdbms/wrapper/Mysql.hpp
+++ b/rdbms/wrapper/Mysql.hpp
@@ -98,6 +98,7 @@ class Mysql {
     }
 
     enum buffer_types {
+      placeholder_uint8,
       placeholder_uint16,
       placeholder_uint32,
       placeholder_uint64,
@@ -115,6 +116,7 @@ class Mysql {
     virtual bool get_is_unsigned() = 0;
     virtual my_bool* get_error() { return &error; }
     // following is to access data
+    virtual uint8_t get_uint8() = 0;
     virtual uint16_t get_uint16() = 0;
     virtual uint32_t get_uint32() = 0;
     virtual uint64_t get_uint64() = 0;
@@ -124,6 +126,66 @@ class Mysql {
     virtual bool reset() = 0;
   };
 
+  struct Placeholder_Uint8: Placeholder {
+    uint8_t val;
+
+    Placeholder_Uint8()
+      : Placeholder(), val(0) {
+
+    }
+
+    std::string show() override {
+      std::stringstream ss;
+      ss << "['" << idx << "' '" << is_null << "' '" << length << "' '" << val << "' '" << get_buffer_length() << "']";
+      return ss.str();
+    }
+
+    buffer_types get_buffer_type() override {
+      return placeholder_uint8;
+    }
+
+    void* get_buffer() override {
+      return &val;
+    }
+
+    unsigned long get_buffer_length() override {
+      return sizeof(uint8_t);
+    }
+
+    bool get_is_unsigned() override {
+      return true;
+    }
+
+    uint8_t get_uint8() override {
+      return val;
+    }
+
+    uint16_t get_uint16() override {
+      return val;
+    }
+
+    uint32_t get_uint32() override {
+      return val;
+    }
+
+    uint64_t get_uint64() override {
+      return val;
+    }
+
+    std::string get_string() override {
+      return std::to_string(val);
+    }
+
+    double get_double() override {
+      return val;
+    }
+
+    bool reset() override {
+      return false;
+    }
+
+  };
+
   struct Placeholder_Uint16: Placeholder {
     uint16_t val;
 
@@ -154,6 +216,16 @@ class Mysql {
       return true;
     }
 
+    uint8_t get_uint8() override {
+      if(std::numeric_limits<uint8_t>::max() < val) {
+        std::ostringstream msg;
+        msg << "Cannot convert uint16 to uint8: Overflow: uint16=" << val <<
+          " maxUint8=" << std::numeric_limits<uint8_t>::max();
+        throw exception::Exception(msg.str());
+      }
+      return val;
+    }
+
     uint16_t get_uint16() override {
       return val;
     }
@@ -210,6 +282,16 @@ class Mysql {
       return true;
     }
 
+    uint8_t get_uint8() override {
+      if(std::numeric_limits<uint8_t>::max() < val) {
+        std::ostringstream msg;
+        msg << "Cannot convert uint32 to uint8: Overflow: uint32=" << val <<
+          " maxUint8=" << std::numeric_limits<uint8_t>::max();
+        throw exception::Exception(msg.str());
+      }
+      return val;
+    }
+
     uint16_t get_uint16() override {
       if(std::numeric_limits<uint16_t>::max() < val) {
         std::ostringstream msg;
@@ -272,6 +354,16 @@ class Mysql {
       return true;
     }
 
+    uint8_t get_uint8() override {
+      if(std::numeric_limits<uint8_t>::max() < val) {
+        std::ostringstream msg;
+        msg << "Cannot convert uint64 to uint8: Overflow: uint64=" << val <<
+          " maxUint8=" << std::numeric_limits<uint8_t>::max();
+        throw exception::Exception(msg.str());
+      }
+      return val;
+    }
+
     uint16_t get_uint16() override {
       if(std::numeric_limits<uint16_t>::max() < val) {
         std::ostringstream msg;
@@ -349,6 +441,17 @@ class Mysql {
       return false;
     }
 
+    uint8_t get_uint8() override {
+      const uint64_t i = std::stoll(val);
+      if(std::numeric_limits<uint8_t>::max() > i) {
+        std::ostringstream msg;
+        msg << "Failed to convert string to uint8: Overflow: string=" << val << " maxUint8=" <<
+          std::numeric_limits<uint8_t>::max();
+        throw exception::Exception(msg.str());
+      }
+      return i;
+    }
+
     uint16_t get_uint16() override {
       const uint64_t i = std::stoll(val);
       if(std::numeric_limits<uint16_t>::max() > i) {
@@ -425,6 +528,10 @@ class Mysql {
       return true;
     }
 
+    uint8_t get_uint8() override {
+      throw val;
+    }
+
     uint16_t get_uint16() override {
       throw val;
     }
diff --git a/rdbms/wrapper/MysqlRset.cpp b/rdbms/wrapper/MysqlRset.cpp
index f2d94573e1..5792224cb2 100644
--- a/rdbms/wrapper/MysqlRset.cpp
+++ b/rdbms/wrapper/MysqlRset.cpp
@@ -119,6 +119,25 @@ optional<std::string> MysqlRset::columnOptionalString(const std::string &colName
   return optional<std::string>(holder->get_string());
 }
 
+//------------------------------------------------------------------------------
+// columnOptionalUint8
+//------------------------------------------------------------------------------
+optional<uint8_t> MysqlRset::columnOptionalUint8(const std::string &colName) const {
+  if (not m_fields.exists(colName)) {
+    throw exception::Exception(std::string(__FUNCTION__) + " column does not exist: " + colName);
+    return nullopt;
+  }
+
+  Mysql::Placeholder* holder = m_stmt.columnHolder(colName); // m_holders[idx];
+
+  // the value can be null
+  if (holder->get_is_null() and *holder->get_is_null()) {
+    return nullopt;
+  }
+
+  return optional<uint8_t>(holder->get_uint8());
+}
+
 //------------------------------------------------------------------------------
 // columnOptionalUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/MysqlRset.hpp b/rdbms/wrapper/MysqlRset.hpp
index 4f520c30b7..97a2f0b069 100644
--- a/rdbms/wrapper/MysqlRset.hpp
+++ b/rdbms/wrapper/MysqlRset.hpp
@@ -94,6 +94,16 @@ public:
    */
   optional<std::string> columnOptionalString(const std::string &colName) const override;
 
+  /**
+   * Returns the value of the specified column as an integer.
+   *
+   * This method will return a null column value as an optional with no value.
+   *
+   * @param colName The name of the column.
+   * @return The value of the specified column.
+   */
+  optional<uint8_t> columnOptionalUint8(const std::string &colName) const override;
+
   /**
    * Returns the value of the specified column as an integer.
    *
diff --git a/rdbms/wrapper/MysqlStmt.cpp b/rdbms/wrapper/MysqlStmt.cpp
index bb76b13b61..3bb54a9619 100644
--- a/rdbms/wrapper/MysqlStmt.cpp
+++ b/rdbms/wrapper/MysqlStmt.cpp
@@ -170,6 +170,52 @@ MYSQL_STMT *MysqlStmt::get() const {
   return m_stmt;
 }
 
+//------------------------------------------------------------------------------
+// bindUint8
+//------------------------------------------------------------------------------
+void MysqlStmt::bindUint8(const std::string &paramName, const uint8_t paramValue) {
+  try {
+    bindOptionalUint8(paramName, paramValue);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// bindOptionalUint8
+//------------------------------------------------------------------------------
+void MysqlStmt::bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) {
+  try {
+    const unsigned int paramIdx = getParamIdx(paramName); // starts from 1.
+    const unsigned int idx = paramIdx - 1;
+
+    Mysql::Placeholder_Uint8* holder = dynamic_cast<Mysql::Placeholder_Uint8*>(m_placeholder[idx]);
+    // if already exists, try to reuse
+    if (!holder and m_placeholder[idx]) {
+      throw exception::Exception(std::string(__FUNCTION__) + " can't cast from Placeholder to Placeholder_Uint16. " );
+    }
+
+    if (!holder) {
+      holder = new Mysql::Placeholder_Uint8();
+      holder->idx = idx;
+      holder->length = sizeof(uint8_t);
+    }
+
+    if (paramValue) {
+      holder->val = paramValue.value();
+    } else {
+      holder->is_null = true;
+    }
+    // delete m_placeholder[idx]; // remove the previous placeholder
+
+    m_placeholder[idx] = holder;
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
+                               getSqlForException() + ": " + ex.getMessage().str());
+  }
+}
+
 //------------------------------------------------------------------------------
 // bindUint16
 //------------------------------------------------------------------------------
@@ -536,6 +582,9 @@ bool MysqlStmt::do_bind() {
     }
 
     switch(holder->get_buffer_type()) {
+    case Mysql::Placeholder::placeholder_uint8:
+      bind[idx].buffer_type = MYSQL_TYPE_TINY;
+      break;
     case Mysql::Placeholder::placeholder_uint16:
       bind[idx].buffer_type = MYSQL_TYPE_SHORT;
       break;
diff --git a/rdbms/wrapper/MysqlStmt.hpp b/rdbms/wrapper/MysqlStmt.hpp
index ea12143f3b..fd8842df28 100644
--- a/rdbms/wrapper/MysqlStmt.hpp
+++ b/rdbms/wrapper/MysqlStmt.hpp
@@ -84,6 +84,22 @@ public:
    */
   MYSQL_STMT *get() const;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindUint8(const std::string &paramName, const uint8_t paramValue) override;
+
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) override;
+
   /**
    * Binds an SQL parameter.
    *
diff --git a/rdbms/wrapper/OcciRset.cpp b/rdbms/wrapper/OcciRset.cpp
index 6094d1bdd8..d0da2c0d7d 100644
--- a/rdbms/wrapper/OcciRset.cpp
+++ b/rdbms/wrapper/OcciRset.cpp
@@ -162,6 +162,32 @@ optional<std::string> OcciRset::columnOptionalString(const std::string &colName)
   }
 }
 
+//------------------------------------------------------------------------------
+// columnOptionalUint8
+//------------------------------------------------------------------------------
+optional<uint8_t> OcciRset::columnOptionalUint8(const std::string &colName) const {
+  try {
+    threading::Mutex locker(m_mutex);
+
+    const int colIdx = m_colNameToIdx.getIdx(colName);
+    const std::string stringValue = m_rset->getString(colIdx);
+    if(stringValue.empty()) {
+      return nullopt;
+    }
+    if(!utils::isValidUInt(stringValue)) {
+      throw exception::Exception(std::string("Column ") + colName + " contains the value " + stringValue +
+                                 " which is not a valid unsigned integer");
+    }
+    return utils::toUint8(stringValue);
+  } catch(exception::Exception &ne) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + m_stmt.getSql() + ": " +
+                               ne.getMessage().str());
+  } catch(std::exception &se) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + m_stmt.getSql() + ": " +
+                               se.what());
+  }
+}
+
 //------------------------------------------------------------------------------
 // columnOptionalUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/OcciRset.hpp b/rdbms/wrapper/OcciRset.hpp
index 2725658fbf..f567902b2a 100644
--- a/rdbms/wrapper/OcciRset.hpp
+++ b/rdbms/wrapper/OcciRset.hpp
@@ -98,6 +98,16 @@ public:
    */
   optional<std::string> columnOptionalString(const std::string &colName) const override;
 
+  /**
+   * Returns the value of the specified column as an integer.
+   *
+   * This method will return a null column value as an optional with no value.
+   *
+   * @param colName The name of the column.
+   * @return The value of the specified column.
+   */
+  optional<uint8_t> columnOptionalUint8(const std::string &colName) const override;
+
   /**
    * Returns the value of the specified column as an integer.
    *
diff --git a/rdbms/wrapper/OcciStmt.cpp b/rdbms/wrapper/OcciStmt.cpp
index 46034805f2..e8e5e1bb11 100644
--- a/rdbms/wrapper/OcciStmt.cpp
+++ b/rdbms/wrapper/OcciStmt.cpp
@@ -87,6 +87,30 @@ void OcciStmt::close() {
   }
 }
 
+//------------------------------------------------------------------------------
+// bindUint8
+//------------------------------------------------------------------------------
+void OcciStmt::bindUint8(const std::string &paramName, const uint8_t paramValue) {
+  try {
+    bindOptionalUint8(paramName, paramValue);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// bindOptionalUint8
+//------------------------------------------------------------------------------
+void OcciStmt::bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) {
+  try {
+    return bindOptionalInteger<uint8_t>(paramName, paramValue);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // bindUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/OcciStmt.hpp b/rdbms/wrapper/OcciStmt.hpp
index 08919b2b9c..4b73c4ca6c 100644
--- a/rdbms/wrapper/OcciStmt.hpp
+++ b/rdbms/wrapper/OcciStmt.hpp
@@ -81,6 +81,22 @@ public:
    */
   void close() override;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindUint8(const std::string &paramName, const uint8_t paramValue) override;
+
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) override;
+
   /**
    * Binds an SQL parameter.
    *
diff --git a/rdbms/wrapper/PostgresRset.cpp b/rdbms/wrapper/PostgresRset.cpp
index eab066f0ea..ab9024e713 100644
--- a/rdbms/wrapper/PostgresRset.cpp
+++ b/rdbms/wrapper/PostgresRset.cpp
@@ -109,6 +109,35 @@ optional<std::string> PostgresRset::columnOptionalString(const std::string &colN
   return optional<std::string>(PQgetvalue(m_resItr->get(), 0, ifield));
 }
 
+//------------------------------------------------------------------------------
+// columnOptionalUint8
+//------------------------------------------------------------------------------
+optional<uint8_t> PostgresRset::columnOptionalUint8(const std::string &colName) const {
+
+  if (nullptr == m_resItr->get()) {
+    throw exception::Exception(std::string(__FUNCTION__) + " no row available");
+  }
+
+  const int ifield = PQfnumber(m_resItr->get(), colName.c_str());
+  if (ifield < 0) {
+    throw exception::Exception(std::string(__FUNCTION__) + " column does not exist: " + colName);
+  }
+
+  // the value can be null
+  if (PQgetisnull(m_resItr->get(), 0, ifield)) {
+    return nullopt;
+  }
+
+  const std::string stringValue(PQgetvalue(m_resItr->get(), 0, ifield));
+
+  if(!utils::isValidUInt(stringValue)) {
+    throw exception::Exception(std::string("Column ") + colName + " contains the value " + stringValue +
+                               " which is not a valid unsigned integer");
+  }
+
+  return utils::toUint8(stringValue);
+}
+
 //------------------------------------------------------------------------------
 // columnOptionalUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/PostgresRset.hpp b/rdbms/wrapper/PostgresRset.hpp
index 9d79c46de3..024312394b 100644
--- a/rdbms/wrapper/PostgresRset.hpp
+++ b/rdbms/wrapper/PostgresRset.hpp
@@ -78,6 +78,16 @@ public:
    */
   optional<std::string> columnOptionalString(const std::string &colName) const override;
 
+  /**
+   * Returns the value of the specified column as an integer.
+   *
+   * This method will return a null column value as an optional with no value.
+   *
+   * @param colName The name of the column.
+   * @return The value of the specified column.
+   */
+  optional<uint8_t> columnOptionalUint8(const std::string &colName) const override;
+
   /**
    * Returns the value of the specified column as an integer.
    *
diff --git a/rdbms/wrapper/PostgresStmt.cpp b/rdbms/wrapper/PostgresStmt.cpp
index dee2dcfc31..745c6d3576 100644
--- a/rdbms/wrapper/PostgresStmt.cpp
+++ b/rdbms/wrapper/PostgresStmt.cpp
@@ -102,6 +102,19 @@ void PostgresStmt::bindOptionalString(const std::string &paramName, const option
   }
 }
 
+//------------------------------------------------------------------------------
+// bindOptionalUint8
+//------------------------------------------------------------------------------
+void PostgresStmt::bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) {
+  try {
+    return bindOptionalInteger<uint8_t>(paramName, paramValue);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed for SQL statement " +
+                        getSqlForException() + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // bindOptionalUint16
 //------------------------------------------------------------------------------
@@ -156,6 +169,18 @@ void PostgresStmt::bindString(const std::string &paramName, const std::string &p
   }
 }
 
+//------------------------------------------------------------------------------
+// bindUint8
+//------------------------------------------------------------------------------
+void PostgresStmt::bindUint8(const std::string &paramName, const uint8_t paramValue) {
+  try {
+    bindOptionalUint8(paramName, paramValue);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // bindUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/PostgresStmt.hpp b/rdbms/wrapper/PostgresStmt.hpp
index 7055487134..26d167aa0a 100644
--- a/rdbms/wrapper/PostgresStmt.hpp
+++ b/rdbms/wrapper/PostgresStmt.hpp
@@ -76,6 +76,14 @@ public:
    */ 
   void bindOptionalString(const std::string &paramName, const optional<std::string> &paramValue) override;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) override;
+
   /**
    * Binds an SQL parameter.
    *
@@ -120,6 +128,14 @@ public:
    */ 
   void bindString(const std::string &paramName, const std::string &paramValue) override;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindUint8(const std::string &paramName, const uint8_t paramValue) override;
+
   /**
    * Binds an SQL parameter.
    *
diff --git a/rdbms/wrapper/RsetWrapper.hpp b/rdbms/wrapper/RsetWrapper.hpp
index a8963fb165..cfec4e27a3 100644
--- a/rdbms/wrapper/RsetWrapper.hpp
+++ b/rdbms/wrapper/RsetWrapper.hpp
@@ -80,6 +80,16 @@ public:
    */
   virtual optional<std::string> columnOptionalString(const std::string &colName) const = 0;
 
+  /**
+   * Returns the value of the specified column as an integer.
+   *
+   * This method will return a null column value as an optional with no value.
+   *
+   * @param colName The name of the column.
+   * @return The value of the specified column.
+   */
+  virtual optional<uint8_t> columnOptionalUint8(const std::string &colName) const = 0;
+
   /**
    * Returns the value of the specified column as an integer.
    *
diff --git a/rdbms/wrapper/SqliteRset.cpp b/rdbms/wrapper/SqliteRset.cpp
index 1e5b367ff2..ea7a551c0e 100644
--- a/rdbms/wrapper/SqliteRset.cpp
+++ b/rdbms/wrapper/SqliteRset.cpp
@@ -256,6 +256,22 @@ optional<std::string> SqliteRset::columnOptionalString(const std::string &colNam
   }
 }
 
+//------------------------------------------------------------------------------
+// columnOptionalUint8
+//------------------------------------------------------------------------------
+optional<uint8_t> SqliteRset::columnOptionalUint8(const std::string &colName) const {
+  try {
+    const ColumnNameToIdxAndType::IdxAndType idxAndType = m_colNameToIdxAndType.getIdxAndType(colName);
+    if(SQLITE_NULL == idxAndType.colType) {
+      return nullopt;
+    } else {
+      return optional<uint8_t>(sqlite3_column_int(m_stmt.get(), idxAndType.colIdx));
+    }
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+  }
+}
+
 //------------------------------------------------------------------------------
 // columnOptionalUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/SqliteRset.hpp b/rdbms/wrapper/SqliteRset.hpp
index 24667a5911..0a0bed7e6d 100644
--- a/rdbms/wrapper/SqliteRset.hpp
+++ b/rdbms/wrapper/SqliteRset.hpp
@@ -93,6 +93,16 @@ public:
    */
   optional<std::string> columnOptionalString(const std::string &colName) const override;
 
+  /**
+   * Returns the value of the specified column as an integer.
+   *
+   * This method will return a null column value as an optional with no value.
+   *
+   * @param colName The name of the column.
+   * @return The value of the specified column.
+   */
+  optional<uint8_t> columnOptionalUint8(const std::string &colName) const override;
+
   /**
    * Returns the value of the specified column as an integer.
    *
diff --git a/rdbms/wrapper/SqliteStmt.cpp b/rdbms/wrapper/SqliteStmt.cpp
index ae64ccbf04..305214eace 100644
--- a/rdbms/wrapper/SqliteStmt.cpp
+++ b/rdbms/wrapper/SqliteStmt.cpp
@@ -149,6 +149,39 @@ sqlite3_stmt *SqliteStmt::get() const {
   return m_stmt;
 }
 
+//------------------------------------------------------------------------------
+// bindUint8
+//------------------------------------------------------------------------------
+void SqliteStmt::bindUint8(const std::string &paramName, const uint8_t paramValue) {
+  try {
+    bindOptionalUint8(paramName, paramValue);
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// bindOptionalUint8
+//------------------------------------------------------------------------------
+void SqliteStmt::bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) {
+  try {
+    const unsigned int paramIdx = getParamIdx(paramName);
+    int bindRc = 0;
+    if(paramValue) {
+      bindRc = sqlite3_bind_int(m_stmt, paramIdx, paramValue.value());
+    } else {
+      bindRc = sqlite3_bind_null(m_stmt, paramIdx);
+    }
+    if(SQLITE_OK != bindRc) {
+      throw exception::Exception(Sqlite::rcToStr(bindRc));
+    }
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
+                               getSqlForException() + ": " + ex.getMessage().str());
+  }
+}
+
 //------------------------------------------------------------------------------
 // bindUint16
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/SqliteStmt.hpp b/rdbms/wrapper/SqliteStmt.hpp
index 3d19b8c8f8..3213cd090b 100644
--- a/rdbms/wrapper/SqliteStmt.hpp
+++ b/rdbms/wrapper/SqliteStmt.hpp
@@ -75,6 +75,22 @@ public:
    */
   sqlite3_stmt *get() const;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindUint8(const std::string &paramName, const uint8_t paramValue) override;
+
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  void bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) override;
+
   /**
    * Binds an SQL parameter.
    *
diff --git a/rdbms/wrapper/StmtWrapper.hpp b/rdbms/wrapper/StmtWrapper.hpp
index 45af44db86..f5c54ebd60 100644
--- a/rdbms/wrapper/StmtWrapper.hpp
+++ b/rdbms/wrapper/StmtWrapper.hpp
@@ -95,6 +95,22 @@ public:
    */
   uint32_t getParamIdx(const std::string &paramName) const;
 
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  virtual void bindUint8(const std::string &paramName, const uint8_t paramValue) = 0;
+
+  /**
+   * Binds an SQL parameter.
+   *
+   * @param paramName The name of the parameter.
+   * @param paramValue The value to be bound.
+   */
+  virtual void bindOptionalUint8(const std::string &paramName, const optional<uint8_t> &paramValue) = 0;
+
   /**
    * Binds an SQL parameter.
    *
-- 
GitLab