1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
19 #ifndef REALM_LINK_VIEW_HPP
20 #define REALM_LINK_VIEW_HPP
22 #include <realm/column.hpp>
23 #include <realm/column_linklist.hpp>
24 #include <realm/link_view_fwd.hpp>
25 #include <realm/table.hpp>
33 class TransactLogConvenientEncoder;
37 /// The effect of calling most of the link list functions on a detached accessor
38 /// is unspecified and may lead to general corruption, or even a crash. The
39 /// exceptions are is_attached() and the destructor.
41 /// FIXME: Rename this class to `LinkList`.
42 class LinkView : public RowIndexes, public std::enable_shared_from_this<LinkView> {
45 bool is_attached() const noexcept;
47 /// This method will return true if the LinkView is detached (no assert).
48 bool is_empty() const noexcept;
50 /// This method will return 0 if the LinkView is detached (no assert).
51 size_t size() const noexcept override;
53 bool operator==(const LinkView&) const noexcept;
54 bool operator!=(const LinkView&) const noexcept;
57 Table::ConstRowExpr operator[](size_t link_ndx) const noexcept;
58 Table::RowExpr operator[](size_t link_ndx) noexcept;
59 Table::ConstRowExpr get(size_t link_ndx) const noexcept;
60 Table::RowExpr get(size_t link_ndx) noexcept;
63 void add(size_t target_row_ndx);
64 void insert(size_t link_ndx, size_t target_row_ndx);
65 void set(size_t link_ndx, size_t target_row_ndx);
66 /// Move the link at \a from_ndx such that it ends up at \a to_ndx. Other
67 /// links are shifted as necessary in such a way that their order is
70 /// Note that \a to_ndx is the desired final index of the moved link,
71 /// therefore, `move(1,1)` is a no-op, while `move(1,2)` moves the link at
72 /// index 1 by one position, such that it ends up at index 2. A side-effect
73 /// of that, is that the link, that was originally at index 2, is moved to
75 void move(size_t from_ndx, size_t to_ndx);
76 void swap(size_t link1_ndx, size_t link2_ndx);
77 void remove(size_t link_ndx);
80 void sort(size_t column, bool ascending = true);
81 void sort(SortDescriptor&& order);
83 TableView get_sorted_view(size_t column_index, bool ascending = true) const;
84 TableView get_sorted_view(SortDescriptor order) const;
86 /// Remove the target row of the specified link from the target table. This
87 /// also removes the specified link from this link list, and any other link
88 /// pointing to that row. This is merely a shorthand for
89 /// `get_target_table.move_last_over(get(link_ndx))`.
90 void remove_target_row(size_t link_ndx);
92 /// Remove all target rows pointed to by links in this link list, and clear
94 void remove_all_target_rows();
96 /// Search this list for a link to the specified target table row (specified
97 /// by its index in the target table). If found, the index of the link to
98 /// that row within this list is returned, otherwise `realm::not_found` is
100 size_t find(size_t target_row_ndx, size_t start = 0) const noexcept;
102 const ColumnBase& get_column_base(size_t index)
103 const override; // FIXME: `ColumnBase` is not part of the public API, so this function must be made private.
104 const Table& get_origin_table() const noexcept;
105 Table& get_origin_table() noexcept;
107 size_t get_origin_row_index() const noexcept;
109 const Table& get_target_table() const noexcept;
110 Table& get_target_table() noexcept;
112 // No-op because LinkViews are always kept in sync.
113 uint_fast64_t sync_if_needed() const override;
114 bool is_in_sync() const override
123 TableRef m_origin_table;
124 LinkListColumn* m_origin_column;
126 using HandoverPatch = LinkViewHandoverPatch;
127 static void generate_patch(const ConstLinkViewRef& ref, std::unique_ptr<HandoverPatch>& patch);
128 static LinkViewRef create_from_and_consume_patch(std::unique_ptr<HandoverPatch>& patch, Group& group);
131 void set_origin_row_index(size_t row_ndx) noexcept;
133 void do_insert(size_t link_ndx, size_t target_row_ndx);
134 size_t do_set(size_t link_ndx, size_t target_row_ndx);
135 size_t do_remove(size_t link_ndx);
136 void do_clear(bool broken_reciprocal_backlinks);
138 void do_nullify_link(size_t old_target_row_ndx);
139 void do_update_link(size_t old_target_row_ndx, size_t new_target_row_ndx);
140 void do_swap_link(size_t target_row_ndx_1, size_t target_row_ndx_2);
142 void refresh_accessor_tree(size_t new_row_ndx) noexcept;
144 void update_from_parent(size_t old_baseline) noexcept;
146 Replication* get_repl() noexcept;
147 void repl_unselect() noexcept;
148 friend class _impl::TransactLogConvenientEncoder;
151 void verify(size_t row_ndx) const;
153 // allocate using make_shared:
154 static std::shared_ptr<LinkView> create(Table* origin_table, LinkListColumn&, size_t row_ndx);
155 static std::shared_ptr<LinkView> create_detached();
157 friend class _impl::LinkListFriend;
158 friend class LinkListColumn;
159 friend class LangBindHelper;
160 friend class SharedGroup;
162 friend class TableViewBase;
164 // must be public for use by make_shared, but cannot be called from outside,
165 // because ctor_cookie is private
167 LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn&, size_t row_ndx);
168 LinkView(const ctor_cookie&);
174 inline LinkView::LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn& column, size_t row_ndx)
175 : RowIndexes(IntegerColumn::unattached_root_tag(), column.get_alloc()) // Throws
176 , m_origin_table(origin_table->get_table_ref())
177 , m_origin_column(&column)
179 m_row_indexes.set_parent(m_origin_column, row_ndx);
180 m_row_indexes.init_from_parent();
183 // create a detached LinkView. Only partially initialized, as it will never be used for
184 // anything, but indicating that it is detached.
185 inline LinkView::LinkView(const ctor_cookie&)
186 : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws
187 , m_origin_table(TableRef())
188 , m_origin_column(nullptr)
192 inline std::shared_ptr<LinkView> LinkView::create(Table* origin_table, LinkListColumn& column, size_t row_ndx)
194 return std::make_shared<LinkView>(ctor_cookie(), origin_table, column, row_ndx);
197 inline std::shared_ptr<LinkView> LinkView::create_detached()
199 return std::make_shared<LinkView>(ctor_cookie());
202 inline LinkView::~LinkView() noexcept
206 m_origin_column->unregister_linkview();
210 inline void LinkView::detach()
212 REALM_ASSERT(is_attached());
214 m_origin_table.reset();
215 m_row_indexes.detach();
218 inline bool LinkView::is_attached() const noexcept
220 return static_cast<bool>(m_origin_table);
223 inline bool LinkView::is_empty() const noexcept
228 if (!m_row_indexes.is_attached())
231 return m_row_indexes.is_empty();
234 inline size_t LinkView::size() const noexcept
239 if (!m_row_indexes.is_attached())
242 return m_row_indexes.size();
245 inline bool LinkView::operator==(const LinkView& link_list) const noexcept
247 Table& target_table_1 = m_origin_column->get_target_table();
248 Table& target_table_2 = link_list.m_origin_column->get_target_table();
249 if (target_table_1.get_index_in_group() != target_table_2.get_index_in_group())
251 if (!m_row_indexes.is_attached() || m_row_indexes.is_empty()) {
252 return !link_list.m_row_indexes.is_attached() || link_list.m_row_indexes.is_empty();
254 return link_list.m_row_indexes.is_attached() && m_row_indexes.compare(link_list.m_row_indexes);
257 inline bool LinkView::operator!=(const LinkView& link_list) const noexcept
259 return !(*this == link_list);
262 inline Table::ConstRowExpr LinkView::get(size_t link_ndx) const noexcept
264 return const_cast<LinkView*>(this)->get(link_ndx);
267 inline Table::RowExpr LinkView::get(size_t link_ndx) noexcept
269 REALM_ASSERT(is_attached());
270 REALM_ASSERT(m_row_indexes.is_attached());
271 REALM_ASSERT_3(link_ndx, <, m_row_indexes.size());
273 Table& target_table = m_origin_column->get_target_table();
274 size_t target_row_ndx = to_size_t(m_row_indexes.get(link_ndx));
275 return target_table[target_row_ndx];
278 inline Table::ConstRowExpr LinkView::operator[](size_t link_ndx) const noexcept
280 return get(link_ndx);
283 inline Table::RowExpr LinkView::operator[](size_t link_ndx) noexcept
285 return get(link_ndx);
288 inline void LinkView::add(size_t target_row_ndx)
290 REALM_ASSERT(is_attached());
291 size_t ins_pos = (m_row_indexes.is_attached()) ? m_row_indexes.size() : 0;
292 insert(ins_pos, target_row_ndx);
295 inline size_t LinkView::find(size_t target_row_ndx, size_t start) const noexcept
297 REALM_ASSERT(is_attached());
298 REALM_ASSERT_3(target_row_ndx, <, m_origin_column->get_target_table().size());
299 REALM_ASSERT_3(start, <=, size());
301 if (!m_row_indexes.is_attached())
304 return m_row_indexes.find_first(target_row_ndx, start);
307 inline const ColumnBase& LinkView::get_column_base(size_t index) const
309 return get_target_table().get_column_base(index);
312 inline const Table& LinkView::get_origin_table() const noexcept
314 return *m_origin_table;
317 inline Table& LinkView::get_origin_table() noexcept
319 return *m_origin_table;
322 inline size_t LinkView::get_origin_row_index() const noexcept
324 REALM_ASSERT(is_attached());
325 return m_row_indexes.get_root_array()->get_ndx_in_parent();
328 inline void LinkView::set_origin_row_index(size_t row_ndx) noexcept
330 REALM_ASSERT(is_attached());
331 m_row_indexes.get_root_array()->set_ndx_in_parent(row_ndx);
334 inline const Table& LinkView::get_target_table() const noexcept
336 return m_origin_column->get_target_table();
339 inline Table& LinkView::get_target_table() noexcept
341 return m_origin_column->get_target_table();
344 inline void LinkView::refresh_accessor_tree(size_t new_row_ndx) noexcept
346 set_origin_row_index(new_row_ndx);
347 m_row_indexes.init_from_parent();
350 inline void LinkView::update_from_parent(size_t old_baseline) noexcept
352 if (m_row_indexes.is_attached())
353 m_row_indexes.update_from_parent(old_baseline);
356 inline Replication* LinkView::get_repl() noexcept
358 typedef _impl::TableFriend tf;
359 return tf::get_repl(*m_origin_table);
363 // The purpose of this class is to give internal access to some, but not all of
364 // the non-public parts of LinkView.
365 class _impl::LinkListFriend {
367 static void do_set(LinkView& list, size_t link_ndx, size_t target_row_ndx)
369 list.do_set(link_ndx, target_row_ndx);
372 static void do_remove(LinkView& list, size_t link_ndx)
374 list.do_remove(link_ndx);
377 static void do_clear(LinkView& list)
379 bool broken_reciprocal_backlinks = false;
380 list.do_clear(broken_reciprocal_backlinks);
383 static void do_insert(LinkView& list, size_t link_ndx, size_t target_row_ndx)
385 list.do_insert(link_ndx, target_row_ndx);
388 static const LinkListColumn& get_origin_column(const LinkView& list)
390 REALM_ASSERT(list.is_attached());
391 return *list.m_origin_column;
397 #endif // REALM_LINK_VIEW_HPP