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:
Marco Bubke
2017-09-18 11:23:09 +02:00
committed by Tim Jenssen
parent bf5bc72b5a
commit a83d038208
9 changed files with 419 additions and 79 deletions

View File

@@ -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)
{ {

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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();
} }
} }