From 21487fe740d748293b5e7d2c60d76ceac730f083 Mon Sep 17 00:00:00 2001
From: Steven Murray <steven.murray@cern.ch>
Date: Mon, 9 Jan 2017 22:59:48 +0100
Subject: [PATCH] Added Stmt::getSqlForException()

---
 rdbms/OcciStmt.cpp   | 30 +++++++++++-------------------
 rdbms/OcciStmt.hpp   | 12 ------------
 rdbms/SqliteConn.cpp |  3 +--
 rdbms/SqliteStmt.cpp | 37 +++++++++++++++----------------------
 rdbms/SqliteStmt.hpp | 12 ------------
 rdbms/Stmt.cpp       | 26 +++++++++++++++++++++++++-
 rdbms/Stmt.hpp       | 22 ++++++++++++++++++++--
 7 files changed, 72 insertions(+), 70 deletions(-)

diff --git a/rdbms/OcciStmt.cpp b/rdbms/OcciStmt.cpp
index caab6da43d..b43403ae38 100644
--- a/rdbms/OcciStmt.cpp
+++ b/rdbms/OcciStmt.cpp
@@ -38,8 +38,7 @@ OcciStmt::OcciStmt(
   const std::string &sql,
   OcciConn &conn,
   oracle::occi::Statement *const stmt) :
-  Stmt(autocommitMode),
-  m_sql(sql),
+  Stmt(sql, autocommitMode),
   m_paramNameToIdx(sql),
   m_conn(conn),
   m_stmt(stmt) {
@@ -60,11 +59,11 @@ OcciStmt::OcciStmt(
   } catch(exception::Exception &ex) {
     close();
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      sql.substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str());
+      getSqlForException() + ": " + ex.getMessage().str());
   } catch(std::exception &se) {
     close();
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      sql.substr(0, c_maxSqlLenInExceptions) + ": " + se.what());
+      getSqlForException() + ": " + se.what());
   }
 }
 
@@ -92,20 +91,13 @@ void OcciStmt::close() {
     }
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str());
+      getSqlForException() + ": " + ex.getMessage().str());
   } catch(std::exception &se) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + se.what());
+      getSqlForException() + ": " + se.what());
   }
 }
 
-//------------------------------------------------------------------------------
-// getSql
-//------------------------------------------------------------------------------
-const std::string &OcciStmt::getSql() const {
-  return m_sql;
-}
-
 //------------------------------------------------------------------------------
 // bindUint64
 //------------------------------------------------------------------------------
@@ -131,10 +123,10 @@ void OcciStmt::bindOptionalUint64(const std::string &paramName, const optional<u
     }
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str());
+      getSqlForException() + ": " + ex.getMessage().str());
   } catch(std::exception &se) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + se.what());
+      getSqlForException() + ": " + se.what());
   }
 }
 
@@ -167,10 +159,10 @@ void OcciStmt::bindOptionalString(const std::string &paramName, const optional<s
     }
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str());
+      getSqlForException() + ": " + ex.getMessage().str());
   } catch(std::exception &se) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + se.what());
+      getSqlForException() + ": " + se.what());
   }
 }
 
@@ -196,7 +188,7 @@ std::unique_ptr<Rset> OcciStmt::executeQuery() {
       }
     }
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.what());
+      getSqlForException() + ": " + ex.what());
   }
 }
 
@@ -222,7 +214,7 @@ void OcciStmt::executeNonQuery() {
       }
     }
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.what());
+      getSqlForException() + ": " + ex.what());
   }
 }
 
diff --git a/rdbms/OcciStmt.hpp b/rdbms/OcciStmt.hpp
index 4dc223610a..66208ef221 100644
--- a/rdbms/OcciStmt.hpp
+++ b/rdbms/OcciStmt.hpp
@@ -76,13 +76,6 @@ public:
    */
   virtual void close() override;
 
-  /**
-   * Returns the SQL statement.
-   *
-   * @return The SQL statement.
-   */
-  virtual const std::string &getSql() const override;
-
   /**
    * Binds an SQL parameter.
    *
@@ -165,11 +158,6 @@ private:
    */
   std::mutex m_mutex;
 
-  /**
-   * The SQL statement.
-   */
-  std::string m_sql;
-
   /**
    * Map from SQL parameter name to parameter index.
    */
