mirror of
https://github.com/status-im/realm-js.git
synced 2025-02-17 09:06:26 +00:00
Replace Results::set_live
with Results::snapshot
and List::snapshot
, and add tests.
`snapshot()` functions are a better fit for what realm-js needs. The new API also makes it clearer that the liveness of a given `Results` cannot change at arbitrary times. Changing the liveness at arbitrary times was not safe and could result in incorrect behavior, such as a non-live `Results` changing.
This commit is contained in:
parent
8798b1c617
commit
7c0b99594a
@ -181,6 +181,12 @@ Results List::filter(Query q)
|
|||||||
return Results(m_realm, *m_object_schema, m_link_view, get_query().and_query(std::move(q)));
|
return Results(m_realm, *m_object_schema, m_link_view, get_query().and_query(std::move(q)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Results List::snapshot() const
|
||||||
|
{
|
||||||
|
verify_attached();
|
||||||
|
return Results(m_realm, *m_object_schema, m_link_view).snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
// These definitions rely on that LinkViews are interned by core
|
// These definitions rely on that LinkViews are interned by core
|
||||||
bool List::operator==(List const& rgt) const noexcept
|
bool List::operator==(List const& rgt) const noexcept
|
||||||
{
|
{
|
||||||
|
@ -75,6 +75,9 @@ public:
|
|||||||
Results sort(SortOrder order);
|
Results sort(SortOrder order);
|
||||||
Results filter(Query q);
|
Results filter(Query q);
|
||||||
|
|
||||||
|
// Return a Results representing a snapshot of this List.
|
||||||
|
Results snapshot() const;
|
||||||
|
|
||||||
bool operator==(List const& rgt) const noexcept;
|
bool operator==(List const& rgt) const noexcept;
|
||||||
|
|
||||||
NotificationToken add_notification_callback(CollectionChangeCallback cb);
|
NotificationToken add_notification_callback(CollectionChangeCallback cb);
|
||||||
|
@ -109,9 +109,9 @@ Results::Results(Results&& other)
|
|||||||
, m_link_view(std::move(other.m_link_view))
|
, m_link_view(std::move(other.m_link_view))
|
||||||
, m_table(other.m_table)
|
, m_table(other.m_table)
|
||||||
, m_sort(std::move(other.m_sort))
|
, m_sort(std::move(other.m_sort))
|
||||||
, m_live(other.m_live)
|
|
||||||
, m_notifier(std::move(other.m_notifier))
|
, m_notifier(std::move(other.m_notifier))
|
||||||
, m_mode(other.m_mode)
|
, m_mode(other.m_mode)
|
||||||
|
, m_update_policy(other.m_update_policy)
|
||||||
, m_has_used_table_view(other.m_has_used_table_view)
|
, m_has_used_table_view(other.m_has_used_table_view)
|
||||||
, m_wants_background_updates(other.m_wants_background_updates)
|
, m_wants_background_updates(other.m_wants_background_updates)
|
||||||
{
|
{
|
||||||
@ -152,19 +152,6 @@ void Results::validate_write() const
|
|||||||
throw InvalidTransactionException("Must be in a write transaction");
|
throw InvalidTransactionException("Must be in a write transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Results::set_live(bool live)
|
|
||||||
{
|
|
||||||
validate_read();
|
|
||||||
|
|
||||||
if (!live && (m_mode == Mode::Table || m_mode == Mode::LinkView)) {
|
|
||||||
m_query = get_query();
|
|
||||||
m_mode = Mode::Query;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_tableview();
|
|
||||||
m_live = live;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Results::size()
|
size_t Results::size()
|
||||||
{
|
{
|
||||||
validate_read();
|
validate_read();
|
||||||
@ -208,7 +195,7 @@ RowExpr Results::get(size_t row_ndx)
|
|||||||
update_tableview();
|
update_tableview();
|
||||||
if (row_ndx >= m_table_view.size())
|
if (row_ndx >= m_table_view.size())
|
||||||
break;
|
break;
|
||||||
if (!m_live && !m_table_view.is_row_attached(row_ndx))
|
if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(row_ndx))
|
||||||
return {};
|
return {};
|
||||||
return m_table_view.get(row_ndx);
|
return m_table_view.get(row_ndx);
|
||||||
}
|
}
|
||||||
@ -258,6 +245,8 @@ util::Optional<RowExpr> Results::last()
|
|||||||
|
|
||||||
bool Results::update_linkview()
|
bool Results::update_linkview()
|
||||||
{
|
{
|
||||||
|
REALM_ASSERT(m_update_policy == UpdatePolicy::Auto);
|
||||||
|
|
||||||
if (m_sort) {
|
if (m_sort) {
|
||||||
m_query = get_query();
|
m_query = get_query();
|
||||||
m_mode = Mode::Query;
|
m_mode = Mode::Query;
|
||||||
@ -267,9 +256,13 @@ bool Results::update_linkview()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Results::update_tableview()
|
void Results::update_tableview(bool wants_notifications)
|
||||||
{
|
{
|
||||||
validate_read();
|
if (m_update_policy == UpdatePolicy::Never) {
|
||||||
|
REALM_ASSERT(m_mode == Mode::TableView);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (m_mode) {
|
switch (m_mode) {
|
||||||
case Mode::Empty:
|
case Mode::Empty:
|
||||||
case Mode::Table:
|
case Mode::Table:
|
||||||
@ -284,10 +277,7 @@ void Results::update_tableview()
|
|||||||
m_mode = Mode::TableView;
|
m_mode = Mode::TableView;
|
||||||
break;
|
break;
|
||||||
case Mode::TableView:
|
case Mode::TableView:
|
||||||
if (!m_live) {
|
if (wants_notifications && !m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) {
|
|
||||||
m_notifier = std::make_shared<_impl::ResultsNotifier>(*this);
|
m_notifier = std::make_shared<_impl::ResultsNotifier>(*this);
|
||||||
_impl::RealmCoordinator::register_notifier(m_notifier);
|
_impl::RealmCoordinator::register_notifier(m_notifier);
|
||||||
}
|
}
|
||||||
@ -429,13 +419,16 @@ void Results::clear()
|
|||||||
validate_write();
|
validate_write();
|
||||||
update_tableview();
|
update_tableview();
|
||||||
|
|
||||||
if (m_live) {
|
switch (m_update_policy) {
|
||||||
m_table_view.clear(RemoveMode::unordered);
|
case UpdatePolicy::Auto:
|
||||||
}
|
m_table_view.clear(RemoveMode::unordered);
|
||||||
else {
|
break;
|
||||||
// Copy the TableView because a non-live Results shouldn't have let its size() change.
|
case UpdatePolicy::Never: {
|
||||||
TableView table_view_copy = m_table_view;
|
// Copy the TableView because a frozen Results shouldn't let its size() change.
|
||||||
table_view_copy.clear(RemoveMode::unordered);
|
TableView copy(m_table_view);
|
||||||
|
copy.clear(RemoveMode::unordered);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Mode::LinkView:
|
case Mode::LinkView:
|
||||||
@ -462,7 +455,9 @@ Query Results::get_query() const
|
|||||||
|
|
||||||
// The TableView has no associated query so create one with no conditions that is restricted
|
// The TableView has no associated query so create one with no conditions that is restricted
|
||||||
// to the rows in the TableView.
|
// to the rows in the TableView.
|
||||||
m_table_view.sync_if_needed();
|
if (m_update_policy == UpdatePolicy::Auto) {
|
||||||
|
m_table_view.sync_if_needed();
|
||||||
|
}
|
||||||
return Query(*m_table, std::unique_ptr<TableViewBase>(new TableView(m_table_view)));
|
return Query(*m_table, std::unique_ptr<TableViewBase>(new TableView(m_table_view)));
|
||||||
}
|
}
|
||||||
case Mode::LinkView:
|
case Mode::LinkView:
|
||||||
@ -504,6 +499,37 @@ Results Results::filter(Query&& q) const
|
|||||||
return Results(m_realm, *m_object_schema, get_query().and_query(std::move(q)), m_sort);
|
return Results(m_realm, *m_object_schema, get_query().and_query(std::move(q)), m_sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Results Results::snapshot() const &
|
||||||
|
{
|
||||||
|
validate_read();
|
||||||
|
|
||||||
|
return Results(*this).snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
Results Results::snapshot() &&
|
||||||
|
{
|
||||||
|
validate_read();
|
||||||
|
|
||||||
|
switch (m_mode) {
|
||||||
|
case Mode::Empty:
|
||||||
|
return Results();
|
||||||
|
|
||||||
|
case Mode::Table:
|
||||||
|
case Mode::LinkView:
|
||||||
|
m_query = get_query();
|
||||||
|
m_mode = Mode::Query;
|
||||||
|
|
||||||
|
REALM_FALLTHROUGH;
|
||||||
|
case Mode::Query:
|
||||||
|
case Mode::TableView:
|
||||||
|
update_tableview(false);
|
||||||
|
m_notifier.reset();
|
||||||
|
m_update_policy = UpdatePolicy::Never;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
REALM_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
void Results::prepare_async()
|
void Results::prepare_async()
|
||||||
{
|
{
|
||||||
if (m_realm->config().read_only) {
|
if (m_realm->config().read_only) {
|
||||||
@ -512,6 +538,9 @@ void Results::prepare_async()
|
|||||||
if (m_realm->is_in_transaction()) {
|
if (m_realm->is_in_transaction()) {
|
||||||
throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction");
|
throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction");
|
||||||
}
|
}
|
||||||
|
if (m_update_policy == UpdatePolicy::Never) {
|
||||||
|
throw std::logic_error("Cannot create asynchronous query for snapshotted Results.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_notifier) {
|
if (!m_notifier) {
|
||||||
m_wants_background_updates = true;
|
m_wants_background_updates = true;
|
||||||
@ -551,6 +580,7 @@ bool Results::is_in_table_order() const
|
|||||||
|
|
||||||
void Results::Internal::set_table_view(Results& results, realm::TableView &&tv)
|
void Results::Internal::set_table_view(Results& results, realm::TableView &&tv)
|
||||||
{
|
{
|
||||||
|
REALM_ASSERT(results.m_update_policy != UpdatePolicy::Never);
|
||||||
// If the previous TableView was never actually used, then stop generating
|
// If the previous TableView was never actually used, then stop generating
|
||||||
// new ones until the user actually uses the Results object again
|
// new ones until the user actually uses the Results object again
|
||||||
if (results.m_mode == Mode::TableView) {
|
if (results.m_mode == Mode::TableView) {
|
||||||
|
@ -83,9 +83,6 @@ public:
|
|||||||
// Get the LinkView this Results is derived from, if any
|
// Get the LinkView this Results is derived from, if any
|
||||||
LinkViewRef get_linkview() const { return m_link_view; }
|
LinkViewRef get_linkview() const { return m_link_view; }
|
||||||
|
|
||||||
// Set whether the TableView should sync if needed before accessing results
|
|
||||||
void set_live(bool live);
|
|
||||||
|
|
||||||
// Get the size of this results
|
// Get the size of this results
|
||||||
// Can be either O(1) or O(N) depending on the state of things
|
// Can be either O(1) or O(N) depending on the state of things
|
||||||
size_t size();
|
size_t size();
|
||||||
@ -114,6 +111,10 @@ public:
|
|||||||
Results filter(Query&& q) const;
|
Results filter(Query&& q) const;
|
||||||
Results sort(SortOrder&& sort) const;
|
Results sort(SortOrder&& sort) const;
|
||||||
|
|
||||||
|
// Return a snapshot of this Results that never updates to reflect changes in the underlying data.
|
||||||
|
Results snapshot() const &;
|
||||||
|
Results snapshot() &&;
|
||||||
|
|
||||||
// Get the min/max/average/sum of the given column
|
// Get the min/max/average/sum of the given column
|
||||||
// All but sum() returns none when there are zero matching rows
|
// All but sum() returns none when there are zero matching rows
|
||||||
// sum() returns 0, except for when it returns none
|
// sum() returns 0, except for when it returns none
|
||||||
@ -128,8 +129,8 @@ public:
|
|||||||
Empty, // Backed by nothing (for missing tables)
|
Empty, // Backed by nothing (for missing tables)
|
||||||
Table, // Backed directly by a Table
|
Table, // Backed directly by a Table
|
||||||
Query, // Backed by a query that has not yet been turned into a TableView
|
Query, // Backed by a query that has not yet been turned into a TableView
|
||||||
LinkView, // Backed directly by a LinkView
|
LinkView, // Backed directly by a LinkView
|
||||||
TableView // Backed by a TableView created from a Query
|
TableView, // Backed by a TableView created from a Query
|
||||||
};
|
};
|
||||||
// Get the currrent mode of the Results
|
// Get the currrent mode of the Results
|
||||||
// Ideally this would not be public but it's needed for some KVO stuff
|
// Ideally this would not be public but it's needed for some KVO stuff
|
||||||
@ -192,6 +193,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class UpdatePolicy {
|
||||||
|
Auto, // Update automatically to reflect changes in the underlying data.
|
||||||
|
Never, // Never update.
|
||||||
|
};
|
||||||
|
|
||||||
SharedRealm m_realm;
|
SharedRealm m_realm;
|
||||||
const ObjectSchema *m_object_schema;
|
const ObjectSchema *m_object_schema;
|
||||||
Query m_query;
|
Query m_query;
|
||||||
@ -199,15 +205,15 @@ private:
|
|||||||
LinkViewRef m_link_view;
|
LinkViewRef m_link_view;
|
||||||
Table* m_table = nullptr;
|
Table* m_table = nullptr;
|
||||||
SortOrder m_sort;
|
SortOrder m_sort;
|
||||||
bool m_live = true;
|
|
||||||
|
|
||||||
_impl::CollectionNotifier::Handle<_impl::ResultsNotifier> m_notifier;
|
_impl::CollectionNotifier::Handle<_impl::ResultsNotifier> m_notifier;
|
||||||
|
|
||||||
Mode m_mode = Mode::Empty;
|
Mode m_mode = Mode::Empty;
|
||||||
|
UpdatePolicy m_update_policy = UpdatePolicy::Auto;
|
||||||
bool m_has_used_table_view = false;
|
bool m_has_used_table_view = false;
|
||||||
bool m_wants_background_updates = true;
|
bool m_wants_background_updates = true;
|
||||||
|
|
||||||
void update_tableview();
|
void update_tableview(bool wants_notifications = true);
|
||||||
bool update_linkview();
|
bool update_linkview();
|
||||||
|
|
||||||
void validate_read() const;
|
void validate_read() const;
|
||||||
|
@ -447,4 +447,35 @@ TEST_CASE("list") {
|
|||||||
REQUIRE(results.get(i).get_index() == i + 6);
|
REQUIRE(results.get(i).get_index() == i + 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot()") {
|
||||||
|
auto objectschema = &*r->config().schema->find("origin");
|
||||||
|
List list(r, *objectschema, lv);
|
||||||
|
|
||||||
|
auto snapshot = list.snapshot();
|
||||||
|
REQUIRE(&snapshot.get_object_schema() == objectschema);
|
||||||
|
REQUIRE(snapshot.get_mode() == Results::Mode::TableView);
|
||||||
|
REQUIRE(snapshot.size() == 10);
|
||||||
|
|
||||||
|
r->begin_transaction();
|
||||||
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
list.remove(0);
|
||||||
|
}
|
||||||
|
REQUIRE(snapshot.size() == 10);
|
||||||
|
for (size_t i = 0; i < snapshot.size(); ++i) {
|
||||||
|
REQUIRE(snapshot.get(i).is_attached());
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
target->move_last_over(i);
|
||||||
|
}
|
||||||
|
REQUIRE(snapshot.size() == 10);
|
||||||
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
REQUIRE(!snapshot.get(i).is_attached());
|
||||||
|
}
|
||||||
|
for (size_t i = 5; i < 10; ++i) {
|
||||||
|
REQUIRE(snapshot.get(i).is_attached());
|
||||||
|
}
|
||||||
|
list.add(0);
|
||||||
|
REQUIRE(snapshot.size() == 10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <realm/commit_log.hpp>
|
#include <realm/commit_log.hpp>
|
||||||
#include <realm/group_shared.hpp>
|
#include <realm/group_shared.hpp>
|
||||||
#include <realm/link_view.hpp>
|
#include <realm/link_view.hpp>
|
||||||
|
#include <realm/query_engine.hpp>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -609,3 +610,307 @@ TEST_CASE("[results] error messages") {
|
|||||||
REQUIRE_THROWS_WITH(results.sum(0), "Cannot sum property 'value': operation not supported for 'string' properties");
|
REQUIRE_THROWS_WITH(results.sum(0), "Cannot sum property 'value': operation not supported for 'string' properties");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("results: snapshots") {
|
||||||
|
InMemoryTestFile config;
|
||||||
|
config.cache = false;
|
||||||
|
config.automatic_change_notifications = false;
|
||||||
|
config.schema = std::make_unique<Schema>(Schema{
|
||||||
|
{"object", "", {
|
||||||
|
{"value", PropertyType::Int},
|
||||||
|
{"array", PropertyType::Array, "linked to object"}
|
||||||
|
}},
|
||||||
|
{"linked to object", "", {
|
||||||
|
{"value", PropertyType::Int}
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto r = Realm::get_shared_realm(config);
|
||||||
|
|
||||||
|
auto write = [&](auto&& f) {
|
||||||
|
r->begin_transaction();
|
||||||
|
f();
|
||||||
|
r->commit_transaction();
|
||||||
|
advance_and_notify(*r);
|
||||||
|
};
|
||||||
|
|
||||||
|
SECTION("snapshot of empty Results") {
|
||||||
|
Results results;
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of Results based on Table") {
|
||||||
|
auto table = r->read_group()->get_table("class_object");
|
||||||
|
Results results(r, *config.schema->find("object"), *table);
|
||||||
|
|
||||||
|
{
|
||||||
|
// A newly-added row should not appear in the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
write([=]{
|
||||||
|
table->add_empty_row();
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Removing a row present in the snapshot should not affect the size of the snapshot,
|
||||||
|
// but will result in the snapshot returning a detached row accessor.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
write([=]{
|
||||||
|
table->move_last_over(0);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Adding a row at the same index that was formerly present in the snapshot shouldn't
|
||||||
|
// affect the state of the snapshot.
|
||||||
|
write([=]{
|
||||||
|
table->add_empty_row();
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of Results based on LinkView") {
|
||||||
|
auto object = r->read_group()->get_table("class_object");
|
||||||
|
auto linked_to = r->read_group()->get_table("class_linked to object");
|
||||||
|
|
||||||
|
write([=]{
|
||||||
|
object->add_empty_row();
|
||||||
|
});
|
||||||
|
|
||||||
|
LinkViewRef lv = object->get_linklist(1, 0);
|
||||||
|
Results results(r, *config.schema->find("linked to object"), lv);
|
||||||
|
|
||||||
|
{
|
||||||
|
// A newly-added row should not appear in the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
write([=]{
|
||||||
|
lv->add(linked_to->add_empty_row());
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Removing a row from the link list should not affect the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
write([=]{
|
||||||
|
lv->remove(0);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||||
|
// returning a detached row accessor.
|
||||||
|
write([=]{
|
||||||
|
linked_to->remove(0);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Adding a new row to the link list shouldn't affect the state of the snapshot.
|
||||||
|
write([=]{
|
||||||
|
lv->add(linked_to->add_empty_row());
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of Results based on Query") {
|
||||||
|
auto table = r->read_group()->get_table("class_object");
|
||||||
|
Query q = table->column<Int>(0) > 0;
|
||||||
|
Results results(r, *config.schema->find("object"), std::move(q));
|
||||||
|
|
||||||
|
{
|
||||||
|
// A newly-added row should not appear in the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
write([=]{
|
||||||
|
table->set_int(0, table->add_empty_row(), 1);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Updating a row to no longer match the query criteria should not affect the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
write([=]{
|
||||||
|
table->set_int(0, 0, 0);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||||
|
// returning a detached row accessor.
|
||||||
|
write([=]{
|
||||||
|
table->remove(0);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
|
||||||
|
write([=]{
|
||||||
|
table->set_int(0, table->add_empty_row(), 1);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of Results based on TableView from query") {
|
||||||
|
auto table = r->read_group()->get_table("class_object");
|
||||||
|
Query q = table->column<Int>(0) > 0;
|
||||||
|
Results results(r, *config.schema->find("object"), q.find_all(), {});
|
||||||
|
|
||||||
|
{
|
||||||
|
// A newly-added row should not appear in the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
write([=]{
|
||||||
|
table->set_int(0, table->add_empty_row(), 1);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Updating a row to no longer match the query criteria should not affect the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
write([=]{
|
||||||
|
table->set_int(0, 0, 0);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||||
|
// returning a detached row accessor.
|
||||||
|
write([=]{
|
||||||
|
table->remove(0);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Adding a new row that matches the query criteria shouldn't affect the state of the snapshot.
|
||||||
|
write([=]{
|
||||||
|
table->set_int(0, table->add_empty_row(), 1);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of Results based on TableView from backlinks") {
|
||||||
|
auto object = r->read_group()->get_table("class_object");
|
||||||
|
auto linked_to = r->read_group()->get_table("class_linked to object");
|
||||||
|
|
||||||
|
write([=]{
|
||||||
|
linked_to->add_empty_row();
|
||||||
|
});
|
||||||
|
|
||||||
|
TableView backlinks = linked_to->get_backlink_view(0, object.get(), 1);
|
||||||
|
Results results(r, *config.schema->find("object"), std::move(backlinks), {});
|
||||||
|
|
||||||
|
auto lv = object->get_linklist(1, object->add_empty_row());
|
||||||
|
|
||||||
|
{
|
||||||
|
// A newly-added row should not appear in the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
write([=]{
|
||||||
|
lv->add(0);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Removing the link should not affect the snapshot.
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
REQUIRE(results.size() == 1);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
write([=]{
|
||||||
|
lv->remove(0);
|
||||||
|
});
|
||||||
|
REQUIRE(results.size() == 0);
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Removing a row present in the snapshot from its table should result in the snapshot
|
||||||
|
// returning a detached row accessor.
|
||||||
|
write([=]{
|
||||||
|
object->remove(0);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
|
||||||
|
// Adding a new link shouldn't affect the state of the snapshot.
|
||||||
|
write([=]{
|
||||||
|
object->add_empty_row();
|
||||||
|
auto lv = object->get_linklist(1, object->add_empty_row());
|
||||||
|
lv->add(0);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 1);
|
||||||
|
REQUIRE(!snapshot.get(0).is_attached());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of Results with notification callback registered") {
|
||||||
|
auto table = r->read_group()->get_table("class_object");
|
||||||
|
Query q = table->column<Int>(0) > 0;
|
||||||
|
Results results(r, *config.schema->find("object"), q.find_all(), {});
|
||||||
|
|
||||||
|
auto token = results.add_notification_callback([&](CollectionChangeSet, std::exception_ptr err) {
|
||||||
|
REQUIRE_FALSE(err);
|
||||||
|
});
|
||||||
|
advance_and_notify(*r);
|
||||||
|
|
||||||
|
SECTION("snapshot of lvalue") {
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
write([=] {
|
||||||
|
table->set_int(0, table->add_empty_row(), 1);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("snapshot of rvalue") {
|
||||||
|
auto snapshot = std::move(results).snapshot();
|
||||||
|
write([=] {
|
||||||
|
table->set_int(0, table->add_empty_row(), 1);
|
||||||
|
});
|
||||||
|
REQUIRE(snapshot.size() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("adding notification callback to snapshot throws") {
|
||||||
|
auto table = r->read_group()->get_table("class_object");
|
||||||
|
Query q = table->column<Int>(0) > 0;
|
||||||
|
Results results(r, *config.schema->find("object"), q.find_all(), {});
|
||||||
|
auto snapshot = results.snapshot();
|
||||||
|
CHECK_THROWS(snapshot.add_notification_callback([](CollectionChangeSet, std::exception_ptr) {}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user