aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Bubke <marco.bubke@qt.io>2021-02-10 15:25:00 +0100
committerMarco Bubke <marco.bubke@qt.io>2021-02-15 10:39:30 +0000
commit18fe4233f8a5dedb3740d59af73ae6f312c591a9 (patch)
treec4a09c5f141f5b0cf2ed78ebd31a1aaed2e6cb96
parent2197eeb4aa8d7a44c444e3646138c69559c9176e (diff)
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>
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h34
-rw-r--r--src/libs/sqlite/sqliteglobal.h2
-rw-r--r--src/libs/sqlite/sqlitereadstatement.h3
-rw-r--r--src/libs/sqlite/sqlitereadwritestatement.h3
-rw-r--r--tests/unit/unittest/mocksqlitestatement.h1
-rw-r--r--tests/unit/unittest/sqlitestatement-test.cpp86
6 files changed, 126 insertions, 3 deletions
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index 42063ce0b8..3b68d8a3fc 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -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)
{
diff --git a/src/libs/sqlite/sqliteglobal.h b/src/libs/sqlite/sqliteglobal.h
index 308bc07fe0..6f6ecba542 100644
--- a/src/libs/sqlite/sqliteglobal.h
+++ b/src/libs/sqlite/sqliteglobal.h
@@ -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
diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h
index 310f91fbc9..9314b54e0f 100644
--- a/src/libs/sqlite/sqlitereadstatement.h
+++ b/src/libs/sqlite/sqlitereadstatement.h
@@ -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();
diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h
index caa9cbe2a0..f7e23e100c 100644
--- a/src/libs/sqlite/sqlitereadwritestatement.h
+++ b/src/libs/sqlite/sqlitereadwritestatement.h
@@ -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;
};
diff --git a/tests/unit/unittest/mocksqlitestatement.h b/tests/unit/unittest/mocksqlitestatement.h
index 7be7f3f05e..767840ee8f 100644
--- a/tests/unit/unittest/mocksqlitestatement.h
+++ b/tests/unit/unittest/mocksqlitestatement.h
@@ -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>
diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp
index a6c8b526d0..6749742b77 100644
--- a/tests/unit/unittest/sqlitestatement-test.cpp
+++ b/tests/unit/unittest/sqlitestatement-test.cpp
@@ -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