diff --git a/rdbms/SqliteConn.cpp b/rdbms/SqliteConn.cpp
index 2af7829764..246f13066d 100644
--- a/rdbms/SqliteConn.cpp
+++ b/rdbms/SqliteConn.cpp
@@ -89,8 +89,7 @@ std::unique_ptr<Stmt> SqliteConn::createStmt(const std::string &sql, const Stmt:
 
     return cta::make_unique<SqliteStmt>(autocommitMode , *this, sql);
   } catch(exception::Exception &ex) {
-    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + sql + ": " +
-      ex.getMessage().str());
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
   }
 }
 
diff --git a/rdbms/SqliteStmt.cpp b/rdbms/SqliteStmt.cpp
index 063643b10f..481e84e112 100644
--- a/rdbms/SqliteStmt.cpp
+++ b/rdbms/SqliteStmt.cpp
@@ -39,9 +39,8 @@ SqliteStmt::SqliteStmt(
   const AutocommitMode autocommitMode,
   SqliteConn &conn,
   const std::string &sql):
-  Stmt(autocommitMode),
+  Stmt(sql, autocommitMode),
   m_conn(conn),
-  m_sql(sql),
   m_paramNameToIdx(sql),
   m_nbAffectedRows(0) {
   m_stmt = nullptr;
@@ -49,7 +48,7 @@ SqliteStmt::SqliteStmt(
 
   const uint maxPrepareRetries = 20; // A worst case scenario of 2 seconds
   for(unsigned int i = 1; i <= maxPrepareRetries; i++) {
-    const int prepareRc = sqlite3_prepare_v2(m_conn.m_sqliteConn, sql.c_str(), nByte, &m_stmt, nullptr);
+    const int prepareRc = sqlite3_prepare_v2(m_conn.m_sqliteConn, m_sql.c_str(), nByte, &m_stmt, nullptr);
 
     if(SQLITE_OK == prepareRc) {
       break;
@@ -66,14 +65,15 @@ SqliteStmt::SqliteStmt(
         // Try to prepare the statement again
         continue;
       } else {
-        throw exception::Exception(std::string(__FUNCTION__) + " failed: sqlite3_prepare_v2 returned SQLITE_LOCKED the"
-          " maximum number of " + std::to_string(i) + " times"); 
+        throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSqlForException() +
+          ": sqlite3_prepare_v2 returned SQLITE_LOCKED the maximum number of " + std::to_string(i) + " times"); 
       }
     }
 
     const std::string msg = sqlite3_errmsg(m_conn.m_sqliteConn);
     sqlite3_finalize(m_stmt);
-    throw exception::Exception(std::string(__FUNCTION__) + " failed: sqlite3_prepare_v2 failed: " + msg);
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSqlForException() +
+      ": sqlite3_prepare_v2 failed: " + msg);
   }
 
   // m_stmt has been set so it is safe to call close() from now on
@@ -94,12 +94,12 @@ SqliteStmt::SqliteStmt(
     }
   } catch(exception::Exception &ex) {
     close();
-    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      sql.substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str());
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSqlForException() + ": " +
+      ex.getMessage().str());
   } catch(std::exception &se) {
     close();
-    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      sql.substr(0, c_maxSqlLenInExceptions) + ": " + se.what());
+    throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSqlForException() + ": " +
+      se.what());
   }
 }
 
@@ -132,18 +132,11 @@ void SqliteStmt::close() {
 sqlite3_stmt *SqliteStmt::get() const {
   if(nullptr == m_stmt) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": nullptr pointer");
+      getSqlForException() + ": nullptr pointer");
   }
   return m_stmt;
 }
 
-//------------------------------------------------------------------------------
-// getSql
-//------------------------------------------------------------------------------
-const std::string &SqliteStmt::getSql() const {
-  return m_sql;
-}
-
 //------------------------------------------------------------------------------
 // bindUint64
 //------------------------------------------------------------------------------
@@ -172,7 +165,7 @@ void SqliteStmt::bindOptionalUint64(const std::string &paramName, const optional
     }
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str());
+      getSqlForException() + ": " + ex.getMessage().str());
   }
 }
 
@@ -208,7 +201,7 @@ void SqliteStmt::bindOptionalString(const std::string &paramName, const optional
     }
   } catch(exception::Exception &ex) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + ex.getMessage().str()); 
+      getSqlForException() + ": " + ex.getMessage().str()); 
   }
 }
 
@@ -230,7 +223,7 @@ void SqliteStmt::executeNonQuery() {
   // Throw an exception if the call to sqlite3_step() failed
   if(SQLITE_DONE != stepRc && SQLITE_ROW != stepRc) {
     throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
-      getSql().substr(0, c_maxSqlLenInExceptions) + ": " + Sqlite::rcToStr(stepRc));
+      getSqlForException() + ": " + Sqlite::rcToStr(stepRc));
   }
 
   m_nbAffectedRows = sqlite3_changes(m_conn.m_sqliteConn);
