Sqlite: Add change set iterator

Task-number: QDS-2998
Change-Id: I7bfa8af51d9d7e6122902ee132ad51019e20afb5
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2020-10-27 15:09:25 +01:00
parent 3c7ab41e9e
commit c3873fcf40
8 changed files with 816 additions and 78 deletions

View File

@@ -37,12 +37,15 @@ namespace Sqlite {
class SQLITE_EXPORT Exception : public std::exception class SQLITE_EXPORT Exception : public std::exception
{ {
public: public:
Exception(const char *whatErrorHasHappen, Exception(const char *whatErrorHasHappen, Utils::SmallString &&sqliteErrorMessage)
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
: m_whatErrorHasHappen(whatErrorHasHappen) : m_whatErrorHasHappen(whatErrorHasHappen)
, m_sqliteErrorMessage(std::move(sqliteErrorMessage)) , m_sqliteErrorMessage(std::move(sqliteErrorMessage))
{} {}
Exception(const char *whatErrorHasHappen)
: m_whatErrorHasHappen(whatErrorHasHappen)
{}
const char *what() const noexcept override { return m_whatErrorHasHappen; } const char *what() const noexcept override { return m_whatErrorHasHappen; }
void printWarning() const; void printWarning() const;
@@ -58,8 +61,7 @@ public:
StatementIsBusy(const char *whatErrorHasHappen, StatementIsBusy(const char *whatErrorHasHappen,
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString()) Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage)) : Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
{ {}
}
}; };
class DatabaseIsBusy : public Exception class DatabaseIsBusy : public Exception
@@ -67,8 +69,7 @@ class DatabaseIsBusy : public Exception
public: public:
DatabaseIsBusy(const char *whatErrorHasHappen) DatabaseIsBusy(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class StatementHasError : public Exception class StatementHasError : public Exception
@@ -77,8 +78,7 @@ public:
StatementHasError(const char *whatErrorHasHappen, StatementHasError(const char *whatErrorHasHappen,
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString()) Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage)) : Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
{ {}
}
}; };
class StatementIsMisused : public Exception class StatementIsMisused : public Exception
@@ -87,8 +87,7 @@ public:
StatementIsMisused(const char *whatErrorHasHappen, StatementIsMisused(const char *whatErrorHasHappen,
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString()) Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage)) : Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
{ {}
}
}; };
class InputOutputError : public Exception class InputOutputError : public Exception
@@ -96,8 +95,7 @@ class InputOutputError : public Exception
public: public:
InputOutputError(const char *whatErrorHasHappen) InputOutputError(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class ConstraintPreventsModification : public Exception class ConstraintPreventsModification : public Exception
@@ -106,8 +104,7 @@ public:
ConstraintPreventsModification(const char *whatErrorHasHappen, ConstraintPreventsModification(const char *whatErrorHasHappen,
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString()) Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage)) : Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
{ {}
}
}; };
class NoValuesToFetch : public Exception class NoValuesToFetch : public Exception
@@ -115,8 +112,7 @@ class NoValuesToFetch : public Exception
public: public:
NoValuesToFetch(const char *whatErrorHasHappen) NoValuesToFetch(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class ColumnCountDoesNotMatch : public Exception class ColumnCountDoesNotMatch : public Exception
@@ -133,8 +129,7 @@ public:
BindingIndexIsOutOfRange(const char *whatErrorHasHappen, BindingIndexIsOutOfRange(const char *whatErrorHasHappen,
Utils::SmallString &&sqliteErrorMessage = Utils::SmallString()) Utils::SmallString &&sqliteErrorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(sqliteErrorMessage)) : Exception(whatErrorHasHappen, std::move(sqliteErrorMessage))
{ {}
}
}; };
class WrongBindingName : public Exception class WrongBindingName : public Exception
@@ -142,8 +137,7 @@ class WrongBindingName : public Exception
public: public:
WrongBindingName(const char *whatErrorHasHappen) WrongBindingName(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class DatabaseIsNotOpen : public Exception class DatabaseIsNotOpen : public Exception
@@ -151,8 +145,7 @@ class DatabaseIsNotOpen : public Exception
public: public:
DatabaseIsNotOpen(const char *whatErrorHasHappen) DatabaseIsNotOpen(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class DatabaseCannotBeOpened : public Exception class DatabaseCannotBeOpened : public Exception
@@ -161,8 +154,7 @@ public:
DatabaseCannotBeOpened(const char *whatErrorHasHappen, DatabaseCannotBeOpened(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString()) Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage)) : Exception(whatErrorHasHappen, std::move(errorMessage))
{ {}
}
}; };
class DatabaseFilePathIsEmpty : public DatabaseCannotBeOpened class DatabaseFilePathIsEmpty : public DatabaseCannotBeOpened
@@ -170,8 +162,7 @@ class DatabaseFilePathIsEmpty : public DatabaseCannotBeOpened
public: public:
DatabaseFilePathIsEmpty(const char *whatErrorHasHappen) DatabaseFilePathIsEmpty(const char *whatErrorHasHappen)
: DatabaseCannotBeOpened(whatErrorHasHappen) : DatabaseCannotBeOpened(whatErrorHasHappen)
{ {}
}
}; };
class DatabaseIsAlreadyOpen : public DatabaseCannotBeOpened class DatabaseIsAlreadyOpen : public DatabaseCannotBeOpened
@@ -179,8 +170,7 @@ class DatabaseIsAlreadyOpen : public DatabaseCannotBeOpened
public: public:
DatabaseIsAlreadyOpen(const char *whatErrorHasHappen) DatabaseIsAlreadyOpen(const char *whatErrorHasHappen)
: DatabaseCannotBeOpened(whatErrorHasHappen) : DatabaseCannotBeOpened(whatErrorHasHappen)
{ {}
}
}; };
class DatabaseCannotBeClosed : public Exception class DatabaseCannotBeClosed : public Exception
@@ -188,8 +178,7 @@ class DatabaseCannotBeClosed : public Exception
public: public:
DatabaseCannotBeClosed(const char *whatErrorHasHappen) DatabaseCannotBeClosed(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class DatabaseIsAlreadyClosed : public DatabaseCannotBeClosed class DatabaseIsAlreadyClosed : public DatabaseCannotBeClosed
@@ -197,8 +186,7 @@ class DatabaseIsAlreadyClosed : public DatabaseCannotBeClosed
public: public:
DatabaseIsAlreadyClosed(const char *whatErrorHasHappen) DatabaseIsAlreadyClosed(const char *whatErrorHasHappen)
: DatabaseCannotBeClosed(whatErrorHasHappen) : DatabaseCannotBeClosed(whatErrorHasHappen)
{ {}
}
}; };
class WrongFilePath : public DatabaseCannotBeOpened class WrongFilePath : public DatabaseCannotBeOpened
@@ -207,8 +195,7 @@ public:
WrongFilePath(const char *whatErrorHasHappen, WrongFilePath(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString()) Utils::SmallString &&errorMessage = Utils::SmallString())
: DatabaseCannotBeOpened(whatErrorHasHappen, std::move(errorMessage)) : DatabaseCannotBeOpened(whatErrorHasHappen, std::move(errorMessage))
{ {}
}
}; };
class PragmaValueNotSet : public Exception class PragmaValueNotSet : public Exception
@@ -216,8 +203,7 @@ class PragmaValueNotSet : public Exception
public: public:
PragmaValueNotSet(const char *whatErrorHasHappen) PragmaValueNotSet(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class NotReadOnlySqlStatement : public Exception class NotReadOnlySqlStatement : public Exception
@@ -225,8 +211,7 @@ class NotReadOnlySqlStatement : public Exception
public: public:
NotReadOnlySqlStatement(const char *whatErrorHasHappen) NotReadOnlySqlStatement(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class NotWriteSqlStatement : public Exception class NotWriteSqlStatement : public Exception
@@ -234,8 +219,7 @@ class NotWriteSqlStatement : public Exception
public: public:
NotWriteSqlStatement(const char *whatErrorHasHappen) NotWriteSqlStatement(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class DeadLock : public Exception class DeadLock : public Exception
@@ -243,8 +227,7 @@ class DeadLock : public Exception
public: public:
DeadLock(const char *whatErrorHasHappen) DeadLock(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {}
}
}; };
class UnknowError : public Exception class UnknowError : public Exception
@@ -253,8 +236,7 @@ public:
UnknowError(const char *whatErrorHasHappen, UnknowError(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString()) Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage)) : Exception(whatErrorHasHappen, std::move(errorMessage))
{ {}
}
}; };
class BindingTooBig : public Exception class BindingTooBig : public Exception
@@ -263,8 +245,7 @@ public:
BindingTooBig(const char *whatErrorHasHappen, BindingTooBig(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString()) Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage)) : Exception(whatErrorHasHappen, std::move(errorMessage))
{ {}
}
}; };
class TooBig : public Exception class TooBig : public Exception
@@ -387,4 +368,46 @@ public:
: Exception(whatErrorHasHappen, std::move(errorMessage)) : Exception(whatErrorHasHappen, std::move(errorMessage))
{} {}
}; };
class CannotCreateChangeSetIterator : public Exception
{
public:
CannotCreateChangeSetIterator(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen)
{}
};
class CannotGetChangeSetOperation : public Exception
{
public:
CannotGetChangeSetOperation(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen)
{}
};
class ChangeSetTupleIsOutOfRange : public Exception
{
public:
ChangeSetTupleIsOutOfRange(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen)
{}
};
class ChangeSetTupleIsMisused : public Exception
{
public:
ChangeSetTupleIsMisused(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen)
{}
};
class UnknownError : public Exception
{
public:
UnknownError(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen)
{}
};
} // namespace Sqlite } // namespace Sqlite

