forked from qt-creator/qt-creator
Sqlite: Add range getter
Sometimes it is handy to return a range so you can break in a for range
loop. So you can now write:
for (auto [key, value] :
statement.range<std::tuple<long long, std::string>>()) {
values.push_back(value);
if (value == Tuple{"foo", 23.3, 2})
break;
}
for (auto [key, value] :
statement.rangeWithTransaction<std::tuple<long long, std::string>>()) {
values.push_back(value);
if (value == Tuple{"foo", 23.3, 2})
break;
}
It will only step over the statement to the break. So you don't fetch
not used values anymore. The second version will add a transaction
around the range.
But be careful. The range is view to the statement. Holding it longer or
reusing it can lead to strange behavior because the state of the
statement is changing.
Change-Id: I90191f7da5a015c7d8077d5bc428655a2b53246c
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "sqliteblob.h"
|
#include "sqliteblob.h"
|
||||||
#include "sqliteexception.h"
|
#include "sqliteexception.h"
|
||||||
|
#include "sqlitetransaction.h"
|
||||||
#include "sqlitevalue.h"
|
#include "sqlitevalue.h"
|
||||||
|
|
||||||
#include <utils/smallstringvector.h>
|
#include <utils/smallstringvector.h>
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
#include <utils/span.h>
|
#include <utils/span.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@@ -163,13 +165,14 @@ extern template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils:
|
|||||||
template<typename BaseStatement, int ResultCount>
|
template<typename BaseStatement, int ResultCount>
|
||||||
class StatementImplementation : public BaseStatement
|
class StatementImplementation : public BaseStatement
|
||||||
{
|
{
|
||||||
|
struct Resetter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using BaseStatement::BaseStatement;
|
using BaseStatement::BaseStatement;
|
||||||
|
|
||||||
void execute()
|
void execute()
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
BaseStatement::next();
|
BaseStatement::next();
|
||||||
resetter.reset();
|
resetter.reset();
|
||||||
}
|
}
|
||||||
@@ -185,7 +188,7 @@ public:
|
|||||||
template<typename... ValueType>
|
template<typename... ValueType>
|
||||||
void write(const ValueType&... values)
|
void write(const ValueType&... values)
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
bindValuesByIndex(1, values...);
|
bindValuesByIndex(1, values...);
|
||||||
BaseStatement::next();
|
BaseStatement::next();
|
||||||
resetter.reset();
|
resetter.reset();
|
||||||
@@ -194,7 +197,7 @@ public:
|
|||||||
template<typename ResultType>
|
template<typename ResultType>
|
||||||
std::vector<ResultType> values(std::size_t reserveSize)
|
std::vector<ResultType> values(std::size_t reserveSize)
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
std::vector<ResultType> resultValues;
|
std::vector<ResultType> resultValues;
|
||||||
resultValues.reserve(std::max(reserveSize, m_maximumResultCount));
|
resultValues.reserve(std::max(reserveSize, m_maximumResultCount));
|
||||||
|
|
||||||
@@ -211,7 +214,7 @@ public:
|
|||||||
template<typename ResultType, typename... QueryTypes>
|
template<typename ResultType, typename... QueryTypes>
|
||||||
auto values(std::size_t reserveSize, const QueryTypes &...queryValues)
|
auto values(std::size_t reserveSize, const QueryTypes &...queryValues)
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
std::vector<ResultType> resultValues;
|
std::vector<ResultType> resultValues;
|
||||||
resultValues.reserve(std::max(reserveSize, m_maximumResultCount));
|
resultValues.reserve(std::max(reserveSize, m_maximumResultCount));
|
||||||
|
|
||||||
@@ -230,13 +233,13 @@ public:
|
|||||||
template<typename ResultType, typename... QueryTypes>
|
template<typename ResultType, typename... QueryTypes>
|
||||||
auto value(const QueryTypes &...queryValues)
|
auto value(const QueryTypes &...queryValues)
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
Utils::optional<ResultType> resultValue;
|
Utils::optional<ResultType> resultValue;
|
||||||
|
|
||||||
bindValues(queryValues...);
|
bindValues(queryValues...);
|
||||||
|
|
||||||
if (BaseStatement::next())
|
if (BaseStatement::next())
|
||||||
resultValue = assignValue<Utils::optional<ResultType>>();
|
resultValue = createOptionalValue<Utils::optional<ResultType>>();
|
||||||
|
|
||||||
resetter.reset();
|
resetter.reset();
|
||||||
|
|
||||||
@@ -258,7 +261,7 @@ public:
|
|||||||
template<typename Callable, typename... QueryTypes>
|
template<typename Callable, typename... QueryTypes>
|
||||||
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
|
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
|
|
||||||
bindValues(queryValues...);
|
bindValues(queryValues...);
|
||||||
|
|
||||||
@@ -275,7 +278,7 @@ public:
|
|||||||
template<typename Container, typename... QueryTypes>
|
template<typename Container, typename... QueryTypes>
|
||||||
void readTo(Container &container, const QueryTypes &...queryValues)
|
void readTo(Container &container, const QueryTypes &...queryValues)
|
||||||
{
|
{
|
||||||
Resetter resetter{*this};
|
Resetter resetter{this};
|
||||||
|
|
||||||
bindValues(queryValues...);
|
bindValues(queryValues...);
|
||||||
|
|
||||||
@@ -285,39 +288,187 @@ public:
|
|||||||
resetter.reset();
|
resetter.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename ResultType, typename... QueryTypes>
|
||||||
|
auto range(const QueryTypes &...queryValues)
|
||||||
|
{
|
||||||
|
return SqliteResultRange<ResultType>{*this, queryValues...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ResultType, typename... QueryTypes>
|
||||||
|
auto rangeWithTransaction(const QueryTypes &...queryValues)
|
||||||
|
{
|
||||||
|
return SqliteResultRangeWithTransaction<ResultType>{*this, queryValues...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ResultType>
|
||||||
|
class BaseSqliteResultRange
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class SqliteResultIteratator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using difference_type = int;
|
||||||
|
using value_type = ResultType;
|
||||||
|
using pointer = ResultType *;
|
||||||
|
using reference = ResultType &;
|
||||||
|
|
||||||
|
SqliteResultIteratator(StatementImplementation &statement)
|
||||||
|
: m_statement{statement}
|
||||||
|
, m_hasNext{m_statement.next()}
|
||||||
|
{}
|
||||||
|
|
||||||
|
SqliteResultIteratator(StatementImplementation &statement, bool hasNext)
|
||||||
|
: m_statement{statement}
|
||||||
|
, m_hasNext{hasNext}
|
||||||
|
{}
|
||||||
|
|
||||||
|
SqliteResultIteratator &operator++()
|
||||||
|
{
|
||||||
|
m_hasNext = m_statement.next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator++(int) { m_hasNext = m_statement.next(); }
|
||||||
|
|
||||||
|
friend bool operator==(const SqliteResultIteratator &first,
|
||||||
|
const SqliteResultIteratator &second)
|
||||||
|
{
|
||||||
|
return first.m_hasNext == second.m_hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const SqliteResultIteratator &first,
|
||||||
|
const SqliteResultIteratator &second)
|
||||||
|
{
|
||||||
|
return !(first == second);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type operator*() const { return m_statement.createValue<ResultType>(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatementImplementation &m_statement;
|
||||||
|
bool m_hasNext = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
using value_type = ResultType;
|
||||||
|
using iterator = SqliteResultIteratator;
|
||||||
|
using const_iterator = iterator;
|
||||||
|
|
||||||
|
template<typename... QueryTypes>
|
||||||
|
BaseSqliteResultRange(StatementImplementation &statement, const QueryTypes &...queryValues)
|
||||||
|
: m_statement{statement}
|
||||||
|
{
|
||||||
|
statement.bindValues(queryValues...);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseSqliteResultRange(BaseSqliteResultRange &) = delete;
|
||||||
|
BaseSqliteResultRange &operator=(BaseSqliteResultRange &) = delete;
|
||||||
|
|
||||||
|
BaseSqliteResultRange(BaseSqliteResultRange &&other)
|
||||||
|
: m_statement{std::move(other.resetter)}
|
||||||
|
{}
|
||||||
|
BaseSqliteResultRange &operator=(BaseSqliteResultRange &&) = delete;
|
||||||
|
|
||||||
|
iterator begin() & { return iterator{m_statement}; }
|
||||||
|
iterator end() & { return iterator{m_statement, false}; }
|
||||||
|
|
||||||
|
const_iterator begin() const & { return iterator{m_statement}; }
|
||||||
|
const_iterator end() const & { return iterator{m_statement, false}; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatementImplementation &m_statement;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ResultType>
|
||||||
|
class SqliteResultRange : public BaseSqliteResultRange<ResultType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename... QueryTypes>
|
||||||
|
SqliteResultRange(StatementImplementation &statement, const QueryTypes &...queryValues)
|
||||||
|
: BaseSqliteResultRange<ResultType>{statement}
|
||||||
|
, resetter{&statement}
|
||||||
|
|
||||||
|
{
|
||||||
|
statement.bindValues(queryValues...);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SqliteResultRange()
|
||||||
|
{
|
||||||
|
if (!std::uncaught_exceptions())
|
||||||
|
resetter.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Resetter resetter;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ResultType>
|
||||||
|
class SqliteResultRangeWithTransaction : public BaseSqliteResultRange<ResultType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename... QueryTypes>
|
||||||
|
SqliteResultRangeWithTransaction(StatementImplementation &statement,
|
||||||
|
const QueryTypes &...queryValues)
|
||||||
|
: BaseSqliteResultRange<ResultType>{statement}
|
||||||
|
, m_transaction{statement.database()}
|
||||||
|
, resetter{&statement}
|
||||||
|
{
|
||||||
|
statement.bindValues(queryValues...);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SqliteResultRangeWithTransaction()
|
||||||
|
{
|
||||||
|
if (!std::uncaught_exceptions()) {
|
||||||
|
resetter.reset();
|
||||||
|
m_transaction.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeferredTransaction m_transaction;
|
||||||
|
Resetter resetter;
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~StatementImplementation() = default;
|
~StatementImplementation() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Resetter
|
struct Resetter
|
||||||
{
|
{
|
||||||
Resetter(StatementImplementation &statement)
|
Resetter(StatementImplementation *statement)
|
||||||
: statement(statement)
|
: statement(statement)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
Resetter(Resetter &) = delete;
|
||||||
|
Resetter &operator=(Resetter &) = delete;
|
||||||
|
|
||||||
|
Resetter(Resetter &&other)
|
||||||
|
: statement{std::exchange(other.statement, nullptr)}
|
||||||
|
{}
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
statement.reset();
|
if (statement)
|
||||||
|
statement->reset();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
shouldReset = false;
|
statement = nullptr;
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldReset = false;
|
statement = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
~Resetter() noexcept
|
~Resetter() noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (shouldReset)
|
if (statement)
|
||||||
statement.reset();
|
statement->reset();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatementImplementation &statement;
|
StatementImplementation *statement;
|
||||||
bool shouldReset = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ValueGetter
|
struct ValueGetter
|
||||||
@@ -356,17 +507,28 @@ private:
|
|||||||
emplaceBackValues(container, std::make_integer_sequence<int, ResultCount>{});
|
emplaceBackValues(container, std::make_integer_sequence<int, ResultCount>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ResultOptionalType,
|
template<typename ResultOptionalType, int... ColumnIndices>
|
||||||
int... ColumnIndices>
|
ResultOptionalType createOptionalValue(std::integer_sequence<int, ColumnIndices...>)
|
||||||
ResultOptionalType assignValue(std::integer_sequence<int, ColumnIndices...>)
|
|
||||||
{
|
{
|
||||||
return ResultOptionalType(Utils::in_place, ValueGetter(*this, ColumnIndices)...);
|
return ResultOptionalType(Utils::in_place, ValueGetter(*this, ColumnIndices)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ResultOptionalType>
|
template<typename ResultOptionalType>
|
||||||
ResultOptionalType assignValue()
|
ResultOptionalType createOptionalValue()
|
||||||
{
|
{
|
||||||
return assignValue<ResultOptionalType>(std::make_integer_sequence<int, ResultCount>{});
|
return createOptionalValue<ResultOptionalType>(std::make_integer_sequence<int, ResultCount>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ResultType, int... ColumnIndices>
|
||||||
|
ResultType createValue(std::integer_sequence<int, ColumnIndices...>)
|
||||||
|
{
|
||||||
|
return ResultType{ValueGetter(*this, ColumnIndices)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ResultType>
|
||||||
|
ResultType createValue()
|
||||||
|
{
|
||||||
|
return createValue<ResultType>(std::make_integer_sequence<int, ResultCount>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Callable, int... ColumnIndices>
|
template<typename Callable, int... ColumnIndices>
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ public:
|
|||||||
Base::checkColumnCount(ResultCount);
|
Base::checkColumnCount(ResultCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using Base::range;
|
||||||
|
using Base::rangeWithTransaction;
|
||||||
using Base::readCallback;
|
using Base::readCallback;
|
||||||
using Base::readTo;
|
using Base::readTo;
|
||||||
using Base::toValue;
|
using Base::toValue;
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <googletest.h>
|
#include "googletest.h"
|
||||||
|
#include "sqlitedatabasemock.h"
|
||||||
|
|
||||||
#include <sqlitebasestatement.h>
|
#include <sqlitebasestatement.h>
|
||||||
|
|
||||||
@@ -33,6 +34,11 @@
|
|||||||
class BaseMockSqliteStatement
|
class BaseMockSqliteStatement
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
BaseMockSqliteStatement() = default;
|
||||||
|
BaseMockSqliteStatement(SqliteDatabaseMock &databaseMock)
|
||||||
|
: m_databaseMock{&databaseMock}
|
||||||
|
{}
|
||||||
|
|
||||||
MOCK_METHOD0(next, bool());
|
MOCK_METHOD0(next, bool());
|
||||||
MOCK_METHOD0(step, void ());
|
MOCK_METHOD0(step, void ());
|
||||||
MOCK_METHOD0(reset, void ());
|
MOCK_METHOD0(reset, void ());
|
||||||
@@ -60,6 +66,11 @@ public:
|
|||||||
MOCK_METHOD1(checkColumnCount, void(int));
|
MOCK_METHOD1(checkColumnCount, void(int));
|
||||||
|
|
||||||
MOCK_CONST_METHOD0(isReadOnlyStatement, bool());
|
MOCK_CONST_METHOD0(isReadOnlyStatement, bool());
|
||||||
|
|
||||||
|
SqliteDatabaseMock &database() { return *m_databaseMock; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SqliteDatabaseMock *m_databaseMock = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -102,8 +113,13 @@ template<int ResultCount = 1>
|
|||||||
class MockSqliteStatement
|
class MockSqliteStatement
|
||||||
: public Sqlite::StatementImplementation<NiceMock<BaseMockSqliteStatement>, ResultCount>
|
: public Sqlite::StatementImplementation<NiceMock<BaseMockSqliteStatement>, ResultCount>
|
||||||
{
|
{
|
||||||
|
using Base = Sqlite::StatementImplementation<NiceMock<BaseMockSqliteStatement>, ResultCount>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MockSqliteStatement() {}
|
explicit MockSqliteStatement() {}
|
||||||
|
explicit MockSqliteStatement(SqliteDatabaseMock &databaseMock)
|
||||||
|
: Base{databaseMock}
|
||||||
|
{}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void checkIsWritableStatement();
|
void checkIsWritableStatement();
|
||||||
|
|||||||
@@ -70,4 +70,3 @@ public:
|
|||||||
|
|
||||||
MOCK_METHOD(void, setAttachedTables, (const Utils::SmallStringVector &tables), (override));
|
MOCK_METHOD(void, setAttachedTables, (const Utils::SmallStringVector &tables), (override));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "googletest.h"
|
#include "googletest.h"
|
||||||
#include "mocksqlitestatement.h"
|
#include "mocksqlitestatement.h"
|
||||||
|
#include "sqlitedatabasemock.h"
|
||||||
#include "sqliteteststatement.h"
|
#include "sqliteteststatement.h"
|
||||||
|
|
||||||
#include <sqliteblob.h>
|
#include <sqliteblob.h>
|
||||||
@@ -662,6 +663,116 @@ TEST_F(SqliteStatement, GetTupleValuesWithoutArguments)
|
|||||||
UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3}));
|
UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeWithoutArguments)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
|
|
||||||
|
auto range = statement.range<Tuple>();
|
||||||
|
std::vector<Tuple> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values,
|
||||||
|
UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeWithTransactionWithoutArguments)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
|
|
||||||
|
auto range = statement.rangeWithTransaction<Tuple>();
|
||||||
|
std::vector<Tuple> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values,
|
||||||
|
UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeInForRangeLoop)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
|
std::vector<Tuple> values;
|
||||||
|
|
||||||
|
for (auto value : statement.range<Tuple>())
|
||||||
|
values.push_back(value);
|
||||||
|
|
||||||
|
ASSERT_THAT(values,
|
||||||
|
UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeWithTransactionInForRangeLoop)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
|
std::vector<Tuple> values;
|
||||||
|
|
||||||
|
for (auto value : statement.rangeWithTransaction<Tuple>())
|
||||||
|
values.push_back(value);
|
||||||
|
|
||||||
|
ASSERT_THAT(values,
|
||||||
|
UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}, Tuple{"poo", 40.0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeInForRangeLoopWithBreak)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test ORDER BY name", database);
|
||||||
|
std::vector<Tuple> values;
|
||||||
|
|
||||||
|
for (auto value : statement.range<Tuple>()) {
|
||||||
|
values.push_back(value);
|
||||||
|
if (value == Tuple{"foo", 23.3, 2})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeWithTransactionInForRangeLoopWithBreak)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test ORDER BY name", database);
|
||||||
|
std::vector<Tuple> values;
|
||||||
|
|
||||||
|
for (auto value : statement.rangeWithTransaction<Tuple>()) {
|
||||||
|
values.push_back(value);
|
||||||
|
if (value == Tuple{"foo", 23.3, 2})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"foo", 23.3, 2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeInForRangeLoopWithContinue)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test ORDER BY name", database);
|
||||||
|
std::vector<Tuple> values;
|
||||||
|
|
||||||
|
for (auto value : statement.range<Tuple>()) {
|
||||||
|
if (value == Tuple{"foo", 23.3, 2})
|
||||||
|
continue;
|
||||||
|
values.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"poo", 40.0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetTupleRangeWithTransactionInForRangeLoopWithContinue)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, double, int>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test ORDER BY name", database);
|
||||||
|
std::vector<Tuple> values;
|
||||||
|
|
||||||
|
for (auto value : statement.rangeWithTransaction<Tuple>()) {
|
||||||
|
if (value == Tuple{"foo", 23.3, 2})
|
||||||
|
continue;
|
||||||
|
values.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre(Tuple{"bar", 0, 1}, Tuple{"poo", 40.0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetSingleValuesWithoutArguments)
|
TEST_F(SqliteStatement, GetSingleValuesWithoutArguments)
|
||||||
{
|
{
|
||||||
ReadStatement<1> statement("SELECT name FROM test", database);
|
ReadStatement<1> statement("SELECT name FROM test", database);
|
||||||
@@ -671,6 +782,26 @@ TEST_F(SqliteStatement, GetSingleValuesWithoutArguments)
|
|||||||
ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo"));
|
ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetSingleRangeWithoutArguments)
|
||||||
|
{
|
||||||
|
ReadStatement<1> statement("SELECT name FROM test", database);
|
||||||
|
|
||||||
|
auto range = statement.range<Utils::SmallStringView>();
|
||||||
|
std::vector<Utils::SmallString> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetSingleRangeWithTransactionWithoutArguments)
|
||||||
|
{
|
||||||
|
ReadStatement<1> statement("SELECT name FROM test", database);
|
||||||
|
|
||||||
|
auto range = statement.rangeWithTransaction<Utils::SmallStringView>();
|
||||||
|
std::vector<Utils::SmallString> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo"));
|
||||||
|
}
|
||||||
|
|
||||||
class FooValue
|
class FooValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -697,6 +828,28 @@ TEST_F(SqliteStatement, GetSingleSqliteValuesWithoutArguments)
|
|||||||
ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull()));
|
ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetSingleSqliteRangeWithoutArguments)
|
||||||
|
{
|
||||||
|
ReadStatement<1> statement("SELECT number FROM test", database);
|
||||||
|
database.execute("INSERT INTO test VALUES (NULL, NULL, NULL)");
|
||||||
|
|
||||||
|
auto range = statement.range<FooValue>();
|
||||||
|
std::vector<FooValue> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetSingleSqliteRangeWithTransactionWithoutArguments)
|
||||||
|
{
|
||||||
|
ReadStatement<1> statement("SELECT number FROM test", database);
|
||||||
|
database.execute("INSERT INTO test VALUES (NULL, NULL, NULL)");
|
||||||
|
|
||||||
|
auto range = statement.rangeWithTransaction<FooValue>();
|
||||||
|
std::vector<FooValue> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull()));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetStructValuesWithoutArguments)
|
TEST_F(SqliteStatement, GetStructValuesWithoutArguments)
|
||||||
{
|
{
|
||||||
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
@@ -709,6 +862,32 @@ TEST_F(SqliteStatement, GetStructValuesWithoutArguments)
|
|||||||
Output{"poo", "40", 3}));
|
Output{"poo", "40", 3}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetStructRangeWithoutArguments)
|
||||||
|
{
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
|
|
||||||
|
auto range = statement.range<Output>();
|
||||||
|
std::vector<Output> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values,
|
||||||
|
UnorderedElementsAre(Output{"bar", "blah", 1},
|
||||||
|
Output{"foo", "23.3", 2},
|
||||||
|
Output{"poo", "40", 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetStructRangeWithTransactionWithoutArguments)
|
||||||
|
{
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test", database);
|
||||||
|
|
||||||
|
auto range = statement.rangeWithTransaction<Output>();
|
||||||
|
std::vector<Output> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values,
|
||||||
|
UnorderedElementsAre(Output{"bar", "blah", 1},
|
||||||
|
Output{"foo", "23.3", 2},
|
||||||
|
Output{"poo", "40", 3}));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetValuesForSingleOutputWithBindingMultipleTimes)
|
TEST_F(SqliteStatement, GetValuesForSingleOutputWithBindingMultipleTimes)
|
||||||
{
|
{
|
||||||
ReadStatement<1> statement("SELECT name FROM test WHERE number=?", database);
|
ReadStatement<1> statement("SELECT name FROM test WHERE number=?", database);
|
||||||
@@ -719,6 +898,28 @@ TEST_F(SqliteStatement, GetValuesForSingleOutputWithBindingMultipleTimes)
|
|||||||
ASSERT_THAT(values, ElementsAre("poo"));
|
ASSERT_THAT(values, ElementsAre("poo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeForSingleOutputWithBindingMultipleTimes)
|
||||||
|
{
|
||||||
|
ReadStatement<1> statement("SELECT name FROM test WHERE number=?", database);
|
||||||
|
statement.values<Utils::SmallString>(3, 40);
|
||||||
|
|
||||||
|
auto range = statement.range<Utils::SmallStringView>(40);
|
||||||
|
std::vector<Utils::SmallString> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre("poo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeWithTransactionForSingleOutputWithBindingMultipleTimes)
|
||||||
|
{
|
||||||
|
ReadStatement<1> statement("SELECT name FROM test WHERE number=?", database);
|
||||||
|
statement.values<Utils::SmallString>(3, 40);
|
||||||
|
|
||||||
|
auto range = statement.rangeWithTransaction<Utils::SmallStringView>(40);
|
||||||
|
std::vector<Utils::SmallString> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre("poo"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetValuesForMultipleOutputValuesAndMultipleQueryValue)
|
TEST_F(SqliteStatement, GetValuesForMultipleOutputValuesAndMultipleQueryValue)
|
||||||
{
|
{
|
||||||
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
||||||
@@ -730,6 +931,30 @@ TEST_F(SqliteStatement, GetValuesForMultipleOutputValuesAndMultipleQueryValue)
|
|||||||
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeForMultipleOutputValuesAndMultipleQueryValue)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
||||||
|
ReadStatement<3> statement(
|
||||||
|
"SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database);
|
||||||
|
|
||||||
|
auto range = statement.range<Tuple>("bar", "blah", 1);
|
||||||
|
std::vector<Tuple> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeWithTransactionForMultipleOutputValuesAndMultipleQueryValue)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
||||||
|
ReadStatement<3> statement(
|
||||||
|
"SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database);
|
||||||
|
|
||||||
|
auto range = statement.rangeWithTransaction<Tuple>("bar", "blah", 1);
|
||||||
|
std::vector<Tuple> values{range.begin(), range.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, CallGetValuesForMultipleOutputValuesAndMultipleQueryValueMultipleTimes)
|
TEST_F(SqliteStatement, CallGetValuesForMultipleOutputValuesAndMultipleQueryValueMultipleTimes)
|
||||||
{
|
{
|
||||||
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
||||||
@@ -742,6 +967,39 @@ TEST_F(SqliteStatement, CallGetValuesForMultipleOutputValuesAndMultipleQueryValu
|
|||||||
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, CallGetRangeForMultipleOutputValuesAndMultipleQueryValueMultipleTimes)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test WHERE name=? AND number=?",
|
||||||
|
database);
|
||||||
|
{
|
||||||
|
auto range = statement.range<Tuple>("bar", "blah");
|
||||||
|
std::vector<Tuple> values1{range.begin(), range.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto range2 = statement.range<Tuple>("bar", "blah");
|
||||||
|
std::vector<Tuple> values{range2.begin(), range2.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement,
|
||||||
|
CallGetRangeWithTransactionForMultipleOutputValuesAndMultipleQueryValueMultipleTimes)
|
||||||
|
{
|
||||||
|
using Tuple = std::tuple<Utils::SmallString, Utils::SmallString, long long>;
|
||||||
|
ReadStatement<3> statement("SELECT name, number, value FROM test WHERE name=? AND number=?",
|
||||||
|
database);
|
||||||
|
{
|
||||||
|
auto range = statement.rangeWithTransaction<Tuple>("bar", "blah");
|
||||||
|
std::vector<Tuple> values1{range.begin(), range.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto range2 = statement.rangeWithTransaction<Tuple>("bar", "blah");
|
||||||
|
std::vector<Tuple> values{range2.begin(), range2.end()};
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetStructOutputValuesAndMultipleQueryValue)
|
TEST_F(SqliteStatement, GetStructOutputValuesAndMultipleQueryValue)
|
||||||
{
|
{
|
||||||
ReadStatement<3> statement(
|
ReadStatement<3> statement(
|
||||||
@@ -851,6 +1109,30 @@ TEST_F(SqliteStatement, GetValuesWithoutArgumentsCallsReset)
|
|||||||
mockStatement.values<int>(3);
|
mockStatement.values<int>(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeWithoutArgumentsCallsReset)
|
||||||
|
{
|
||||||
|
MockSqliteStatement mockStatement;
|
||||||
|
|
||||||
|
EXPECT_CALL(mockStatement, reset());
|
||||||
|
|
||||||
|
mockStatement.range<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeWithTransactionWithoutArgumentsCalls)
|
||||||
|
{
|
||||||
|
InSequence s;
|
||||||
|
SqliteDatabaseMock databaseMock;
|
||||||
|
MockSqliteStatement mockStatement{databaseMock};
|
||||||
|
|
||||||
|
EXPECT_CALL(databaseMock, lock());
|
||||||
|
EXPECT_CALL(databaseMock, deferredBegin());
|
||||||
|
EXPECT_CALL(mockStatement, reset());
|
||||||
|
EXPECT_CALL(databaseMock, commit());
|
||||||
|
EXPECT_CALL(databaseMock, unlock());
|
||||||
|
|
||||||
|
mockStatement.rangeWithTransaction<int>();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetValuesWithoutArgumentsCallsResetIfExceptionIsThrown)
|
TEST_F(SqliteStatement, GetValuesWithoutArgumentsCallsResetIfExceptionIsThrown)
|
||||||
{
|
{
|
||||||
MockSqliteStatement mockStatement;
|
MockSqliteStatement mockStatement;
|
||||||
@@ -861,6 +1143,38 @@ TEST_F(SqliteStatement, GetValuesWithoutArgumentsCallsResetIfExceptionIsThrown)
|
|||||||
EXPECT_THROW(mockStatement.values<int>(3), Sqlite::StatementHasError);
|
EXPECT_THROW(mockStatement.values<int>(3), Sqlite::StatementHasError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeWithoutArgumentsCallsResetIfExceptionIsThrown)
|
||||||
|
{
|
||||||
|
MockSqliteStatement mockStatement;
|
||||||
|
ON_CALL(mockStatement, next()).WillByDefault(Throw(Sqlite::StatementHasError("")));
|
||||||
|
auto range = mockStatement.range<int>();
|
||||||
|
|
||||||
|
EXPECT_CALL(mockStatement, reset());
|
||||||
|
|
||||||
|
EXPECT_THROW(range.begin(), Sqlite::StatementHasError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetRangeWithTransactionWithoutArgumentsCallsResetIfExceptionIsThrown)
|
||||||
|
{
|
||||||
|
InSequence s;
|
||||||
|
SqliteDatabaseMock databaseMock;
|
||||||
|
MockSqliteStatement mockStatement{databaseMock};
|
||||||
|
ON_CALL(mockStatement, next()).WillByDefault(Throw(Sqlite::StatementHasError("")));
|
||||||
|
|
||||||
|
EXPECT_CALL(databaseMock, lock());
|
||||||
|
EXPECT_CALL(databaseMock, deferredBegin());
|
||||||
|
EXPECT_CALL(mockStatement, reset());
|
||||||
|
EXPECT_CALL(databaseMock, rollback());
|
||||||
|
EXPECT_CALL(databaseMock, unlock());
|
||||||
|
|
||||||
|
EXPECT_THROW(
|
||||||
|
{
|
||||||
|
auto range = mockStatement.rangeWithTransaction<int>();
|
||||||
|
range.begin();
|
||||||
|
},
|
||||||
|
Sqlite::StatementHasError);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetValuesWithSimpleArgumentsCallsReset)
|
TEST_F(SqliteStatement, GetValuesWithSimpleArgumentsCallsReset)
|
||||||
{
|
{
|
||||||
MockSqliteStatement mockStatement;
|
MockSqliteStatement mockStatement;
|
||||||
|
|||||||
Reference in New Issue
Block a user