Refine SQLite3 error types with new exception handling (#2145)

Signed-off-by: yuanlu <2573580691@qq.com>
This commit is contained in:
元路 2024-09-06 15:37:09 +08:00 committed by GitHub
parent 1326205483
commit 59919f33ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 251 additions and 9 deletions

View File

@ -137,15 +137,24 @@ class SqlError : public Failure
/// SQLSTATE string describing the error type, if known; or empty string. /// SQLSTATE string describing the error type, if known; or empty string.
const std::string sqlState_; const std::string sqlState_;
const int errcode_{0};
const int extendedErrcode_{0};
public: public:
DROGON_EXPORT explicit SqlError(const std::string &msg = "", DROGON_EXPORT explicit SqlError(const std::string &msg = "",
const std::string &Q = "", const std::string &Q = "",
const char sqlstate[] = nullptr); 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; DROGON_EXPORT virtual ~SqlError() noexcept;
/// The query whose execution triggered the exception /// The query whose execution triggered the exception
DROGON_EXPORT const std::string &query() const noexcept; DROGON_EXPORT const std::string &query() const noexcept;
DROGON_EXPORT const std::string &sqlState() 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!" /// "Help, I don't know whether transaction was committed successfully!"
@ -293,6 +302,14 @@ class FeatureNotSupported : public SqlError
: SqlError(err, Q, sqlstate) : 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 /// Error in data provided to SQL statement
@ -305,6 +322,14 @@ class DataException : public SqlError
: SqlError(err, Q, sqlstate) : 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 class IntegrityConstraintViolation : public SqlError
@ -316,6 +341,14 @@ class IntegrityConstraintViolation : public SqlError
: SqlError(err, Q, sqlstate) : 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 class RestrictViolation : public IntegrityConstraintViolation
@ -327,6 +360,14 @@ class RestrictViolation : public IntegrityConstraintViolation
: IntegrityConstraintViolation(err, Q, sqlstate) : 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 class NotNullViolation : public IntegrityConstraintViolation
@ -338,6 +379,14 @@ class NotNullViolation : public IntegrityConstraintViolation
: IntegrityConstraintViolation(err, Q, sqlstate) : 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 class ForeignKeyViolation : public IntegrityConstraintViolation
@ -349,6 +398,14 @@ class ForeignKeyViolation : public IntegrityConstraintViolation
: IntegrityConstraintViolation(err, Q, sqlstate) : 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 class UniqueViolation : public IntegrityConstraintViolation
@ -360,6 +417,14 @@ class UniqueViolation : public IntegrityConstraintViolation
: IntegrityConstraintViolation(err, Q, sqlstate) : 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 class CheckViolation : public IntegrityConstraintViolation
@ -371,6 +436,14 @@ class CheckViolation : public IntegrityConstraintViolation
: IntegrityConstraintViolation(err, Q, sqlstate) : 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 class InvalidCursorState : public SqlError
@ -382,6 +455,14 @@ class InvalidCursorState : public SqlError
: SqlError(err, Q, sqlstate) : 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 class InvalidSqlStatementName : public SqlError
@ -393,6 +474,14 @@ class InvalidSqlStatementName : public SqlError
: SqlError(err, Q, sqlstate) : 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 class InvalidCursorName : public SqlError
@ -404,6 +493,14 @@ class InvalidCursorName : public SqlError
: SqlError(err, Q, sqlstate) : 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 class SyntaxError : public SqlError
@ -419,6 +516,15 @@ class SyntaxError : public SqlError
: SqlError(err, Q, sqlstate), errorPosition_(pos) : 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 class UndefinedColumn : public SyntaxError
@ -430,6 +536,14 @@ class UndefinedColumn : public SyntaxError
: SyntaxError(err, Q, sqlstate) : 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 class UndefinedFunction : public SyntaxError
@ -441,6 +555,14 @@ class UndefinedFunction : public SyntaxError
: SyntaxError(err, Q, sqlstate) : 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 class UndefinedTable : public SyntaxError
@ -452,6 +574,14 @@ class UndefinedTable : public SyntaxError
: SyntaxError(err, Q, sqlstate) : 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 class InsufficientPrivilege : public SqlError
@ -463,6 +593,14 @@ class InsufficientPrivilege : public SqlError
: SqlError(err, Q, sqlstate) : 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 /// Resource shortage on the server
@ -475,6 +613,14 @@ class InsufficientResources : public SqlError
: SqlError(err, Q, sqlstate) : 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 class DiskFull : public InsufficientResources
@ -486,6 +632,14 @@ class DiskFull : public InsufficientResources
: InsufficientResources(err, Q, sqlstate) : 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 class OutOfMemory : public InsufficientResources
@ -497,6 +651,14 @@ class OutOfMemory : public InsufficientResources
: InsufficientResources(err, Q, sqlstate) : 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 class TooManyConnections : public BrokenConnection

View File

@ -42,7 +42,23 @@ BrokenConnection::BrokenConnection(const std::string &whatarg)
SqlError::SqlError(const std::string &whatarg, SqlError::SqlError(const std::string &whatarg,
const std::string &Q, const std::string &Q,
const char sqlstate[]) 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_; return sqlState_;
} }
int SqlError::errcode() const noexcept
{
return errcode_;
}
int SqlError::extendedErrcode() const noexcept
{
return extendedErrcode_;
}
InDoubtError::InDoubtError(const std::string &whatarg) : Failure(whatarg) InDoubtError::InDoubtError(const std::string &whatarg) : Failure(whatarg)
{ {
} }

View File

@ -14,7 +14,10 @@
#include "Sqlite3Connection.h" #include "Sqlite3Connection.h"
#include "Sqlite3ResultImpl.h" #include "Sqlite3ResultImpl.h"
#include <drogon/orm/Exception.h>
#include <drogon/utils/Utilities.h> #include <drogon/utils/Utilities.h>
#include <stdexcept>
#include <string>
#include <string_view> #include <string_view>
#include <cctype> #include <cctype>
#include <exception> #include <exception>
@ -24,14 +27,54 @@
using namespace drogon; using namespace drogon;
using namespace drogon::orm; using namespace drogon::orm;
namespace
{
}
std::once_flag Sqlite3Connection::once_; std::once_flag Sqlite3Connection::once_;
void Sqlite3Connection::onError( void Sqlite3Connection::onError(
const std::string_view &sql, const std::string_view &sql,
const std::function<void(const std::exception_ptr &)> &exceptCallback) const std::function<void(const std::exception_ptr &)> &exceptCallback,
const int &extendedErrcode)
{ {
auto exceptPtr = std::make_exception_ptr( int errcode = extendedErrcode & 0xFF; // low 8 bit
SqlError(sqlite3_errmsg(connectionPtr_.get()), std::string{sql})); #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); exceptCallback(exceptPtr);
} }
@ -148,7 +191,8 @@ void Sqlite3Connection::execSqlInQueue(
: nullptr; : nullptr;
if (ret != SQLITE_OK || !stmtPtr) if (ret != SQLITE_OK || !stmtPtr)
{ {
onError(sql, exceptCallback); int ext_ret = sqlite3_extended_errcode(connectionPtr_.get());
onError(sql, exceptCallback, ext_ret);
idleCb_(); idleCb_();
return; return;
} }
@ -207,13 +251,14 @@ void Sqlite3Connection::execSqlInQueue(
} }
if (bindRet != SQLITE_OK) if (bindRet != SQLITE_OK)
{ {
onError(sql, exceptCallback); int eret = sqlite3_extended_errcode(connectionPtr_.get());
onError(sql, exceptCallback, eret);
sqlite3_reset(stmt); sqlite3_reset(stmt);
idleCb_(); idleCb_();
return; return;
} }
} }
int r; int r, er;
int columnNum = sqlite3_column_count(stmt); int columnNum = sqlite3_column_count(stmt);
auto resultPtr = std::make_shared<Sqlite3ResultImpl>(); auto resultPtr = std::make_shared<Sqlite3ResultImpl>();
for (int i = 0; i < columnNum; ++i) for (int i = 0; i < columnNum; ++i)
@ -233,6 +278,10 @@ void Sqlite3Connection::execSqlInQueue(
// Readonly, hold read lock; // Readonly, hold read lock;
std::shared_lock<SharedMutex> lock(*sharedMutexPtr_); std::shared_lock<SharedMutex> lock(*sharedMutexPtr_);
r = stmtStep(stmt, resultPtr, columnNum); r = stmtStep(stmt, resultPtr, columnNum);
if (r != SQLITE_DONE)
{
er = sqlite3_extended_errcode(connectionPtr_.get());
}
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
else else
@ -246,12 +295,16 @@ void Sqlite3Connection::execSqlInQueue(
resultPtr->insertId_ = resultPtr->insertId_ =
sqlite3_last_insert_rowid(connectionPtr_.get()); sqlite3_last_insert_rowid(connectionPtr_.get());
} }
else
{
er = sqlite3_extended_errcode(connectionPtr_.get());
}
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
if (r != SQLITE_DONE) if (r != SQLITE_DONE)
{ {
onError(sql, exceptCallback); onError(sql, exceptCallback, er);
sqlite3_reset(stmt); sqlite3_reset(stmt);
idleCb_(); idleCb_();
return; return;

View File

@ -75,7 +75,8 @@ class Sqlite3Connection : public DbConnection,
const std::function<void(const std::exception_ptr &)> &exceptCallback); const std::function<void(const std::exception_ptr &)> &exceptCallback);
void onError( void onError(
const std::string_view &sql, const std::string_view &sql,
const std::function<void(const std::exception_ptr &)> &exceptCallback); const std::function<void(const std::exception_ptr &)> &exceptCallback,
const int &extendedErrcode);
int stmtStep(sqlite3_stmt *stmt, int stmtStep(sqlite3_stmt *stmt,
const std::shared_ptr<Sqlite3ResultImpl> &resultPtr, const std::shared_ptr<Sqlite3ResultImpl> &resultPtr,
int columnNum); int columnNum);