From 59919f33efb68b2f64c71f76e94b9ec3baa345b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=83=E8=B7=AF?= <2573580691@qq.com> Date: Fri, 6 Sep 2024 15:37:09 +0800 Subject: [PATCH] Refine SQLite3 error types with new exception handling (#2145) Signed-off-by: yuanlu <2573580691@qq.com> --- orm_lib/inc/drogon/orm/Exception.h | 162 ++++++++++++++++++ orm_lib/src/Exception.cc | 28 ++- orm_lib/src/sqlite3_impl/Sqlite3Connection.cc | 67 +++++++- orm_lib/src/sqlite3_impl/Sqlite3Connection.h | 3 +- 4 files changed, 251 insertions(+), 9 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Exception.h b/orm_lib/inc/drogon/orm/Exception.h index a3041f73..f40211d1 100644 --- a/orm_lib/inc/drogon/orm/Exception.h +++ b/orm_lib/inc/drogon/orm/Exception.h @@ -137,15 +137,24 @@ class SqlError : public Failure /// SQLSTATE string describing the error type, if known; or empty string. const std::string sqlState_; + const int errcode_{0}; + const int extendedErrcode_{0}; + public: DROGON_EXPORT explicit SqlError(const std::string &msg = "", const std::string &Q = "", const char sqlstate[] = nullptr); + DROGON_EXPORT explicit SqlError(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode); DROGON_EXPORT virtual ~SqlError() noexcept; /// The query whose execution triggered the exception DROGON_EXPORT const std::string &query() const noexcept; DROGON_EXPORT const std::string &sqlState() const noexcept; + DROGON_EXPORT int errcode() const noexcept; + DROGON_EXPORT int extendedErrcode() const noexcept; }; /// "Help, I don't know whether transaction was committed successfully!" @@ -293,6 +302,14 @@ class FeatureNotSupported : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit FeatureNotSupported(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; /// Error in data provided to SQL statement @@ -305,6 +322,14 @@ class DataException : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit DataException(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; class IntegrityConstraintViolation : public SqlError @@ -316,6 +341,14 @@ class IntegrityConstraintViolation : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit IntegrityConstraintViolation(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; class RestrictViolation : public IntegrityConstraintViolation @@ -327,6 +360,14 @@ class RestrictViolation : public IntegrityConstraintViolation : IntegrityConstraintViolation(err, Q, sqlstate) { } + + explicit RestrictViolation(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode) + { + } }; class NotNullViolation : public IntegrityConstraintViolation @@ -338,6 +379,14 @@ class NotNullViolation : public IntegrityConstraintViolation : IntegrityConstraintViolation(err, Q, sqlstate) { } + + explicit NotNullViolation(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode) + { + } }; class ForeignKeyViolation : public IntegrityConstraintViolation @@ -349,6 +398,14 @@ class ForeignKeyViolation : public IntegrityConstraintViolation : IntegrityConstraintViolation(err, Q, sqlstate) { } + + explicit ForeignKeyViolation(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode) + { + } }; class UniqueViolation : public IntegrityConstraintViolation @@ -360,6 +417,14 @@ class UniqueViolation : public IntegrityConstraintViolation : IntegrityConstraintViolation(err, Q, sqlstate) { } + + explicit UniqueViolation(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode) + { + } }; class CheckViolation : public IntegrityConstraintViolation @@ -371,6 +436,14 @@ class CheckViolation : public IntegrityConstraintViolation : IntegrityConstraintViolation(err, Q, sqlstate) { } + + explicit CheckViolation(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : IntegrityConstraintViolation(msg, Q, errcode, extendedErrcode) + { + } }; class InvalidCursorState : public SqlError @@ -382,6 +455,14 @@ class InvalidCursorState : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit InvalidCursorState(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; class InvalidSqlStatementName : public SqlError @@ -393,6 +474,14 @@ class InvalidSqlStatementName : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit InvalidSqlStatementName(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; class InvalidCursorName : public SqlError @@ -404,6 +493,14 @@ class InvalidCursorName : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit InvalidCursorName(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; class SyntaxError : public SqlError @@ -419,6 +516,15 @@ class SyntaxError : public SqlError : SqlError(err, Q, sqlstate), errorPosition_(pos) { } + + explicit SyntaxError(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode, + int pos = -1) + : SqlError(msg, Q, errcode, extendedErrcode), errorPosition_(pos) + { + } }; class UndefinedColumn : public SyntaxError @@ -430,6 +536,14 @@ class UndefinedColumn : public SyntaxError : SyntaxError(err, Q, sqlstate) { } + + explicit UndefinedColumn(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SyntaxError(msg, Q, errcode, extendedErrcode) + { + } }; class UndefinedFunction : public SyntaxError @@ -441,6 +555,14 @@ class UndefinedFunction : public SyntaxError : SyntaxError(err, Q, sqlstate) { } + + explicit UndefinedFunction(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SyntaxError(msg, Q, errcode, extendedErrcode) + { + } }; class UndefinedTable : public SyntaxError @@ -452,6 +574,14 @@ class UndefinedTable : public SyntaxError : SyntaxError(err, Q, sqlstate) { } + + explicit UndefinedTable(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SyntaxError(msg, Q, errcode, extendedErrcode) + { + } }; class InsufficientPrivilege : public SqlError @@ -463,6 +593,14 @@ class InsufficientPrivilege : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit InsufficientPrivilege(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; /// Resource shortage on the server @@ -475,6 +613,14 @@ class InsufficientResources : public SqlError : SqlError(err, Q, sqlstate) { } + + explicit InsufficientResources(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : SqlError(msg, Q, errcode, extendedErrcode) + { + } }; class DiskFull : public InsufficientResources @@ -486,6 +632,14 @@ class DiskFull : public InsufficientResources : InsufficientResources(err, Q, sqlstate) { } + + explicit DiskFull(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : InsufficientResources(msg, Q, errcode, extendedErrcode) + { + } }; class OutOfMemory : public InsufficientResources @@ -497,6 +651,14 @@ class OutOfMemory : public InsufficientResources : InsufficientResources(err, Q, sqlstate) { } + + explicit OutOfMemory(const std::string &msg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : InsufficientResources(msg, Q, errcode, extendedErrcode) + { + } }; class TooManyConnections : public BrokenConnection diff --git a/orm_lib/src/Exception.cc b/orm_lib/src/Exception.cc index 7e5c2d08..fc02e611 100644 --- a/orm_lib/src/Exception.cc +++ b/orm_lib/src/Exception.cc @@ -42,7 +42,23 @@ BrokenConnection::BrokenConnection(const std::string &whatarg) SqlError::SqlError(const std::string &whatarg, const std::string &Q, const char sqlstate[]) - : Failure(whatarg), query_(Q), sqlState_(sqlstate ? sqlstate : "") + : Failure(whatarg), + query_(Q), + sqlState_(sqlstate ? sqlstate : ""), + errcode_(0), + extendedErrcode_(0) +{ +} + +SqlError::SqlError(const std::string &whatarg, + const std::string &Q, + const int errcode, + const int extendedErrcode) + : Failure(whatarg), + query_(Q), + sqlState_(""), + errcode_(errcode), + extendedErrcode_(extendedErrcode) { } @@ -60,6 +76,16 @@ const std::string &SqlError::sqlState() const noexcept return sqlState_; } +int SqlError::errcode() const noexcept +{ + return errcode_; +} + +int SqlError::extendedErrcode() const noexcept +{ + return extendedErrcode_; +} + InDoubtError::InDoubtError(const std::string &whatarg) : Failure(whatarg) { } diff --git a/orm_lib/src/sqlite3_impl/Sqlite3Connection.cc b/orm_lib/src/sqlite3_impl/Sqlite3Connection.cc index 8ad70c74..27abe13d 100644 --- a/orm_lib/src/sqlite3_impl/Sqlite3Connection.cc +++ b/orm_lib/src/sqlite3_impl/Sqlite3Connection.cc @@ -14,7 +14,10 @@ #include "Sqlite3Connection.h" #include "Sqlite3ResultImpl.h" +#include #include +#include +#include #include #include #include @@ -24,14 +27,54 @@ using namespace drogon; using namespace drogon::orm; +namespace +{ + +} + std::once_flag Sqlite3Connection::once_; void Sqlite3Connection::onError( const std::string_view &sql, - const std::function &exceptCallback) + const std::function &exceptCallback, + const int &extendedErrcode) { - auto exceptPtr = std::make_exception_ptr( - SqlError(sqlite3_errmsg(connectionPtr_.get()), std::string{sql})); + int errcode = extendedErrcode & 0xFF; // low 8 bit +#define ORM_ERR_CASE(code, type) \ + case code: \ + { \ + auto exceptPtr = std::make_exception_ptr( \ + drogon::orm::type(sqlite3_errmsg(connectionPtr_.get()), \ + std::string{sql}, \ + errcode, \ + extendedErrcode)); \ + exceptCallback(exceptPtr); \ + return; \ + }; + switch (extendedErrcode) + { + ORM_ERR_CASE(SQLITE_CONSTRAINT_NOTNULL, NotNullViolation) + ORM_ERR_CASE(SQLITE_CONSTRAINT_FOREIGNKEY, ForeignKeyViolation) + ORM_ERR_CASE(SQLITE_CONSTRAINT_PRIMARYKEY, UniqueViolation) + ORM_ERR_CASE(SQLITE_CONSTRAINT_UNIQUE, UniqueViolation) + ORM_ERR_CASE(SQLITE_CONSTRAINT_CHECK, CheckViolation) + } + switch (errcode) + { + ORM_ERR_CASE(SQLITE_MISMATCH, DataException) + ORM_ERR_CASE(SQLITE_CONSTRAINT, IntegrityConstraintViolation) + ORM_ERR_CASE(SQLITE_PERM, InsufficientPrivilege) + ORM_ERR_CASE(SQLITE_AUTH, InsufficientPrivilege) + ORM_ERR_CASE(SQLITE_NOMEM, OutOfMemory) + ORM_ERR_CASE(SQLITE_FULL, DiskFull) + } +#undef ORM_ERR_CASE + + auto exceptPtr = + std::make_exception_ptr(SqlError(sqlite3_errmsg(connectionPtr_.get()), + std::string{sql}, + errcode, + extendedErrcode)); exceptCallback(exceptPtr); } @@ -148,7 +191,8 @@ void Sqlite3Connection::execSqlInQueue( : nullptr; if (ret != SQLITE_OK || !stmtPtr) { - onError(sql, exceptCallback); + int ext_ret = sqlite3_extended_errcode(connectionPtr_.get()); + onError(sql, exceptCallback, ext_ret); idleCb_(); return; } @@ -207,13 +251,14 @@ void Sqlite3Connection::execSqlInQueue( } if (bindRet != SQLITE_OK) { - onError(sql, exceptCallback); + int eret = sqlite3_extended_errcode(connectionPtr_.get()); + onError(sql, exceptCallback, eret); sqlite3_reset(stmt); idleCb_(); return; } } - int r; + int r, er; int columnNum = sqlite3_column_count(stmt); auto resultPtr = std::make_shared(); for (int i = 0; i < columnNum; ++i) @@ -233,6 +278,10 @@ void Sqlite3Connection::execSqlInQueue( // Readonly, hold read lock; std::shared_lock lock(*sharedMutexPtr_); r = stmtStep(stmt, resultPtr, columnNum); + if (r != SQLITE_DONE) + { + er = sqlite3_extended_errcode(connectionPtr_.get()); + } sqlite3_reset(stmt); } else @@ -246,12 +295,16 @@ void Sqlite3Connection::execSqlInQueue( resultPtr->insertId_ = sqlite3_last_insert_rowid(connectionPtr_.get()); } + else + { + er = sqlite3_extended_errcode(connectionPtr_.get()); + } sqlite3_reset(stmt); } if (r != SQLITE_DONE) { - onError(sql, exceptCallback); + onError(sql, exceptCallback, er); sqlite3_reset(stmt); idleCb_(); return; diff --git a/orm_lib/src/sqlite3_impl/Sqlite3Connection.h b/orm_lib/src/sqlite3_impl/Sqlite3Connection.h index 8f3ed834..9cdd64c3 100644 --- a/orm_lib/src/sqlite3_impl/Sqlite3Connection.h +++ b/orm_lib/src/sqlite3_impl/Sqlite3Connection.h @@ -75,7 +75,8 @@ class Sqlite3Connection : public DbConnection, const std::function &exceptCallback); void onError( const std::string_view &sql, - const std::function &exceptCallback); + const std::function &exceptCallback, + const int &extendedErrcode); int stmtStep(sqlite3_stmt *stmt, const std::shared_ptr &resultPtr, int columnNum);