forked from qt-creator/qt-creator
Sqlite: Improve exception handling
Introducing different exceptions for different error cases. Change-Id: I4371d1e64d9dca2a9f68dcbaa4a891c55879c1f5 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io> Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
#include "sqlitestatement.h"
|
#include "sqlitestatement.h"
|
||||||
#include "sqlitewritestatement.h"
|
#include "sqlitewritestatement.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@@ -230,24 +231,29 @@ void DatabaseBackend::cacheTextEncoding()
|
|||||||
void DatabaseBackend::checkForOpenDatabaseWhichCanBeClosed()
|
void DatabaseBackend::checkForOpenDatabaseWhichCanBeClosed()
|
||||||
{
|
{
|
||||||
if (m_databaseHandle == nullptr)
|
if (m_databaseHandle == nullptr)
|
||||||
throwException("SqliteDatabaseBackend::close: database is not open so it can not be closed.");
|
throw DatabaseIsAlreadyClosed("SqliteDatabaseBackend::close: database is not open so it can not be closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseBackend::checkDatabaseClosing(int resultCode)
|
void DatabaseBackend::checkDatabaseClosing(int resultCode)
|
||||||
{
|
{
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case SQLITE_OK: return;
|
case SQLITE_OK: return;
|
||||||
default: throwException("SqliteDatabaseBackend::close: unknown error happens at closing!");
|
case SQLITE_BUSY: throw DatabaseIsBusy("SqliteDatabaseBackend::close: database is busy because of e.g. unfinalized statements and will stay open!");
|
||||||
|
default: throwUnknowError("SqliteDatabaseBackend::close: unknown error happens at closing!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseBackend::checkCanOpenDatabase(Utils::SmallStringView databaseFilePath)
|
void DatabaseBackend::checkCanOpenDatabase(Utils::SmallStringView databaseFilePath)
|
||||||
{
|
{
|
||||||
if (databaseFilePath.isEmpty())
|
if (databaseFilePath.isEmpty())
|
||||||
throw Exception("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened:", "database file path is empty!");
|
throw DatabaseFilePathIsEmpty("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because the file path is empty!");
|
||||||
|
|
||||||
|
if (!QFileInfo::exists(QFileInfo(QString(databaseFilePath)).path()))
|
||||||
|
throw WrongFilePath("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because of wrong file path!",
|
||||||
|
Utils::SmallString(databaseFilePath));
|
||||||
|
|
||||||
if (databaseIsOpen())
|
if (databaseIsOpen())
|
||||||
throw Exception("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened:", "database is already open!");
|
throw DatabaseIsAlreadyOpen("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because it is already open!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseBackend::checkDatabaseCouldBeOpened(int resultCode)
|
void DatabaseBackend::checkDatabaseCouldBeOpened(int resultCode)
|
||||||
@@ -262,16 +268,16 @@ void DatabaseBackend::checkDatabaseCouldBeOpened(int resultCode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseBackend::checkPragmaValue(Utils::SmallStringView databaseValue,
|
void DatabaseBackend::checkPragmaValue(Utils::SmallStringView databaseValue,
|
||||||
Utils::SmallStringView expectedValue)
|
Utils::SmallStringView expectedValue)
|
||||||
{
|
{
|
||||||
if (databaseValue != expectedValue)
|
if (databaseValue != expectedValue)
|
||||||
throwException("SqliteDatabaseBackend::setPragmaValue: pragma value is not set!");
|
throw PragmaValueNotSet("SqliteDatabaseBackend::setPragmaValue: pragma value is not set!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseBackend::checkDatabaseHandleIsNotNull()
|
void DatabaseBackend::checkDatabaseHandleIsNotNull()
|
||||||
{
|
{
|
||||||
if (m_databaseHandle == nullptr)
|
if (m_databaseHandle == nullptr)
|
||||||
throwException("SqliteDatabaseBackend: database is not open!");
|
throwDatabaseIsNotOpen("SqliteDatabaseBackend: database is not open!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseBackend::checkIfMultithreadingIsActivated(int resultCode)
|
void DatabaseBackend::checkIfMultithreadingIsActivated(int resultCode)
|
||||||
@@ -392,6 +398,16 @@ void DatabaseBackend::throwException(const char *whatHasHappens) const
|
|||||||
throw Exception(whatHasHappens);
|
throw Exception(whatHasHappens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DatabaseBackend::throwUnknowError(const char *whatHasHappens) const
|
||||||
|
{
|
||||||
|
throw UnknowError(whatHasHappens);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseBackend::throwDatabaseIsNotOpen(const char *whatHasHappens) const
|
||||||
|
{
|
||||||
|
throw DatabaseIsNotOpen(whatHasHappens);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
Type DatabaseBackend::toValue(Utils::SmallStringView sqlStatement)
|
Type DatabaseBackend::toValue(Utils::SmallStringView sqlStatement)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,8 +110,9 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
Q_NORETURN static void throwExceptionStatic(const char *whatHasHappens);
|
Q_NORETURN static void throwExceptionStatic(const char *whatHasHappens);
|
||||||
Q_NORETURN void throwException(const char *whatHasHappens) const;
|
[[noreturn]] void throwException(const char *whatHasHappens) const;
|
||||||
|
[[noreturn]] void throwUnknowError(const char *whatHasHappens) const;
|
||||||
|
[[noreturn]] void throwDatabaseIsNotOpen(const char *whatHasHappens) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Database &m_database;
|
Database &m_database;
|
||||||
|
|||||||
@@ -48,4 +48,211 @@ private:
|
|||||||
Utils::SmallString m_sqliteErrorMessage;
|
Utils::SmallString m_sqliteErrorMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class StatementIsBusy : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StatementIsBusy(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseIsBusy : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseIsBusy(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatementHasError : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StatementHasError(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatementIsMisused : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StatementIsMisused(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ContraintPreventsModification : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContraintPreventsModification(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NoValuesToFetch : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoValuesToFetch(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class InvalidColumnFetched : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InvalidColumnFetched(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BindingIndexIsOutOfRange : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BindingIndexIsOutOfRange(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WrongBingingName : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WrongBingingName(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseIsNotOpen : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseIsNotOpen(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseCannotBeOpened : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseCannotBeOpened(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseFilePathIsEmpty : public DatabaseCannotBeOpened
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseFilePathIsEmpty(const char *whatErrorHasHappen)
|
||||||
|
: DatabaseCannotBeOpened(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseIsAlreadyOpen : public DatabaseCannotBeOpened
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseIsAlreadyOpen(const char *whatErrorHasHappen)
|
||||||
|
: DatabaseCannotBeOpened(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseCannotBeClosed : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseCannotBeClosed(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseIsAlreadyClosed : public DatabaseCannotBeClosed
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseIsAlreadyClosed(const char *whatErrorHasHappen)
|
||||||
|
: DatabaseCannotBeClosed(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WrongFilePath : public DatabaseCannotBeOpened
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WrongFilePath(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||||
|
: DatabaseCannotBeOpened(whatErrorHasHappen, std::move(errorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PragmaValueNotSet : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PragmaValueNotSet(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NotReadOnlySqlStatement : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NotReadOnlySqlStatement(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NotWriteSqlStatement : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NotWriteSqlStatement(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeadLock : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeadLock(const char *whatErrorHasHappen)
|
||||||
|
: Exception(whatErrorHasHappen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnknowError : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnknowError(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BindingTooBig : public Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BindingTooBig(const char *whatErrorHasHappen,
|
||||||
|
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||||
|
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Sqlite
|
} // namespace Sqlite
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ ReadStatement::ReadStatement(Utils::SmallStringView sqlStatement,
|
|||||||
void ReadStatement::checkIsReadOnlyStatement()
|
void ReadStatement::checkIsReadOnlyStatement()
|
||||||
{
|
{
|
||||||
if (!isReadOnlyStatement())
|
if (!isReadOnlyStatement())
|
||||||
throwException("SqliteStatement::SqliteReadStatement: is not read only statement!");
|
throw NotReadOnlySqlStatement("SqliteStatement::SqliteReadStatement: is not read only statement!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Sqlite
|
} // namespace Sqlite
|
||||||
|
|||||||
@@ -95,19 +95,26 @@ private:
|
|||||||
void Statement::waitForUnlockNotify() const
|
void Statement::waitForUnlockNotify() const
|
||||||
{
|
{
|
||||||
UnlockNotification unlockNotification;
|
UnlockNotification unlockNotification;
|
||||||
int resultCode = sqlite3_unlock_notify(sqliteDatabaseHandle(), UnlockNotification::unlockNotifyCallBack, &unlockNotification);
|
int resultCode = sqlite3_unlock_notify(sqliteDatabaseHandle(),
|
||||||
|
UnlockNotification::unlockNotifyCallBack,
|
||||||
|
&unlockNotification);
|
||||||
|
|
||||||
if (resultCode == SQLITE_OK)
|
if (resultCode == SQLITE_LOCKED)
|
||||||
unlockNotification.wait();
|
throw DeadLock("SqliteStatement::waitForUnlockNotify: database is in a dead lock!");
|
||||||
else
|
|
||||||
throwException("SqliteStatement::waitForUnlockNotify: database is in a dead lock!");
|
unlockNotification.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::reset() const
|
void Statement::reset() const
|
||||||
{
|
{
|
||||||
int resultCode = sqlite3_reset(m_compiledStatement.get());
|
int resultCode = sqlite3_reset(m_compiledStatement.get());
|
||||||
if (resultCode != SQLITE_OK)
|
switch (resultCode) {
|
||||||
throwException("SqliteStatement::reset: can't reset statement!");
|
case SQLITE_OK: return;
|
||||||
|
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!");
|
||||||
|
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!");
|
||||||
|
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
|
||||||
|
case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!");
|
||||||
|
}
|
||||||
|
|
||||||
m_isReadyToFetchValues = false;
|
m_isReadyToFetchValues = false;
|
||||||
}
|
}
|
||||||
@@ -159,30 +166,30 @@ Utils::SmallStringVector Statement::columnNames() const
|
|||||||
|
|
||||||
void Statement::bind(int index, int value)
|
void Statement::bind(int index, int value)
|
||||||
{
|
{
|
||||||
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
|
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
|
||||||
if (resultCode != SQLITE_OK)
|
checkForBindingError(resultCode);
|
||||||
throwException("SqliteStatement::bind: cant' bind 32 bit integer!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::bind(int index, long long value)
|
void Statement::bind(int index, long long value)
|
||||||
{
|
{
|
||||||
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
|
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
|
||||||
if (resultCode != SQLITE_OK)
|
checkForBindingError(resultCode);
|
||||||
throwException("SqliteStatement::bind: cant' bind 64 bit integer!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::bind(int index, double value)
|
void Statement::bind(int index, double value)
|
||||||
{
|
{
|
||||||
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
|
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
|
||||||
if (resultCode != SQLITE_OK)
|
checkForBindingError(resultCode);
|
||||||
throwException("SqliteStatement::bind: cant' bind double!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::bind(int index, Utils::SmallStringView text)
|
void Statement::bind(int index, Utils::SmallStringView text)
|
||||||
{
|
{
|
||||||
int resultCode = sqlite3_bind_text(m_compiledStatement.get(), index, text.data(), int(text.size()), SQLITE_TRANSIENT);
|
int resultCode = sqlite3_bind_text(m_compiledStatement.get(),
|
||||||
if (resultCode != SQLITE_OK)
|
index,
|
||||||
throwException("SqliteStatement::bind: cant' bind double!");
|
text.data(),
|
||||||
|
int(text.size()),
|
||||||
|
SQLITE_TRANSIENT);
|
||||||
|
checkForBindingError(resultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
@@ -250,13 +257,13 @@ bool Statement::checkForStepError(int resultCode) const
|
|||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case SQLITE_ROW: return true;
|
case SQLITE_ROW: return true;
|
||||||
case SQLITE_DONE: return false;
|
case SQLITE_DONE: return false;
|
||||||
case SQLITE_BUSY: throwException("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!");
|
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!");
|
||||||
case SQLITE_ERROR : throwException("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!");
|
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!");
|
||||||
case SQLITE_MISUSE: throwException("SqliteStatement::stepStatement: was called inappropriately!");
|
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
|
||||||
case SQLITE_CONSTRAINT: throwException("SqliteStatement::stepStatement: contraint prevent insert or update!");
|
case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!");
|
||||||
}
|
}
|
||||||
|
|
||||||
throwException("SqliteStatement::stepStatement: unknown error has happened");
|
throwUnknowError("SqliteStatement::stepStatement: unknown error has happened");
|
||||||
|
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
@@ -265,12 +272,24 @@ void Statement::checkForPrepareError(int resultCode) const
|
|||||||
{
|
{
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case SQLITE_OK: return;
|
case SQLITE_OK: return;
|
||||||
case SQLITE_BUSY: throwException("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!");
|
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!");
|
||||||
case SQLITE_ERROR : throwException("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!");
|
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!");
|
||||||
case SQLITE_MISUSE: throwException("SqliteStatement::prepareStatement: was called inappropriately!");
|
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!");
|
||||||
}
|
}
|
||||||
|
|
||||||
throwException("SqliteStatement::prepareStatement: unknown error has happened");
|
throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::checkForBindingError(int resultCode) const
|
||||||
|
{
|
||||||
|
switch (resultCode) {
|
||||||
|
case SQLITE_OK: return;
|
||||||
|
case SQLITE_TOOBIG: throwBingingTooBig("SqliteStatement::bind: string or blob are over size limits(SQLITE_LIMIT_LENGTH)!");
|
||||||
|
case SQLITE_RANGE : throwBindingIndexIsOutOfRange("SqliteStatement::bind: binding index is out of range!");
|
||||||
|
case SQLITE_NOMEM: throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
throwUnknowError("SqliteStatement::bind: unknown error has happened");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::setIfIsReadyToFetchValues(int resultCode) const
|
void Statement::setIfIsReadyToFetchValues(int resultCode) const
|
||||||
@@ -285,33 +304,27 @@ void Statement::setIfIsReadyToFetchValues(int resultCode) const
|
|||||||
void Statement::checkIfIsReadyToFetchValues() const
|
void Statement::checkIfIsReadyToFetchValues() const
|
||||||
{
|
{
|
||||||
if (!m_isReadyToFetchValues)
|
if (!m_isReadyToFetchValues)
|
||||||
throwException("SqliteStatement::value: there are no values to fetch!");
|
throwNoValuesToFetch("SqliteStatement::value: there are no values to fetch!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::checkColumnsAreValid(const std::vector<int> &columns) const
|
void Statement::checkColumnsAreValid(const std::vector<int> &columns) const
|
||||||
{
|
{
|
||||||
for (int column : columns) {
|
for (int column : columns) {
|
||||||
if (column < 0 || column >= m_columnCount)
|
if (column < 0 || column >= m_columnCount)
|
||||||
throwException("SqliteStatement::values: column index out of bound!");
|
throwInvalidColumnFetched("SqliteStatement::values: column index out of bound!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::checkColumnIsValid(int column) const
|
void Statement::checkColumnIsValid(int column) const
|
||||||
{
|
{
|
||||||
if (column < 0 || column >= m_columnCount)
|
if (column < 0 || column >= m_columnCount)
|
||||||
throwException("SqliteStatement::values: column index out of bound!");
|
throwInvalidColumnFetched("SqliteStatement::values: column index out of bound!");
|
||||||
}
|
|
||||||
|
|
||||||
void Statement::checkBindingIndex(int index) const
|
|
||||||
{
|
|
||||||
if (index <= 0 || index > m_bindingParameterCount)
|
|
||||||
throwException("SqliteStatement::bind: binding index is out of bound!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::checkBindingName(int index) const
|
void Statement::checkBindingName(int index) const
|
||||||
{
|
{
|
||||||
if (index <= 0 || index > m_bindingParameterCount)
|
if (index <= 0 || index > m_bindingParameterCount)
|
||||||
throwException("SqliteStatement::bind: binding name are not exists in this statement!");
|
throwWrongBingingName("SqliteStatement::bind: binding name are not exists in this statement!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::setBindingParameterCount()
|
void Statement::setBindingParameterCount()
|
||||||
@@ -345,9 +358,57 @@ bool Statement::isReadOnlyStatement() const
|
|||||||
return sqlite3_stmt_readonly(m_compiledStatement.get());
|
return sqlite3_stmt_readonly(m_compiledStatement.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statement::throwException(const char *whatHasHappened) const
|
void Statement::throwStatementIsBusy(const char *whatHasHappened) const
|
||||||
{
|
{
|
||||||
throw Exception(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
throw StatementIsBusy(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwStatementHasError(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw StatementHasError(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwStatementIsMisused(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwConstraintPreventsModification(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw ContraintPreventsModification(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwNoValuesToFetch(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw NoValuesToFetch(whatHasHappened);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwInvalidColumnFetched(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw InvalidColumnFetched(whatHasHappened);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwBindingIndexIsOutOfRange(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw BindingIndexIsOutOfRange(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwWrongBingingName(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw WrongBingingName(whatHasHappened);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwUnknowError(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
if (sqliteDatabaseHandle())
|
||||||
|
throw UnknowError(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
|
else
|
||||||
|
throw UnknowError(whatHasHappened);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Statement::throwBingingTooBig(const char *whatHasHappened) const
|
||||||
|
{
|
||||||
|
throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Statement::columnName(int column) const
|
QString Statement::columnName(int column) const
|
||||||
|
|||||||
@@ -355,17 +355,26 @@ protected:
|
|||||||
|
|
||||||
bool checkForStepError(int resultCode) const;
|
bool checkForStepError(int resultCode) const;
|
||||||
void checkForPrepareError(int resultCode) const;
|
void checkForPrepareError(int resultCode) const;
|
||||||
|
void checkForBindingError(int resultCode) const;
|
||||||
void setIfIsReadyToFetchValues(int resultCode) const;
|
void setIfIsReadyToFetchValues(int resultCode) const;
|
||||||
void checkIfIsReadyToFetchValues() const;
|
void checkIfIsReadyToFetchValues() const;
|
||||||
void checkColumnsAreValid(const std::vector<int> &columns) const;
|
void checkColumnsAreValid(const std::vector<int> &columns) const;
|
||||||
void checkColumnIsValid(int column) const;
|
void checkColumnIsValid(int column) const;
|
||||||
void checkBindingIndex(int index) const;
|
|
||||||
void checkBindingName(int index) const;
|
void checkBindingName(int index) const;
|
||||||
void setBindingParameterCount();
|
void setBindingParameterCount();
|
||||||
void setBindingColumnNamesFromStatement();
|
void setBindingColumnNamesFromStatement();
|
||||||
void setColumnCount();
|
void setColumnCount();
|
||||||
bool isReadOnlyStatement() const;
|
bool isReadOnlyStatement() const;
|
||||||
Q_NORETURN void throwException(const char *whatHasHappened) const;
|
[[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwStatementHasError(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwBindingIndexIsOutOfRange(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwUnknowError(const char *whatHasHappened) const;
|
||||||
|
[[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const;
|
||||||
|
|
||||||
template <typename ContainerType>
|
template <typename ContainerType>
|
||||||
ContainerType columnValues(const std::vector<int> &columnIndices) const;
|
ContainerType columnValues(const std::vector<int> &columnIndices) const;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ WriteStatement::WriteStatement(Utils::SmallStringView sqlStatement,
|
|||||||
void WriteStatement::checkIsWritableStatement()
|
void WriteStatement::checkIsWritableStatement()
|
||||||
{
|
{
|
||||||
if (isReadOnlyStatement())
|
if (isReadOnlyStatement())
|
||||||
throwException("SqliteStatement::SqliteWriteStatement: is not a writable statement!");
|
throw NotWriteSqlStatement("SqliteStatement::SqliteWriteStatement: is not a writable statement!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Sqlite
|
} // namespace Sqlite
|
||||||
|
|||||||
@@ -61,19 +61,21 @@ using SqliteDatabaseBackendSlowTest = SqliteDatabaseBackend;
|
|||||||
|
|
||||||
TEST_F(SqliteDatabaseBackend, OpenAlreadyOpenDatabase)
|
TEST_F(SqliteDatabaseBackend, OpenAlreadyOpenDatabase)
|
||||||
{
|
{
|
||||||
ASSERT_THROW(databaseBackend.open(databaseFilePath, OpenMode::ReadWrite), Exception);
|
ASSERT_THROW(databaseBackend.open(databaseFilePath, OpenMode::ReadWrite),
|
||||||
|
Sqlite::DatabaseIsAlreadyOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteDatabaseBackend, CloseAlreadyClosedDatabase)
|
TEST_F(SqliteDatabaseBackend, CloseAlreadyClosedDatabase)
|
||||||
{
|
{
|
||||||
databaseBackend.close();
|
databaseBackend.close();
|
||||||
|
|
||||||
ASSERT_THROW(databaseBackend.close(), Exception);
|
ASSERT_THROW(databaseBackend.close(), Sqlite::DatabaseIsAlreadyClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteDatabaseBackend, OpenWithWrongPath)
|
TEST_F(SqliteDatabaseBackend, OpenWithWrongPath)
|
||||||
{
|
{
|
||||||
ASSERT_THROW(databaseBackend.open("/xxx/SqliteDatabaseBackendTest.db", OpenMode::ReadWrite), Exception);
|
ASSERT_THROW(databaseBackend.open("/xxx/SqliteDatabaseBackendTest.db", OpenMode::ReadWrite),
|
||||||
|
Sqlite::WrongFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteDatabaseBackend, DefaultJournalMode)
|
TEST_F(SqliteDatabaseBackend, DefaultJournalMode)
|
||||||
@@ -148,7 +150,8 @@ TEST_F(SqliteDatabaseBackend, TextEncodingCannotBeChangedAfterTouchingDatabase)
|
|||||||
|
|
||||||
databaseBackend.execute("CREATE TABLE text(name, number)");
|
databaseBackend.execute("CREATE TABLE text(name, number)");
|
||||||
|
|
||||||
ASSERT_THROW(databaseBackend.setTextEncoding(TextEncoding::Utf16), Exception);
|
ASSERT_THROW(databaseBackend.setTextEncoding(TextEncoding::Utf16),
|
||||||
|
Sqlite::PragmaValueNotSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteDatabaseBackend, OpenModeReadOnly)
|
TEST_F(SqliteDatabaseBackend, OpenModeReadOnly)
|
||||||
|
|||||||
@@ -87,12 +87,21 @@ struct Output
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(SqliteStatement, PrepareFailure)
|
TEST_F(SqliteStatement, ThrowsStatementHasErrorForWrongSqlStatement)
|
||||||
{
|
{
|
||||||
ASSERT_THROW(ReadStatement("blah blah blah", database), Exception);
|
ASSERT_THROW(ReadStatement("blah blah blah", database), Sqlite::StatementHasError);
|
||||||
ASSERT_THROW(WriteStatement("blah blah blah", database), Exception);
|
}
|
||||||
ASSERT_THROW(ReadStatement("INSERT INTO test(name, number) VALUES (?, ?)", database), Exception);
|
|
||||||
ASSERT_THROW(WriteStatement("SELECT name, number FROM test '", database), Exception);
|
TEST_F(SqliteStatement, ThrowsNotReadOnlySqlStatementForWritableSqlStatementInReadStatement)
|
||||||
|
{
|
||||||
|
ASSERT_THROW(ReadStatement("INSERT INTO test(name, number) VALUES (?, ?)", database),
|
||||||
|
Sqlite::NotReadOnlySqlStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, ThrowsNotReadonlySqlStatementForWritableSqlStatementInReadStatement)
|
||||||
|
{
|
||||||
|
ASSERT_THROW(WriteStatement("SELECT name, number FROM test", database),
|
||||||
|
Sqlite::NotWriteSqlStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, CountRows)
|
TEST_F(SqliteStatement, CountRows)
|
||||||
@@ -124,21 +133,35 @@ TEST_F(SqliteStatement, Value)
|
|||||||
ASSERT_THAT(statement.text(1), "23.3");
|
ASSERT_THAT(statement.text(1), "23.3");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, ValueFailure)
|
TEST_F(SqliteStatement, ThrowNoValuesToFetchForNotSteppedStatement)
|
||||||
{
|
{
|
||||||
ReadStatement statement("SELECT name, number FROM test", database);
|
ReadStatement statement("SELECT name, number FROM test", database);
|
||||||
ASSERT_THROW(statement.value<int>(0), Exception);
|
|
||||||
|
|
||||||
statement.reset();
|
ASSERT_THROW(statement.value<int>(0), Sqlite::NoValuesToFetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, ThrowNoValuesToFetchForDoneStatement)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT name, number FROM test", database);
|
||||||
while (statement.next()) {}
|
while (statement.next()) {}
|
||||||
ASSERT_THROW(statement.value<int>(0), Exception);
|
|
||||||
|
|
||||||
statement.reset();
|
ASSERT_THROW(statement.value<int>(0), Sqlite::NoValuesToFetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, ThrowInvalidColumnFetchedForNegativeColumn)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT name, number FROM test", database);
|
||||||
statement.next();
|
statement.next();
|
||||||
ASSERT_THROW(statement.value<int>(-1), Exception);
|
|
||||||
ASSERT_THROW(statement.value<int>(2), Exception);
|
ASSERT_THROW(statement.value<int>(-1), Sqlite::InvalidColumnFetched);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, ThrowInvalidColumnFetchedForNotExistingColumn)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT name, number FROM test", database);
|
||||||
|
statement.next();
|
||||||
|
|
||||||
|
ASSERT_THROW(statement.value<int>(2), Sqlite::InvalidColumnFetched);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, ToIntergerValue)
|
TEST_F(SqliteStatement, ToIntergerValue)
|
||||||
@@ -245,13 +268,25 @@ TEST_F(SqliteStatement, BindDoubleByIndex)
|
|||||||
ASSERT_THAT(statement.text(0), "foo");
|
ASSERT_THAT(statement.text(0), "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindFailure)
|
TEST_F(SqliteStatement, BindIndexIsZeroIsThrowingBindingIndexIsOutOfBound)
|
||||||
{
|
{
|
||||||
ReadStatement statement("SELECT name, number FROM test WHERE number=@number", database);
|
ReadStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
ASSERT_THROW(statement.bind(0, 40), Exception);
|
ASSERT_THROW(statement.bind(0, 40), Sqlite::BindingIndexIsOutOfRange);
|
||||||
ASSERT_THROW(statement.bind(2, 40), Exception);
|
}
|
||||||
ASSERT_THROW(statement.bind("@name", 40), Exception);
|
|
||||||
|
TEST_F(SqliteStatement, BindIndexIsTpLargeIsThrowingBindingIndexIsOutOfBound)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
|
ASSERT_THROW(statement.bind(2, 40), Sqlite::BindingIndexIsOutOfRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WrongBindingNameThrowingBindingIndexIsOutOfBound)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT name, number FROM test WHERE number=@name", database);
|
||||||
|
|
||||||
|
ASSERT_THROW(statement.bind("@name2", 40), Sqlite::WrongBingingName);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, RequestBindingNamesFromStatement)
|
TEST_F(SqliteStatement, RequestBindingNamesFromStatement)
|
||||||
@@ -299,13 +334,20 @@ TEST_F(SqliteStatement, WriteNamedValues)
|
|||||||
ASSERT_THAT(statement, HasValues("see", "7.23", 1));
|
ASSERT_THAT(statement, HasValues("see", "7.23", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, ClosedDatabase)
|
TEST_F(SqliteStatement, CannotWriteToClosedDatabase)
|
||||||
{
|
{
|
||||||
database.close();
|
database.close();
|
||||||
ASSERT_THROW(WriteStatement("INSERT INTO test(name, number) VALUES (?, ?)", database), Exception);
|
|
||||||
ASSERT_THROW(ReadStatement("SELECT * FROM test", database), Exception);
|
ASSERT_THROW(WriteStatement("INSERT INTO test(name, number) VALUES (?, ?)", database),
|
||||||
ASSERT_THROW(ReadWriteStatement("INSERT INTO test(name, number) VALUES (?, ?)", database), Exception);
|
Sqlite::DatabaseIsNotOpen);
|
||||||
database.open(QDir::tempPath() + QStringLiteral("/SqliteStatementTest.db"));
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, CannotReadFromClosedDatabase)
|
||||||
|
{
|
||||||
|
database.close();
|
||||||
|
|
||||||
|
ASSERT_THROW(ReadStatement("SELECT * FROM test", database),
|
||||||
|
Sqlite::DatabaseIsNotOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetTupleValuesWithoutArguments)
|
TEST_F(SqliteStatement, GetTupleValuesWithoutArguments)
|
||||||
@@ -461,7 +503,8 @@ void SqliteStatement::SetUp()
|
|||||||
|
|
||||||
void SqliteStatement::TearDown()
|
void SqliteStatement::TearDown()
|
||||||
{
|
{
|
||||||
database.close();
|
if (database.isOpen())
|
||||||
|
database.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user