View File

@@ -24,6 +24,7 @@
****************************************************************************/ ****************************************************************************/
#include "sqlitesessionchangeset.h" #include "sqlitesessionchangeset.h"
#include "sqliteexception.h"
#include "sqlitesessions.h" #include "sqlitesessions.h"
#include <utils/smallstringio.h> #include <utils/smallstringio.h>
@@ -33,7 +34,7 @@
namespace Sqlite { namespace Sqlite {
namespace { namespace {
void checkResultCode(int resultCode) void checkSessionChangeSetCreation(int resultCode)
{ {
switch (resultCode) { switch (resultCode) {
case SQLITE_NOMEM: case SQLITE_NOMEM:
@@ -44,29 +45,183 @@ void checkResultCode(int resultCode)
throw UnknowError("Unknow exception"); throw UnknowError("Unknow exception");
} }
void checkIteratorCreation(int resultCode)
{
if (resultCode != SQLITE_OK)
throw Sqlite::CannotCreateChangeSetIterator{
"SessionChangeSet: Cannot create iterator from blob."};
}
void checkIteratorOperation(int resultCode)
{
if (resultCode != SQLITE_OK)
throw Sqlite::CannotGetChangeSetOperation{
"SessionChangeSet: Cannot create iterator from blob."};
}
void checkChangeSetValue(int resultCode)
{
switch (resultCode) {
case SQLITE_OK:
return;
case SQLITE_RANGE:
throw Sqlite::ChangeSetTupleIsOutOfRange{
"SessionChangeSet: You tried to access a non existing column."};
case SQLITE_MISUSE:
throw Sqlite::ChangeSetIsMisused{
"SessionChangeSet: Some misuse happened as you tried to access."};
}
throw Sqlite::UnknownError{"SessionChangeSet: Some unknown error happened."};
}
ValueView convertSqliteValue(sqlite3_value *value)
{
if (value) {
int type = sqlite3_value_type(value);
switch (type) {
case SQLITE_INTEGER:
return ValueView::create(sqlite3_value_int64(value));
case SQLITE_FLOAT:
return ValueView::create(sqlite3_value_double(value));
case SQLITE_TEXT:
return ValueView::create(
Utils::SmallStringView{reinterpret_cast<const char *const>(sqlite3_value_text(value)),
static_cast<std::size_t>(sqlite3_value_bytes(value))});
case SQLITE_NULL:
return ValueView::create(NullValue{});
}
}
return ValueView::create(NullValue{});
}
} // namespace } // namespace
SessionChangeSet::SessionChangeSet(BlobView blob) SessionChangeSet::SessionChangeSet(BlobView blob)
: data(sqlite3_malloc64(blob.size())) : m_data(sqlite3_malloc64(blob.size()))
, size(int(blob.size())) , m_size(int(blob.size()))
{ {
std::memcpy(data, blob.data(), blob.size()); std::memcpy(m_data, blob.data(), blob.size());
} }
SessionChangeSet::SessionChangeSet(Sessions &session) SessionChangeSet::SessionChangeSet(Sessions &session)
{ {
int resultCode = sqlite3session_changeset(session.session.get(), &size, &data); int resultCode = sqlite3session_changeset(session.session.get(), &m_size, &m_data);
checkResultCode(resultCode); checkSessionChangeSetCreation(resultCode);
} }
SessionChangeSet::~SessionChangeSet() SessionChangeSet::~SessionChangeSet()
{ {
sqlite3_free(data); sqlite3_free(m_data);
} }
BlobView SessionChangeSet::asBlobView() const BlobView SessionChangeSet::asBlobView() const
{ {
return {static_cast<const byte *>(data), static_cast<std::size_t>(size)}; return {static_cast<const byte *>(m_data), static_cast<std::size_t>(m_size)};
} }
SessionChangeSetInternal::ConstIterator SessionChangeSet::begin() const
{
sqlite3_changeset_iter *sessionIterator;
int resultCode = sqlite3changeset_start(&sessionIterator, m_size, m_data);
checkIteratorCreation(resultCode);
SessionChangeSetInternal::ConstIterator iterator{sessionIterator};
++iterator;
return iterator;
}
namespace SessionChangeSetInternal {
ConstIterator::~ConstIterator()
{
sqlite3changeset_finalize(m_sessionIterator);
}
namespace {
State convertState(int state)
{
switch (state) {
case SQLITE_ROW:
return State::Row;
case SQLITE_DONE:
return State::Done;
}
return State::Invalid;
}
Operation convertOperation(int operation)
{
switch (operation) {
case SQLITE_INSERT:
return Operation::Insert;
case SQLITE_UPDATE:
return Operation::Update;
case SQLITE_DELETE:
return Operation::Delete;
}
return Operation::Invalid;
}
} // namespace
ConstIterator &ConstIterator::operator++()
{
int state = sqlite3changeset_next(m_sessionIterator);
m_state = convertState(state);
return *this;
}
Tuple ConstIterator::operator*() const
{
const char *table;
int columnCount;
int operation;
int isIndirect;
int resultCode = sqlite3changeset_op(m_sessionIterator, &table, &columnCount, &operation, &isIndirect);
checkIteratorOperation(resultCode);
return {table, m_sessionIterator, columnCount, convertOperation(operation)};
}
namespace {
ValueViews fetchValues(sqlite3_changeset_iter *sessionIterator, int column, Operation operation)
{
sqlite3_value *newValue = nullptr;
if (operation == Operation::Insert || operation == Operation::Update) {
int resultCode = sqlite3changeset_new(sessionIterator, column, &newValue);
checkChangeSetValue(resultCode);
}
sqlite3_value *oldValue = nullptr;
if (operation == Operation::Delete || operation == Operation::Update) {
int resultCode = sqlite3changeset_old(sessionIterator, column, &oldValue);
checkChangeSetValue(resultCode);
}
return {convertSqliteValue(newValue), convertSqliteValue(oldValue)};
}
} // namespace
ValueViews ConstTupleIterator::operator*() const
{
return fetchValues(m_sessionIterator, m_column, m_operation);
}
ValueViews Tuple::operator[](int column) const
{
return fetchValues(sessionIterator, column, operation);
}
} // namespace SessionChangeSetInternal
} // namespace Sqlite } // namespace Sqlite

View File

@@ -27,17 +27,173 @@
#include "sqliteblob.h" #include "sqliteblob.h"
#include "sqliteglobal.h" #include "sqliteglobal.h"
#include "sqlitevalue.h"
#include <utils/smallstring.h>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <iosfwd> #include <iosfwd>
struct sqlite3_changeset_iter;
namespace Sqlite { namespace Sqlite {
class Sessions; class Sessions;
class SessionChangeSet namespace SessionChangeSetInternal {
enum class Operation : char { Invalid, Insert, Update, Delete };
class SentinelIterator
{};
class ValueViews
{
public:
ValueView newValue;
ValueView oldValue;
};
class ConstTupleIterator
{
public:
using difference_type = int;
using value_type = ValueView;
using pointer = const ValueView *;
using reference = const ValueView &;
using iterator_category = std::forward_iterator_tag;
ConstTupleIterator(sqlite3_changeset_iter *sessionIterator, int index, Operation operation)
: m_sessionIterator{sessionIterator}
, m_column{index}
, m_operation{operation}
{}
ConstTupleIterator operator++()
{
++m_column;
return *this;
}
friend bool operator==(const ConstTupleIterator &first, const ConstTupleIterator &second)
{
return first.m_column == second.m_column;
}
friend bool operator!=(const ConstTupleIterator &first, const ConstTupleIterator &second)
{
return !(first == second);
}
ValueViews operator*() const;
private:
sqlite3_changeset_iter *m_sessionIterator = {};
int m_column = 0;
Operation m_operation = Operation::Invalid;
};
class Tuple
{
public:
using difference_type = int;
using value_type = ValueView;
using reference = ValueView &;
using const_reference = const ValueView &;
using iterator = ConstTupleIterator;
using const_iterator = ConstTupleIterator;
using size_type = int;
Utils::SmallStringView table;
sqlite3_changeset_iter *sessionIterator = {};
int columnCount = 0;
Operation operation = Operation::Invalid;
ValueViews operator[](int column) const;
ConstTupleIterator begin() const { return {sessionIterator, 0, operation}; }
ConstTupleIterator end() const { return {sessionIterator, columnCount, operation}; }
};
enum class State : char { Invalid, Row, Done };
class ConstIterator
{
public:
using difference_type = long;
using value_type = Tuple;
using pointer = const Tuple *;
using reference = const Tuple &;
using iterator_category = std::input_iterator_tag;
ConstIterator(sqlite3_changeset_iter *sessionIterator)
: m_sessionIterator(sessionIterator)
{}
ConstIterator(const ConstIterator &) = delete;
void operator=(const ConstIterator &) = delete;
ConstIterator(ConstIterator &&other)
: m_sessionIterator(other.m_sessionIterator)
, m_state(other.m_state)
{
other.m_sessionIterator = {};
other.m_state = State::Done;
}
ConstIterator &operator=(ConstIterator &&other)
{
auto tmp = std::move(other);
std::swap(tmp, *this);
return *this;
}
~ConstIterator();
ConstIterator &operator++();
friend bool operator==(const ConstIterator &first, const ConstIterator &second)
{
return first.m_sessionIterator == second.m_sessionIterator;
}
friend bool operator!=(const ConstIterator &first, const ConstIterator &second)
{
return !(first == second);
}
friend bool operator==(const ConstIterator &first, SentinelIterator)
{
return first.m_state == State::Done;
}
friend bool operator!=(const ConstIterator &first, SentinelIterator)
{
return first.m_state == State::Row;
}
friend bool operator==(SentinelIterator first, const ConstIterator &second)
{
return second == first;
}
friend bool operator!=(SentinelIterator first, const ConstIterator &second)
{
return second != first;
}
Tuple operator*() const;
State state() const { return m_state; }
private:
sqlite3_changeset_iter *m_sessionIterator = {};
State m_state = State::Invalid;
};
} // namespace SessionChangeSetInternal
class SQLITE_EXPORT SessionChangeSet
{ {
public: public:
SessionChangeSet(BlobView blob); SessionChangeSet(BlobView blob);
@@ -58,20 +214,27 @@ public:
friend void swap(SessionChangeSet &first, SessionChangeSet &second) noexcept friend void swap(SessionChangeSet &first, SessionChangeSet &second) noexcept
{ {
SessionChangeSet temp; SessionChangeSet temp;
std::swap(temp.data, first.data); std::swap(temp.m_data, first.m_data);
std::swap(temp.size, first.size); std::swap(temp.m_size, first.m_size);
std::swap(first.data, second.data); std::swap(first.m_data, second.m_data);
std::swap(first.size, second.size); std::swap(first.m_size, second.m_size);
std::swap(temp.data, second.data); std::swap(temp.m_data, second.m_data);
std::swap(temp.size, second.size); std::swap(temp.m_size, second.m_size);
} }
SessionChangeSetInternal::ConstIterator begin() const;
SessionChangeSetInternal::SentinelIterator end() const { return {}; }
void *data() const { return m_data; }
int size() const { return m_size; }
private: private:
SessionChangeSet() = default; SessionChangeSet() = default;
public: private:
void *data = nullptr; void *m_data = nullptr;
int size = {}; int m_size = {};
}; };
using SessionChangeSets = std::vector<SessionChangeSet>; using SessionChangeSets = std::vector<SessionChangeSet>;

View File

@@ -136,8 +136,8 @@ void Sessions::revert()
for (auto &changeSet : changeSets) { for (auto &changeSet : changeSets) {
int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(), int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(),
changeSet.size, changeSet.size(),
changeSet.data, changeSet.data(),
nullptr, nullptr,
xConflict, xConflict,
nullptr, nullptr,
@@ -160,8 +160,8 @@ void Sessions::apply()
for (auto &changeSet : changeSets) { for (auto &changeSet : changeSets) {
int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(), int resultCode = sqlite3changeset_apply_v2(database.backend().sqliteDatabaseHandle(),
changeSet.size, changeSet.size(),
changeSet.data, changeSet.data(),
nullptr, nullptr,
xConflict, xConflict,
nullptr, nullptr,
@@ -185,4 +185,14 @@ void Sessions::deleteAll()
WriteStatement{Utils::SmallString{"DELETE FROM ", sessionsTableName}, database}.execute(); WriteStatement{Utils::SmallString{"DELETE FROM ", sessionsTableName}, database}.execute();
} }
SessionChangeSets Sessions::changeSets() const
{
ReadStatement selectChangeSets{Utils::PathString{"SELECT changeset FROM ",
sessionsTableName,
" ORDER BY id DESC"},
database};
return selectChangeSets.values<SessionChangeSet>(1024);
}
} // namespace Sqlite } // namespace Sqlite

View File

@@ -26,6 +26,7 @@
#pragma once #pragma once
#include "sqlitedatabase.h" #include "sqlitedatabase.h"
#include "sqlitesessionchangeset.h"
#include "sqlitewritestatement.h" #include "sqlitewritestatement.h"
extern "C" { extern "C" {
@@ -81,6 +82,8 @@ public:
void applyAndUpdateSessions(); void applyAndUpdateSessions();
void deleteAll(); void deleteAll();
SessionChangeSets changeSets() const;
private: private:
void attachTables(const Utils::SmallStringVector &tables); void attachTables(const Utils::SmallStringVector &tables);

View File

@@ -331,6 +331,28 @@ std::ostream &operator<<(std::ostream &out, const Value &value)
return out << ")"; return out << ")";
} }
std::ostream &operator<<(std::ostream &out, const ValueView &value)
{
out << "(";
switch (value.type()) {
case Sqlite::ValueType::Integer:
out << value.toInteger();
break;
case Sqlite::ValueType::Float:
out << value.toFloat();
break;
case Sqlite::ValueType::String:
out << "\"" << value.toStringView() << "\"";
break;
case Sqlite::ValueType::Null:
out << "null";
break;
}
return out << ")";
}
namespace { namespace {
Utils::SmallStringView operationText(int operation) Utils::SmallStringView operationText(int operation)
{ {
@@ -398,7 +420,7 @@ std::ostream &operator<<(std::ostream &out, sqlite3_changeset_iter *iter)
std::ostream &operator<<(std::ostream &out, const SessionChangeSet &changeset) std::ostream &operator<<(std::ostream &out, const SessionChangeSet &changeset)
{ {
sqlite3_changeset_iter *iter = nullptr; sqlite3_changeset_iter *iter = nullptr;
sqlite3changeset_start(&iter, changeset.size, const_cast<void *>(changeset.data)); sqlite3changeset_start(&iter, changeset.size(), const_cast<void *>(changeset.data()));
out << "ChangeSets(["; out << "ChangeSets([";
@@ -414,6 +436,79 @@ std::ostream &operator<<(std::ostream &out, const SessionChangeSet &changeset)
return out; return out;
} }
namespace SessionChangeSetInternal {
namespace {
const char *toText(Operation operation)
{
switch (operation) {
case Operation::Invalid:
return "Invalid";
case Operation::Insert:
return "Invalid";
case Operation::Update:
return "Invalid";
case Operation::Delete:
return "Invalid";
}
return "";
}
const char *toText(State state)
{
switch (state) {
case State::Invalid:
return "Invalid";
case State::Row:
return "Row";
case State::Done:
return "Done";
}
return "";
}
} // namespace
std::ostream &operator<<(std::ostream &out, SentinelIterator)
{
return out << "sentinel";
}
std::ostream &operator<<(std::ostream &out, Operation operation)
{
return out << toText(operation);
}
std::ostream &operator<<(std::ostream &out, State state)
{
return out << toText(state);
}
std::ostream &operator<<(std::ostream &out, const Tuple &tuple)
{
return out << "(" << tuple.operation << ", " << tuple.columnCount << ")";
}
std::ostream &operator<<(std::ostream &out, const ValueViews &valueViews)
{
return out << "(" << valueViews.newValue << ", " << valueViews.oldValue << ")";
}
std::ostream &operator<<(std::ostream &out, const ConstIterator &iterator)
{
return out << "(" << (*iterator) << ", " << iterator.state() << ")";
}
std::ostream &operator<<(std::ostream &out, const ConstTupleIterator &iterator)
{
auto value = *iterator;
return out << "(" << value.newValue << ", " << value.newValue << ")";
}
} // namespace SessionChangeSetInternal
} // namespace Sqlite } // namespace Sqlite
namespace ClangBackEnd { namespace ClangBackEnd {

View File

@@ -66,10 +66,30 @@ void PrintTo(const TextRange &range, ::std::ostream *os);
namespace Sqlite { namespace Sqlite {
class Value; class Value;
class ValueView;
class SessionChangeSet; class SessionChangeSet;
std::ostream &operator<<(std::ostream &out, const Value &value); std::ostream &operator<<(std::ostream &out, const Value &value);
std::ostream &operator<<(std::ostream &out, const ValueView &value);
std::ostream &operator<<(std::ostream &out, const SessionChangeSet &changeset); std::ostream &operator<<(std::ostream &out, const SessionChangeSet &changeset);
namespace SessionChangeSetInternal {
class ConstIterator;
class ConstTupleIterator;
class SentinelIterator;
class Tuple;
class ValueViews;
enum class Operation : char;
enum class State : char;
std::ostream &operator<<(std::ostream &out, SentinelIterator iterator);
std::ostream &operator<<(std::ostream &out, const ConstIterator &iterator);
std::ostream &operator<<(std::ostream &out, const ConstTupleIterator &iterator);
std::ostream &operator<<(std::ostream &out, const Tuple &tuple);
std::ostream &operator<<(std::ostream &out, Operation operation);
std::ostream &operator<<(std::ostream &out, State operation);
std::ostream &operator<<(std::ostream &out, const ValueViews &valueViews);
} // namespace SessionChangeSetInternal
} // namespace Sqlite } // namespace Sqlite
namespace ProjectExplorer { namespace ProjectExplorer {

View File

@@ -38,6 +38,8 @@ namespace {
using Sqlite::SessionChangeSet; using Sqlite::SessionChangeSet;
using Sqlite::SessionChangeSets; using Sqlite::SessionChangeSets;
using Sqlite::SessionChangeSetInternal::Operation;
using Sqlite::SessionChangeSetInternal::ValueViews;
class DatabaseExecute class DatabaseExecute
{ {
@@ -79,6 +81,17 @@ MATCHER_P3(HasData,
return data.name == name && data.number == number && data.value == value; return data.name == name && data.number == number && data.value == value;
} }
MATCHER_P2(HasValues,
newValue,
oldValue,
std::string(negation ? "hasn't " : "has ") + PrintToString(newValue) + ", "
+ PrintToString(oldValue))
{
const ValueViews &values = arg;
return values.newValue == newValue && values.oldValue == oldValue;
}
class Tag class Tag
{ {
public: public:
@@ -113,7 +126,6 @@ protected:
std::vector<Data> fetchData() { return selectData.values<Data, 3>(8); } std::vector<Data> fetchData() { return selectData.values<Data, 3>(8); }
std::vector<Tag> fetchTags() { return selectTags.values<Tag, 2>(8); } std::vector<Tag> fetchTags() { return selectTags.values<Tag, 2>(8); }
SessionChangeSets fetchChangeSets() { return selectChangeSets.values<SessionChangeSet>(8); }
protected: protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
@@ -128,6 +140,9 @@ protected:
Sqlite::WriteStatement insertData{"INSERT INTO data(name, number, value) VALUES (?1, ?2, ?3) " Sqlite::WriteStatement insertData{"INSERT INTO data(name, number, value) VALUES (?1, ?2, ?3) "
"ON CONFLICT (name) DO UPDATE SET (number, value) = (?2, ?3)", "ON CONFLICT (name) DO UPDATE SET (number, value) = (?2, ?3)",
database}; database};
Sqlite::WriteStatement insertOneDatum{"INSERT INTO data(value, name) VALUES (?1, ?2) "
"ON CONFLICT (name) DO UPDATE SET (value) = (?2)",
database};
Sqlite::WriteStatement updateNumber{"UPDATE data SET number = ?002 WHERE name=?001", database}; Sqlite::WriteStatement updateNumber{"UPDATE data SET number = ?002 WHERE name=?001", database};
Sqlite::WriteStatement updateValue{"UPDATE data SET value = ?002 WHERE name=?001", database}; Sqlite::WriteStatement updateValue{"UPDATE data SET value = ?002 WHERE name=?001", database};
Sqlite::WriteStatement deleteData{"DELETE FROM data WHERE name=?", database}; Sqlite::WriteStatement deleteData{"DELETE FROM data WHERE name=?", database};
@@ -151,7 +166,7 @@ TEST_F(Sessions, CreateEmptySession)
sessions.create(); sessions.create();
sessions.commit(); sessions.commit();
ASSERT_THAT(fetchChangeSets(), IsEmpty()); ASSERT_THAT(sessions.changeSets(), IsEmpty());
} }
TEST_F(Sessions, CreateSessionWithInsert) TEST_F(Sessions, CreateSessionWithInsert)
@@ -160,7 +175,7 @@ TEST_F(Sessions, CreateSessionWithInsert)
insertData.write("foo", 22, 3.14); insertData.write("foo", 22, 3.14);
sessions.commit(); sessions.commit();
ASSERT_THAT(fetchChangeSets(), SizeIs(1)); ASSERT_THAT(sessions.changeSets(), SizeIs(1));
} }
TEST_F(Sessions, CreateSessionWithUpdate) TEST_F(Sessions, CreateSessionWithUpdate)
@@ -171,7 +186,7 @@ TEST_F(Sessions, CreateSessionWithUpdate)
updateNumber.write("foo", "bar"); updateNumber.write("foo", "bar");
sessions.commit(); sessions.commit();
ASSERT_THAT(fetchChangeSets(), SizeIs(1)); ASSERT_THAT(sessions.changeSets(), SizeIs(1));
} }
TEST_F(Sessions, CreateSessionWithDelete) TEST_F(Sessions, CreateSessionWithDelete)
@@ -182,7 +197,7 @@ TEST_F(Sessions, CreateSessionWithDelete)
deleteData.write("foo"); deleteData.write("foo");
sessions.commit(); sessions.commit();
ASSERT_THAT(fetchChangeSets(), SizeIs(1)); ASSERT_THAT(sessions.changeSets(), SizeIs(1));
} }
TEST_F(Sessions, CreateSessionWithInsertAndUpdate) TEST_F(Sessions, CreateSessionWithInsertAndUpdate)
@@ -195,7 +210,7 @@ TEST_F(Sessions, CreateSessionWithInsertAndUpdate)
updateNumber.write("foo", "bar"); updateNumber.write("foo", "bar");
sessions.commit(); sessions.commit();
ASSERT_THAT(fetchChangeSets(), SizeIs(2)); ASSERT_THAT(sessions.changeSets(), SizeIs(2));
} }
TEST_F(Sessions, CreateSession) TEST_F(Sessions, CreateSession)
@@ -205,7 +220,7 @@ TEST_F(Sessions, CreateSession)
sessions.commit(); sessions.commit();
ASSERT_THAT(fetchChangeSets(), SizeIs(1)); ASSERT_THAT(sessions.changeSets(), SizeIs(1));
} }
TEST_F(Sessions, RevertSession) TEST_F(Sessions, RevertSession)
@@ -437,7 +452,261 @@ TEST_F(Sessions, ApplyAndUpdateSessionsHasOnlyOneChangeSet)
sessions.applyAndUpdateSessions(); sessions.applyAndUpdateSessions();
ASSERT_THAT(fetchChangeSets(), SizeIs(1)); ASSERT_THAT(sessions.changeSets(), SizeIs(1));
} }
TEST_F(Sessions, ForEmptySessionBeginEqualsEnd)
{
auto changeSets = sessions.changeSets();
auto begin = changeSets.begin();
ASSERT_THAT(begin, Eq(changeSets.end()));
}
TEST_F(Sessions, IteratorBeginUnequalsEndIfChangeSetHasContent)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
ASSERT_THAT(begin, Ne(changeSet.end()));
}
TEST_F(Sessions, NextIteratorUnequalsBeginIfChangeSetHasContent)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto next = std::next(changeSet.begin());
ASSERT_NE(next, changeSet.begin());
}
TEST_F(Sessions, NextIteratorEqualsEndIfChangeSetHasContent)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto next = std::next(changeSet.begin());
ASSERT_THAT(next, Eq(changeSet.end()));
}
TEST_F(Sessions, NextIteratorNotUnqualsEndIfChangeSetHasContent)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto next = std::next(changeSet.begin());
ASSERT_THAT(next, Not(Ne(changeSet.end())));
}
TEST_F(Sessions, BeginIteratorHasInsertOperation)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ASSERT_THAT(tuple.operation, Eq(Operation::Insert));
}
TEST_F(Sessions, BeginIteratorHasUpdateOperation)
{
insertData.write("foo", "bar", 3.14);
sessions.create();
updateValue.write("foo", 99);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ASSERT_THAT(tuple.operation, Eq(Operation::Update));
}
TEST_F(Sessions, BeginIteratorHasDeleteOperation)
{
insertData.write("foo", "bar", 3.14);
sessions.create();
deleteData.write("foo");
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ASSERT_THAT(tuple.operation, Eq(Operation::Delete));
}
TEST_F(Sessions, BeginIteratorHasDataTableName)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ASSERT_THAT(tuple.table, Eq("data"));
}
TEST_F(Sessions, ConvertAllValueTypesInChangeSet)
{
sessions.create();
insertData.write("foo", "bar", 3.14);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
std::vector<ValueViews> values{tuple.begin(), tuple.end()};
ASSERT_THAT(values,
ElementsAre(HasValues(1, nullptr),
HasValues("foo", nullptr),
HasValues("bar", nullptr),
HasValues(3.14, nullptr)));
}
TEST_F(Sessions, InsertOneValueChangeSet)
{
sessions.create();
insertOneDatum.write("foo");
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
std::vector<ValueViews> values{tuple.begin(), tuple.end()};
ASSERT_THAT(values,
ElementsAre(HasValues(1, nullptr),
HasValues(nullptr, nullptr),
HasValues(nullptr, nullptr),
HasValues("foo", nullptr)));
}
TEST_F(Sessions, UpdateOneValueChangeSet)
{
insertData.write("foo", "bar", 3.14);
sessions.create();
updateValue.write("foo", 99);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
std::vector<ValueViews> values{tuple.begin(), tuple.end()};
ASSERT_THAT(values,
ElementsAre(HasValues(nullptr, 1),
HasValues(nullptr, nullptr),
HasValues(nullptr, nullptr),
HasValues(99, 3.14)));
}
TEST_F(Sessions, DeleteRowChangeSet)
{
insertData.write("foo", "bar", 3.14);
sessions.create();
deleteData.write("foo");
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
std::vector<ValueViews> values{tuple.begin(), tuple.end()};
ASSERT_THAT(values,
ElementsAre(HasValues(nullptr, 1),
HasValues(nullptr, "foo"),
HasValues(nullptr, "bar"),
HasValues(nullptr, 3.14)));
}
TEST_F(Sessions, EmptyChangeSet)
{
sessions.create();
sessions.commit();
auto changeSets = sessions.changeSets();
ASSERT_THAT(changeSets, ElementsAre());
}
TEST_F(Sessions, AccessInsertOneValueChangeSet)
{
sessions.create();
insertOneDatum.write("foo");
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ValueViews value = tuple[3];
ASSERT_THAT(value, HasValues("foo", nullptr));
}
TEST_F(Sessions, AccessUpdateOneValueChangeSet)
{
insertData.write("foo", "bar", 3.14);
sessions.create();
updateValue.write("foo", 99);
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ValueViews value = tuple[3];
ASSERT_THAT(value, HasValues(99, 3.14));
}
TEST_F(Sessions, AccessDeleteRowChangeSet)
{
insertData.write("foo", "bar", 3.14);
sessions.create();
deleteData.write("foo");
sessions.commit();
auto changeSets = sessions.changeSets();
auto &&changeSet = changeSets.front();
auto begin = changeSet.begin();
auto tuple = (*begin);
ValueViews value = tuple[0];
ASSERT_THAT(value, HasValues(nullptr, 1));
}
} // namespace } // namespace