Commit a83d0382 authored by Marco Bubke's avatar Marco Bubke Committed by Tim Jenssen

Sqlite: Improve exception handling

Introducing different exceptions for different error cases.

Change-Id: I4371d1e64d9dca2a9f68dcbaa4a891c55879c1f5
Reviewed-by: Tim Jenssen's avatarTim Jenssen <tim.jenssen@qt.io>
Reviewed-by: Marco Bubke's avatarMarco Bubke <marco.bubke@qt.io>
parent bf5bc72b
......@@ -31,6 +31,7 @@
#include "sqlitestatement.h"
#include "sqlitewritestatement.h"
#include <QFileInfo>
#include <QThread>
#include <QDebug>
......@@ -230,24 +231,29 @@ void DatabaseBackend::cacheTextEncoding()
void DatabaseBackend::checkForOpenDatabaseWhichCanBeClosed()
{
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)
{
switch (resultCode) {
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)
{
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())
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)
......@@ -262,16 +268,16 @@ void DatabaseBackend::checkDatabaseCouldBeOpened(int resultCode)
}
void DatabaseBackend::checkPragmaValue(Utils::SmallStringView databaseValue,
Utils::SmallStringView expectedValue)
Utils::SmallStringView expectedValue)
{
if (databaseValue != expectedValue)
throwException("SqliteDatabaseBackend::setPragmaValue: pragma value is not set!");
throw PragmaValueNotSet("SqliteDatabaseBackend::setPragmaValue: pragma value is not set!");
}
void DatabaseBackend::checkDatabaseHandleIsNotNull()
{
if (m_databaseHandle == nullptr)
throwException("SqliteDatabaseBackend: database is not open!");
throwDatabaseIsNotOpen("SqliteDatabaseBackend: database is not open!");
}
void DatabaseBackend::checkIfMultithreadingIsActivated(int resultCode)
......@@ -392,6 +398,16 @@ void DatabaseBackend::throwException(const char *whatHasHappens) const
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>
Type DatabaseBackend::toValue(Utils::SmallStringView sqlStatement)
{
......
......@@ -110,8 +110,9 @@ protected:
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:
Database &m_database;
......
......@@ -48,4 +48,211 @@ private:
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
......@@ -39,7 +39,7 @@ ReadStatement::ReadStatement(Utils::SmallStringView sqlStatement,
void ReadStatement::checkIsReadOnlyStatement()
{
if (!isReadOnlyStatement())
throwException("SqliteStatement::SqliteReadStatement: is not read only statement!");
throw NotReadOnlySqlStatement("SqliteStatement::SqliteReadStatement: is not read only statement!");
}
} // namespace Sqlite
......@@ -95,19 +95,26 @@ private:
void Statement::waitForUnlockNotify() const
{
UnlockNotification unlockNotification;
int resultCode = sqlite3_unlock_notify(sqliteDatabaseHandle(), UnlockNotification::unlockNotifyCallBack, &unlockNotification);
int resultCode = sqlite3_unlock_notify(sqliteDatabaseHandle(),
UnlockNotification::unlockNotifyCallBack,
&unlockNotification);
if (resultCode == SQLITE_OK)
unlockNotification.wait();
else
throwException("SqliteStatement::waitForUnlockNotify: database is in a dead lock!");
if (resultCode == SQLITE_LOCKED)
throw DeadLock("SqliteStatement::waitForUnlockNotify: database is in a dead lock!");
unlockNotification.wait();
}
void Statement::reset() const
{
int resultCode = sqlite3_reset(m_compiledStatement.get());
if (resultCode != SQLITE_OK)
throwException("SqliteStatement::reset: can't reset statement!");
switch (resultCode) {
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;
}
......@@ -159,30 +166,30 @@ Utils::SmallStringVector Statement::columnNames() const
void Statement::bind(int index, int value)
{
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
throwException("SqliteStatement::bind: cant' bind 32 bit integer!");
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
checkForBindingError(resultCode);
}
void Statement::bind(int index, long long value)
{
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
throwException("SqliteStatement::bind: cant' bind 64 bit integer!");
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
checkForBindingError(resultCode);
}
void Statement::bind(int index, double value)
{
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
throwException("SqliteStatement::bind: cant' bind double!");
checkForBindingError(resultCode);
}
void Statement::bind(int index, Utils::SmallStringView text)
{
int resultCode = sqlite3_bind_text(m_compiledStatement.get(), index, text.data(), int(text.size()), SQLITE_TRANSIENT);
if (resultCode != SQLITE_OK)
throwException("SqliteStatement::bind: cant' bind double!");
int resultCode = sqlite3_bind_text(m_compiledStatement.get(),
index,
text.data(),
int(text.size()),
SQLITE_TRANSIENT);
checkForBindingError(resultCode);
}
template <typename Type>
......@@ -250,13 +257,13 @@ bool Statement::checkForStepError(int resultCode) const
switch (resultCode) {
case SQLITE_ROW: return true;
case SQLITE_DONE: return false;
case SQLITE_BUSY: throwException("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_MISUSE: throwException("SqliteStatement::stepStatement: was called inappropriately!");
case SQLITE_CONSTRAINT: throwException("SqliteStatement::stepStatement: contraint prevent insert or update!");
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!");
}
throwException("SqliteStatement::stepStatement: unknown error has happened");
throwUnknowError("SqliteStatement::stepStatement: unknown error has happened");
Q_UNREACHABLE();
}
......@@ -265,12 +272,24 @@ void Statement::checkForPrepareError(int resultCode) const
{
switch (resultCode) {
case SQLITE_OK: return;
case SQLITE_BUSY: throwException("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_MISUSE: throwException("SqliteStatement::prepareStatement: was called inappropriately!");
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!");
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!");
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!");
}
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();
}
throwException("SqliteStatement::prepareStatement: unknown error has happened");
throwUnknowError("SqliteStatement::bind: unknown error has happened");
}
void Statement::setIfIsReadyToFetchValues(int resultCode) const
......@@ -285,33 +304,27 @@ void Statement::setIfIsReadyToFetchValues(int resultCode) const
void Statement::checkIfIsReadyToFetchValues() const
{
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
{
for (int column : columns) {
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
{
if (column < 0 || column >= m_columnCount)
throwException("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!");
throwInvalidColumnFetched("SqliteStatement::values: column index out of bound!");
}
void Statement::checkBindingName(int index) const
{
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()
......@@ -345,9 +358,57 @@ bool Statement::isReadOnlyStatement() const
return sqlite3_stmt_readonly(m_compiledStatement.get());
}
void Statement::throwException(const char *whatHasHappened) const
void Statement::throwStatementIsBusy(const char *whatHasHappened) const
{
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 Exception(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
}
QString Statement::columnName(int column) const
......
......@@ -355,17 +355,26 @@ protected:
bool checkForStepError(int resultCode) const;
void checkForPrepareError(int resultCode) const;
void checkForBindingError(int resultCode) const;
void setIfIsReadyToFetchValues(int resultCode) const;
void checkIfIsReadyToFetchValues() const;
void checkColumnsAreValid(const std::vector<int> &columns) const;
void checkColumnIsValid(int column) const;
void checkBindingIndex(int index) const;
void checkBindingName(int index) const;
void setBindingParameterCount();
void setBindingColumnNamesFromStatement();
void setColumnCount();
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>
ContainerType columnValues(const std::vector<int> &columnIndices) const;
......
......@@ -37,7 +37,7 @@ WriteStatement::WriteStatement(Utils::SmallStringView sqlStatement,
void WriteStatement::checkIsWritableStatement()
{
if (isReadOnlyStatement())
throwException("SqliteStatement::SqliteWriteStatement: is not a writable statement!");
throw NotWriteSqlStatement("SqliteStatement::SqliteWriteStatement: is not a writable statement!");
}
} // namespace Sqlite
......@@ -61,19 +61,21 @@ using SqliteDatabaseBackendSlowTest = SqliteDatabaseBackend;
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)
{
databaseBackend.close();
ASSERT_THROW(databaseBackend.close(), Exception);
ASSERT_THROW(databaseBackend.close(), Sqlite::DatabaseIsAlreadyClosed);
}
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)
......@@ -148,7 +150,8 @@ TEST_F(SqliteDatabaseBackend, TextEncodingCannotBeChangedAfterTouchingDatabase)
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)
......
......@@ -87,12 +87,21 @@ struct Output
}
};
TEST_F(SqliteStatement, PrepareFailure)
TEST_F(SqliteStatement, ThrowsStatementHasErrorForWrongSqlStatement)
{
ASSERT_THROW(ReadStatement("blah blah blah", database), Exception);
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);
ASSERT_THROW(ReadStatement("blah blah blah", database), Sqlite::StatementHasError);
}
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)
......@@ -124,21 +133,35 @@ TEST_F(SqliteStatement, Value)
ASSERT_THAT(statement.text(1), "23.3");
}
TEST_F(SqliteStatement, ValueFailure)
TEST_F(SqliteStatement, ThrowNoValuesToFetchForNotSteppedStatement)
{
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()) {}
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();
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)
......@@ -245,13 +268,25 @@ TEST_F(SqliteStatement, BindDoubleByIndex)
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(2, 40), Exception);
ASSERT_THROW(statement.bind("@name", 40), Exception);
ASSERT_THROW(statement.bind(0, 40), Sqlite::BindingIndexIsOutOfRange);
}
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);