Refactor the transaction log parsers to eliminate some duplication
This commit is contained in:
parent
8c94cd1b2c
commit
d46f2c65ba
|
@ -29,9 +29,30 @@
|
|||
using namespace realm;
|
||||
|
||||
namespace {
|
||||
// A transaction log handler that just validates that all operations made are
|
||||
// ones supported by the object store
|
||||
class TransactLogValidator {
|
||||
template<typename Derived>
|
||||
struct MarkDirtyMixin {
|
||||
bool mark_dirty(size_t row, size_t col) { static_cast<Derived *>(this)->mark_dirty(row, col); return true; }
|
||||
|
||||
bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); }
|
||||
bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); }
|
||||
bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); }
|
||||
bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); }
|
||||
bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); }
|
||||
bool set_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); }
|
||||
bool set_timestamp(size_t col, size_t row, Timestamp) { return mark_dirty(row, col); }
|
||||
bool set_table(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); }
|
||||
bool set_link(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
bool set_null(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); }
|
||||
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
};
|
||||
|
||||
class TransactLogValidationMixin {
|
||||
// Index of currently selected table
|
||||
size_t m_current_table = 0;
|
||||
|
||||
|
@ -120,30 +141,20 @@ public:
|
|||
bool link_list_clear(size_t) { return true; }
|
||||
bool link_list_move(size_t, size_t) { return true; }
|
||||
bool link_list_swap(size_t, size_t) { return true; }
|
||||
bool set_int(size_t, size_t, int_fast64_t) { return true; }
|
||||
bool set_bool(size_t, size_t, bool) { return true; }
|
||||
bool set_float(size_t, size_t, float) { return true; }
|
||||
bool set_double(size_t, size_t, double) { return true; }
|
||||
bool set_string(size_t, size_t, StringData) { return true; }
|
||||
bool set_binary(size_t, size_t, BinaryData) { return true; }
|
||||
bool set_olddatetime(size_t, size_t, OldDateTime) { return true; }
|
||||
bool set_timestamp(size_t, size_t, Timestamp) { return true; }
|
||||
bool set_table(size_t, size_t) { return true; }
|
||||
bool set_mixed(size_t, size_t, const Mixed&) { return true; }
|
||||
bool set_link(size_t, size_t, size_t, size_t) { return true; }
|
||||
bool set_null(size_t, size_t) { return true; }
|
||||
bool nullify_link(size_t, size_t, size_t) { return true; }
|
||||
bool insert_substring(size_t, size_t, size_t, StringData) { return true; }
|
||||
bool erase_substring(size_t, size_t, size_t, size_t) { return true; }
|
||||
bool optimize_table() { return true; }
|
||||
bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; }
|
||||
bool set_string_unique(size_t, size_t, size_t, StringData) { return true; }
|
||||
bool change_link_targets(size_t, size_t) { return true; }
|
||||
bool optimize_table() { return true; }
|
||||
};
|
||||
|
||||
|
||||
// A transaction log handler that just validates that all operations made are
|
||||
// ones supported by the object store
|
||||
struct TransactLogValidator : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogValidator> {
|
||||
void mark_dirty(size_t, size_t) { }
|
||||
};
|
||||
|
||||
// Extends TransactLogValidator to also track changes and report it to the
|
||||
// binding context if any properties are being observed
|
||||
class TransactLogObserver : public TransactLogValidator {
|
||||
class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogObserver> {
|
||||
using ColumnInfo = BindingContext::ColumnInfo;
|
||||
using ObserverState = BindingContext::ObserverState;
|
||||
|
||||
|
@ -182,16 +193,6 @@ class TransactLogObserver : public TransactLogValidator {
|
|||
}
|
||||
}
|
||||
|
||||
// Mark the given row/col as needing notifications sent
|
||||
bool mark_dirty(size_t row_ndx, size_t col_ndx)
|
||||
{
|
||||
auto it = lower_bound(begin(m_observers), end(m_observers), ObserverState{current_table(), row_ndx, nullptr});
|
||||
if (it != end(m_observers) && it->table_ndx == current_table() && it->row_ndx == row_ndx) {
|
||||
get_change(*it, col_ndx).changed = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove the given observer from the list of observed objects and add it
|
||||
// to the listed of invalidated objects
|
||||
void invalidate(ObserverState *o)
|
||||
|
@ -207,10 +208,7 @@ public:
|
|||
{
|
||||
if (!context) {
|
||||
if (validate_schema_changes) {
|
||||
// The handler functions are non-virtual, so the parent class's
|
||||
// versions are called if we don't need to track changes to observed
|
||||
// objects
|
||||
func(static_cast<TransactLogValidator&>(*this));
|
||||
func(TransactLogValidator());
|
||||
}
|
||||
else {
|
||||
func();
|
||||
|
@ -222,7 +220,7 @@ public:
|
|||
if (m_observers.empty()) {
|
||||
auto old_version = sg.get_version_of_current_transaction();
|
||||
if (validate_schema_changes) {
|
||||
func(static_cast<TransactLogValidator&>(*this));
|
||||
func(TransactLogValidator());
|
||||
}
|
||||
else {
|
||||
func();
|
||||
|
@ -237,6 +235,15 @@ public:
|
|||
context->did_change(m_observers, invalidated);
|
||||
}
|
||||
|
||||
// Mark the given row/col as needing notifications sent
|
||||
void mark_dirty(size_t row_ndx, size_t col_ndx)
|
||||
{
|
||||
auto it = lower_bound(begin(m_observers), end(m_observers), ObserverState{current_table(), row_ndx, nullptr});
|
||||
if (it != end(m_observers) && it->table_ndx == current_table() && it->row_ndx == row_ndx) {
|
||||
get_change(*it, col_ndx).changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Called at the end of the transaction log immediately before the version
|
||||
// is advanced
|
||||
void parse_complete()
|
||||
|
@ -250,7 +257,7 @@ public:
|
|||
if (observer.table_ndx >= table_ndx)
|
||||
++observer.table_ndx;
|
||||
}
|
||||
TransactLogValidator::insert_group_level_table(table_ndx, prior_size, name);
|
||||
TransactLogValidationMixin::insert_group_level_table(table_ndx, prior_size, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -411,29 +418,10 @@ public:
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Things that just mark the field as modified
|
||||
bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); }
|
||||
bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); }
|
||||
bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); }
|
||||
bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); }
|
||||
bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); }
|
||||
bool set_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); }
|
||||
bool set_timestamp(size_t col, size_t row, Timestamp) { return mark_dirty(row, col); }
|
||||
bool set_table(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); }
|
||||
bool set_link(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
bool set_null(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); }
|
||||
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
};
|
||||
|
||||
// Extends TransactLogValidator to track changes made to LinkViews
|
||||
class LinkViewObserver : public TransactLogValidator {
|
||||
class LinkViewObserver : public TransactLogValidationMixin, public MarkDirtyMixin<LinkViewObserver> {
|
||||
_impl::TransactionChangeInfo& m_info;
|
||||
CollectionChangeIndices* m_active = nullptr;
|
||||
|
||||
|
@ -448,17 +436,16 @@ class LinkViewObserver : public TransactLogValidator {
|
|||
return &m_info.tables[tbl_ndx];
|
||||
}
|
||||
|
||||
bool mark_dirty(size_t row, __unused size_t col)
|
||||
{
|
||||
if (auto change = get_change())
|
||||
change->modify(row);
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
LinkViewObserver(_impl::TransactionChangeInfo& info)
|
||||
: m_info(info) { }
|
||||
|
||||
void mark_dirty(size_t row, __unused size_t col)
|
||||
{
|
||||
if (auto change = get_change())
|
||||
change->modify(row);
|
||||
}
|
||||
|
||||
bool select_link_list(size_t col, size_t row, size_t)
|
||||
{
|
||||
mark_dirty(row, col);
|
||||
|
@ -563,25 +550,6 @@ public:
|
|||
change->clear(0); // FIXME
|
||||
return true;
|
||||
}
|
||||
|
||||
// Things that just mark the field as modified
|
||||
bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); }
|
||||
bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); }
|
||||
bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); }
|
||||
bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); }
|
||||
bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); }
|
||||
bool set_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); }
|
||||
bool set_timestamp(size_t col, size_t row, Timestamp) { return mark_dirty(row, col); }
|
||||
bool set_table(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); }
|
||||
bool set_link(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
bool set_null(size_t col, size_t row) { return mark_dirty(row, col); }
|
||||
bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); }
|
||||
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
|
||||
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
|
||||
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
|
|
|
@ -363,8 +363,28 @@ TEST_CASE("Async Results error handling") {
|
|||
auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
|
||||
Results results(r, *config.schema->find("object"), *r->read_group()->get_table("class_object"));
|
||||
|
||||
class OpenFileLimiter {
|
||||
public:
|
||||
OpenFileLimiter()
|
||||
{
|
||||
// Set the max open files to zero so that opening new files will fail
|
||||
getrlimit(RLIMIT_NOFILE, &m_old);
|
||||
rlimit rl = m_old;
|
||||
rl.rlim_cur = 0;
|
||||
setrlimit(RLIMIT_NOFILE, &rl);
|
||||
}
|
||||
|
||||
~OpenFileLimiter()
|
||||
{
|
||||
setrlimit(RLIMIT_NOFILE, &m_old);
|
||||
}
|
||||
|
||||
private:
|
||||
rlimit m_old;
|
||||
};
|
||||
|
||||
SECTION("error when opening the advancer SG") {
|
||||
unlink(config.path.c_str());
|
||||
OpenFileLimiter limiter;
|
||||
|
||||
SECTION("error is delivered asynchronously") {
|
||||
bool called = false;
|
||||
|
@ -412,7 +432,7 @@ TEST_CASE("Async Results error handling") {
|
|||
REQUIRE(err);
|
||||
called = true;
|
||||
});
|
||||
unlink(config.path.c_str());
|
||||
OpenFileLimiter limiter;
|
||||
|
||||
REQUIRE(!called);
|
||||
coordinator->on_change();
|
||||
|
@ -428,7 +448,7 @@ TEST_CASE("Async Results error handling") {
|
|||
REQUIRE_FALSE(called);
|
||||
called = true;
|
||||
});
|
||||
unlink(config.path.c_str());
|
||||
OpenFileLimiter limiter;
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
|
@ -445,6 +465,5 @@ TEST_CASE("Async Results error handling") {
|
|||
|
||||
REQUIRE(called2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue