forked from qt-creator/qt-creator
Sqlite: Add callback method
Sometimes it is better to have a callback instead of returning a container. The call has to manage the state if an exception is called but otherwise it will reduce the memory footprint. There will be to a RETURNING to Sqlite which will read back values as you write. Change-Id: I7eb49850e2c76f883a03277b31c5e713e9774c92 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -37,9 +37,10 @@
|
||||
#include <utils/span.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
using std::int64_t;
|
||||
|
||||
@@ -316,6 +317,25 @@ public:
|
||||
return statement.template fetchValue<Type>(0);
|
||||
}
|
||||
|
||||
template<int ResultTypeCount = 1, typename Callable, typename... QueryTypes>
|
||||
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
|
||||
{
|
||||
BaseStatement::checkColumnCount(ResultTypeCount);
|
||||
|
||||
Resetter resetter{*this};
|
||||
|
||||
bindValues(queryValues...);
|
||||
|
||||
while (BaseStatement::next()) {
|
||||
auto control = callCallable<ResultTypeCount>(callable);
|
||||
|
||||
if (control == CallbackControl::Abort)
|
||||
break;
|
||||
}
|
||||
|
||||
resetter.reset();
|
||||
}
|
||||
|
||||
protected:
|
||||
~StatementImplementation() = default;
|
||||
|
||||
@@ -398,6 +418,18 @@ private:
|
||||
return assignValue<ResultOptionalType>(std::make_integer_sequence<int, ResultTypeCount>{});
|
||||
}
|
||||
|
||||
template<typename Callable, int... ColumnIndices>
|
||||
CallbackControl callCallable(Callable &&callable, std::integer_sequence<int, ColumnIndices...>)
|
||||
{
|
||||
return std::invoke(callable, ValueGetter(*this, ColumnIndices)...);
|
||||
}
|
||||
|
||||
template<int ResultTypeCount, typename Callable>
|
||||
CallbackControl callCallable(Callable &&callable)
|
||||
{
|
||||
return callCallable(callable, std::make_integer_sequence<int, ResultTypeCount>{});
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void bindValuesByIndex(int index, const ValueType &value)
|
||||
{
|
||||
|
@@ -68,4 +68,6 @@ enum class ChangeType : int { Delete = 9, Insert = 18, Update = 23 };
|
||||
|
||||
enum class byte : unsigned char {};
|
||||
|
||||
enum class CallbackControl : unsigned char { Continue, Abort };
|
||||
|
||||
} // namespace Sqlite
|
||||
|
@@ -34,9 +34,10 @@ class SQLITE_EXPORT ReadStatement final : protected StatementImplementation<Base
|
||||
public:
|
||||
explicit ReadStatement(Utils::SmallStringView sqlStatement, Database &database);
|
||||
|
||||
using StatementImplementation::readCallback;
|
||||
using StatementImplementation::toValue;
|
||||
using StatementImplementation::value;
|
||||
using StatementImplementation::values;
|
||||
using StatementImplementation::toValue;
|
||||
|
||||
protected:
|
||||
void checkIsReadOnlyStatement();
|
||||
|
@@ -37,9 +37,10 @@ public:
|
||||
ReadWriteStatement(Utils::SmallStringView sqlStatement, Database &database);
|
||||
|
||||
using StatementImplementation::execute;
|
||||
using StatementImplementation::readCallback;
|
||||
using StatementImplementation::toValue;
|
||||
using StatementImplementation::value;
|
||||
using StatementImplementation::values;
|
||||
using StatementImplementation::toValue;
|
||||
using StatementImplementation::write;
|
||||
};
|
||||
|
||||
|
@@ -42,6 +42,7 @@ public:
|
||||
MOCK_CONST_METHOD1(fetchLongLongValue, long long (int));
|
||||
MOCK_CONST_METHOD1(fetchDoubleValue, double (int));
|
||||
MOCK_CONST_METHOD1(fetchSmallStringValue, Utils::SmallString (int));
|
||||
MOCK_CONST_METHOD1(fetchSmallStringViewValue, Utils::SmallStringView(int));
|
||||
MOCK_CONST_METHOD1(fetchPathStringValue, Utils::PathString (int));
|
||||
|
||||
template<typename Type>
|
||||
|
@@ -946,4 +946,90 @@ TEST_F(SqliteStatement, ThrowInvalidColumnFetchedForToManyArgumentsForToValues)
|
||||
Sqlite::ColumnCountDoesNotMatch);
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallback)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
ReadStatement statement("SELECT name, value FROM test", database);
|
||||
|
||||
EXPECT_CALL(callbackMock, Call(Eq("bar"), Eq(1)));
|
||||
EXPECT_CALL(callbackMock, Call(Eq("foo"), Eq(2)));
|
||||
EXPECT_CALL(callbackMock, Call(Eq("poo"), Eq(3)));
|
||||
|
||||
statement.readCallback<2>(callbackMock.AsStdFunction());
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallbackCalledWithArguments)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
ReadStatement statement("SELECT name, value FROM test WHERE value=?", database);
|
||||
|
||||
EXPECT_CALL(callbackMock, Call(Eq("foo"), Eq(2)));
|
||||
|
||||
statement.readCallback<2>(callbackMock.AsStdFunction(), 2);
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallbackAborts)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
ReadStatement statement("SELECT name, value FROM test ORDER BY name", database);
|
||||
|
||||
EXPECT_CALL(callbackMock, Call(Eq("bar"), Eq(1)));
|
||||
EXPECT_CALL(callbackMock, Call(Eq("foo"), Eq(2))).WillOnce(Return(Sqlite::CallbackControl::Abort));
|
||||
EXPECT_CALL(callbackMock, Call(Eq("poo"), Eq(3))).Times(0);
|
||||
|
||||
statement.readCallback<2>(callbackMock.AsStdFunction());
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ThrowInvalidColumnFetchedForToManyArgumentsForReadCallback)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView)> callbackMock;
|
||||
SqliteTestStatement statement("SELECT name, number FROM test", database);
|
||||
|
||||
ASSERT_THROW(statement.readCallback<1>(callbackMock.AsStdFunction()),
|
||||
Sqlite::ColumnCountDoesNotMatch);
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallbackCallsResetAfterCallbacks)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
MockSqliteStatement mockStatement;
|
||||
|
||||
EXPECT_CALL(mockStatement, reset());
|
||||
|
||||
mockStatement.readCallback<2>(callbackMock.AsStdFunction());
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallbackCallsResetAfterCallbacksAborts)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
MockSqliteStatement mockStatement;
|
||||
ON_CALL(callbackMock, Call(_, _)).WillByDefault(Return(Sqlite::CallbackControl::Abort));
|
||||
|
||||
EXPECT_CALL(mockStatement, reset());
|
||||
|
||||
mockStatement.readCallback<2>(callbackMock.AsStdFunction());
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallbackThrowsForError)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
MockSqliteStatement mockStatement;
|
||||
ON_CALL(mockStatement, next()).WillByDefault(Throw(Sqlite::StatementHasError("")));
|
||||
|
||||
ASSERT_THROW(mockStatement.readCallback<2>(callbackMock.AsStdFunction()),
|
||||
Sqlite::StatementHasError);
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, ReadCallbackCallsResetIfExceptionIsThrown)
|
||||
{
|
||||
MockFunction<Sqlite::CallbackControl(Utils::SmallStringView, long long)> callbackMock;
|
||||
MockSqliteStatement mockStatement;
|
||||
ON_CALL(mockStatement, next()).WillByDefault(Throw(Sqlite::StatementHasError("")));
|
||||
|
||||
EXPECT_CALL(mockStatement, reset());
|
||||
|
||||
EXPECT_THROW(mockStatement.readCallback<2>(callbackMock.AsStdFunction()),
|
||||
Sqlite::StatementHasError);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user