diff --git a/include/SQLiteCpp/Database.h b/include/SQLiteCpp/Database.h index f64c9951..b82ccc68 100644 --- a/include/SQLiteCpp/Database.h +++ b/include/SQLiteCpp/Database.h @@ -378,7 +378,7 @@ class Database { if (SQLITE_OK != aRet) { - throw SQLite::Exception(sqlite3_errstr(aRet)); + throw SQLite::Exception(mpSQLite, aRet); } } diff --git a/include/SQLiteCpp/Exception.h b/include/SQLiteCpp/Exception.h index ae7a8ed9..e3a5ecea 100644 --- a/include/SQLiteCpp/Exception.h +++ b/include/SQLiteCpp/Exception.h @@ -12,6 +12,27 @@ #include #include +#include +#include + + +/// Compatibility with non-clang compilers. +#ifndef __has_feature + #define __has_feature(x) 0 +#endif + +// Detect whether the compiler supports C++11 noexcept exception specifications. +#if ( defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || (__GNUC__ > 4)) \ + && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// GCC 4.7 and following have noexcept +#elif defined(__clang__) && __has_feature(cxx_noexcept) +// Clang 3.0 and above have noexcept +#elif defined(_MSC_VER) && _MSC_VER > 1800 +// Visual Studio 2015 and above have noexcept +#else + // Visual Studio 2013 does not support noexcept, and "throw()" is deprecated by C++11 + #define noexcept +#endif namespace SQLite @@ -30,29 +51,73 @@ class Exception : public std::runtime_error * @param[in] aErrorMessage The string message describing the SQLite error */ explicit Exception(const std::string& aErrorMessage) : - std::runtime_error(aErrorMessage) + std::runtime_error(aErrorMessage), + mErrcode(-1), // 0 would be SQLITE_OK, which doesn't make sense + mExtendedErrcode(-1) { } -}; + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + */ + explicit Exception(sqlite3* apSQLite) : + std::runtime_error(sqlite3_errmsg(apSQLite)), + mErrcode(sqlite3_errcode(apSQLite)), + mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) + { + } -} // namespace SQLite + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + * @param[in] ret Return value from function call that failed. + */ + explicit Exception(sqlite3* apSQLite, int ret) : + std::runtime_error(sqlite3_errmsg(apSQLite)), + mErrcode(ret), + mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) + { + } + /** + * @brief Encapsulation of the error message from SQLite3, based on std::runtime_error. + * + * @param[in] apSQLite The SQLite object, to obtain detailed error messages from. + * @param[in] ret Return value from function call that failed. + * @param[in] aErrorMessage String providing more context, added to the SQLite errmsg + */ + explicit Exception(sqlite3* apSQLite, int ret, const std::string &aErrorMessage) : + std::runtime_error(aErrorMessage + ": " + sqlite3_errmsg(apSQLite)), + mErrcode(ret), + mExtendedErrcode(sqlite3_extended_errcode(apSQLite)) + { + } -/// Compatibility with non-clang compilers. -#ifndef __has_feature - #define __has_feature(x) 0 -#endif + /// @brief Return the result code (if any, otherwise -1). + inline int getErrorCode() const noexcept // nothrow + { + return mErrcode; + } -// Detect whether the compiler supports C++11 noexcept exception specifications. -#if ( defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || (__GNUC__ > 4)) \ - && defined(__GXX_EXPERIMENTAL_CXX0X__)) -// GCC 4.7 and following have noexcept -#elif defined(__clang__) && __has_feature(cxx_noexcept) -// Clang 3.0 and above have noexcept -#elif defined(_MSC_VER) && _MSC_VER > 1800 -// Visual Studio 2015 and above have noexcept -#else - // Visual Studio 2013 does not support noexcept, and "throw()" is deprecated by C++11 - #define noexcept -#endif + /// @brief Return the extended numeric result code (if any, otherwise -1). + inline int getExtendedErrorCode() const noexcept // nothrow + { + return mExtendedErrcode; + } + + /// @brief Return a string, solely based on the error code + inline const char *getErrStr() const noexcept // nothrow + { + return sqlite3_errstr(mErrcode); + } + +private: + const int mErrcode; + const int mExtendedErrcode; +}; + + +} // namespace SQLite diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 3ee31627..dc44b6ec 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -457,7 +457,7 @@ class Statement { if (SQLITE_OK != aRet) { - throw SQLite::Exception(sqlite3_errstr(aRet)); + throw SQLite::Exception(mStmtPtr, aRet); } } diff --git a/src/Backup.cpp b/src/Backup.cpp index ba8815a4..c46ecfd8 100644 --- a/src/Backup.cpp +++ b/src/Backup.cpp @@ -31,8 +31,7 @@ Backup::Backup(Database& aDestDatabase, apSrcDatabaseName); if (NULL == mpSQLiteBackup) { - std::string strerr = sqlite3_errmsg(aDestDatabase.getHandle()); - throw SQLite::Exception(strerr); + throw SQLite::Exception(aDestDatabase.getHandle()); } } diff --git a/src/Database.cpp b/src/Database.cpp index 2ce2183c..f19dee93 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -36,9 +36,9 @@ Database::Database(const char* apFilename, const int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs); if (SQLITE_OK != ret) { - std::string strerr = sqlite3_errstr(ret); + const SQLite::Exception exception(mpSQLite, ret); // must create before closing sqlite3_close(mpSQLite); // close is required even in case of error on opening - throw SQLite::Exception(strerr); + throw exception; } if (aBusyTimeoutMs > 0) @@ -58,9 +58,9 @@ Database::Database(const std::string& aFilename, const int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str()); if (SQLITE_OK != ret) { - std::string strerr = sqlite3_errstr(ret); + const SQLite::Exception exception(mpSQLite, ret); // must create before closing sqlite3_close(mpSQLite); // close is required even in case of error on opening - throw SQLite::Exception(strerr); + throw exception; } if (aBusyTimeoutMs > 0) diff --git a/src/Statement.cpp b/src/Statement.cpp index 20d4bc5f..eec9a92b 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -195,7 +195,7 @@ bool Statement::executeStep() { mbOk = false; mbDone = false; - throw SQLite::Exception(sqlite3_errstr(ret)); + throw SQLite::Exception(mStmtPtr, ret); } } else @@ -227,7 +227,7 @@ int Statement::exec() { mbOk = false; mbDone = false; - throw SQLite::Exception(sqlite3_errstr(ret)); + throw SQLite::Exception(mStmtPtr, ret); } } else @@ -317,7 +317,7 @@ Statement::Ptr::Ptr(sqlite3* apSQLite, std::string& aQuery) : const int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast(aQuery.size()), &mpStmt, NULL); if (SQLITE_OK != ret) { - throw SQLite::Exception(sqlite3_errstr(ret)); + throw SQLite::Exception(apSQLite, ret); } // Initialize the reference counter of the sqlite3_stmt : // used to share the mStmtPtr between Statement and Column objects;