diff options
author | Marco Bubke <marco.bubke@qt.io> | 2021-02-10 15:25:00 +0100 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2021-02-15 10:39:30 +0000 |
commit | 18fe4233f8a5dedb3740d59af73ae6f312c591a9 (patch) | |
tree | c4a09c5f141f5b0cf2ed78ebd31a1aaed2e6cb96 | |
parent | 2197eeb4aa8d7a44c444e3646138c69559c9176e (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.h | 34 | ||||
-rw-r--r-- | src/libs/sqlite/sqliteglobal.h | 2 | ||||
-rw-r--r-- | src/libs/sqlite/sqlitereadstatement.h | 3 | ||||
-rw-r--r-- | src/libs/sqlite/sqlitereadwritestatement.h | 3 | ||||
-rw-r--r-- | tests/unit/unittest/mocksqlitestatement.h | 1 | ||||
-rw-r--r-- | tests/unit/unittest/sqlitestatement-test.cpp | 86 |
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 |