@@ -238,7 +231,7 @@ void SqliteStmt::executeNonQuery() {
   // 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().substr(0, c_maxSqlLenInExceptions) + ": The SQL statment returned a result set");
+      getSqlForException() + ": The SQL statment returned a result set");
   }
 }
 
diff --git a/rdbms/SqliteStmt.hpp b/rdbms/SqliteStmt.hpp
index c70758dfaf..845e0f6def 100644
--- a/rdbms/SqliteStmt.hpp
+++ b/rdbms/SqliteStmt.hpp
@@ -71,13 +71,6 @@ public:
    */
   sqlite3_stmt *get() const;
 
-  /**
-   * Returns the SQL statement.
-   *
-   * @return The SQL statement.
-   */
-  virtual const std::string &getSql() const override;
-
   /**
    * Binds an SQL parameter.
    *
@@ -151,11 +144,6 @@ private:
    */
   SqliteConn &m_conn;
 
-  /**
-   * The SQL statement.
-   */
-  std::string m_sql;
-
   /**
    * Map from SQL parameter name to parameter index.
    */
diff --git a/rdbms/Stmt.cpp b/rdbms/Stmt.cpp
index 8d275f167e..6655ea0c52 100644
--- a/rdbms/Stmt.cpp
+++ b/rdbms/Stmt.cpp
@@ -24,7 +24,9 @@ namespace rdbms {
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-Stmt::Stmt(const AutocommitMode autocommitMode): m_autoCommitMode(autocommitMode) {
+Stmt::Stmt(const std::string &sql, const AutocommitMode autocommitMode):
+  m_sql(sql),
+  m_autoCommitMode(autocommitMode) {
 }
 
 //------------------------------------------------------------------------------
@@ -33,6 +35,28 @@ Stmt::Stmt(const AutocommitMode autocommitMode): m_autoCommitMode(autocommitMode
 Stmt::~Stmt() throw() {
 }
 
+//------------------------------------------------------------------------------
+// getSql
+//------------------------------------------------------------------------------
+const std::string &Stmt::getSql() const {
+  return m_sql;
+}
+
+//------------------------------------------------------------------------------
+// getSqlForException
+//------------------------------------------------------------------------------
+std::string Stmt::getSqlForException() const {
+  if(m_sql.length() <= c_maxSqlLenInExceptions) {
+    return m_sql;
+  } else {
+    if(c_maxSqlLenInExceptions >= 3) {
+      return m_sql.substr(0, c_maxSqlLenInExceptions - 3) + "...";
+    } else {
+      return std::string("..."). substr(0, c_maxSqlLenInExceptions);
+    }
+  }
+}
+
 //------------------------------------------------------------------------------
 // bindBool
 //------------------------------------------------------------------------------
diff --git a/rdbms/Stmt.hpp b/rdbms/Stmt.hpp
index 15dce6e950..7683d3789c 100644
--- a/rdbms/Stmt.hpp
+++ b/rdbms/Stmt.hpp
@@ -45,9 +45,10 @@ public:
   /**
    * Constructor.
    *
+   * @param sql The SQL statement.
    * @param autocommitMode The autocommit mode of the statement.
    */
-  Stmt(const AutocommitMode autocommitMode);
+  Stmt(const std::string &sql, const AutocommitMode autocommitMode);
 
   /**
    * Returns the autocommit mode of teh statement.
@@ -91,7 +92,7 @@ public:
    *
    * @return The SQL statement.
    */
-  virtual const std::string &getSql() const = 0;
+  const std::string &getSql() const;
 
   /**
    * Binds an SQL parameter.
@@ -172,6 +173,11 @@ public:
 
 protected:
 
+  /**
+   * The SQL statement.
+   */
+  std::string m_sql;
+
   /**
    * The autocommit mode of the statement.
    */
@@ -182,6 +188,18 @@ protected:
    */
   const uint32_t c_maxSqlLenInExceptions = 80;
 
+  /**
+   * Returns the SQL string to be used in an exception message.  The string
+   * will be clipped at a maxmum of c_maxSqlLenInExceptions characters.  If the
+   * string is actually clipped then the three last characters will be an
+   * replaced by an ellipsis of three dots, in other word "...".  These 3
+   * characters will indicate to the reader of the exception message that the
+   * SQL statement has been clipped.
+   *
+   * @return The SQL string to be used in an exception message.
+   */
+  std::string getSqlForException() const;
+
 }; // class Stmt
 
 } // namespace rdbms
-- 
GitLab