collection.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. #ifndef REALM_COLLECTION_HPP
  2. #define REALM_COLLECTION_HPP
  3. #include <realm/obj.hpp>
  4. #include <realm/bplustree.hpp>
  5. #include <realm/obj_list.hpp>
  6. #include <realm/table.hpp>
  7. #include <iosfwd> // std::ostream
  8. #include <type_traits> // std::void_t
  9. namespace realm {
  10. template <class L>
  11. struct CollectionIterator;
  12. class CollectionBase {
  13. public:
  14. virtual ~CollectionBase() {}
  15. /// The size of the collection.
  16. virtual size_t size() const = 0;
  17. /// True if the element at @a ndx is NULL.
  18. virtual bool is_null(size_t ndx) const = 0;
  19. /// Get element at @a ndx as a `Mixed`.
  20. virtual Mixed get_any(size_t ndx) const = 0;
  21. /// Clear the collection.
  22. virtual void clear() = 0;
  23. /// Get the min element, according to whatever comparison function is
  24. /// meaningful for the collection.
  25. virtual Mixed min(size_t* return_ndx = nullptr) const = 0;
  26. /// Get the max element, according to whatever comparison function is
  27. /// meaningful for the collection.
  28. virtual Mixed max(size_t* return_ndx = nullptr) const = 0;
  29. /// For collections of arithmetic types, return the sum of all elements.
  30. virtual Mixed sum(size_t* return_cnt = nullptr) const = 0;
  31. /// For collections of arithmetic types, return the average of all elements.
  32. virtual Mixed avg(size_t* return_cnt = nullptr) const = 0;
  33. /// Produce a clone of the collection accessor referring to the same
  34. /// underlying memory.
  35. virtual std::unique_ptr<CollectionBase> clone_collection() const = 0;
  36. /// Modifies a vector of indices so that they refer to values sorted
  37. /// according to the specified sort order.
  38. virtual void sort(std::vector<size_t>& indices, bool ascending = true) const = 0;
  39. /// Modifies a vector of indices so that they refer to distinct values. If
  40. /// @a sort_order is supplied, the indices will refer to values in sort
  41. /// order, otherwise the indices will be in the same order as they appear in
  42. /// the collection.
  43. virtual void distinct(std::vector<size_t>& indices, util::Optional<bool> sort_order = util::none) const = 0;
  44. // Return index of the first occurrence of 'value'
  45. virtual size_t find_any(Mixed value) const = 0;
  46. /// True if `size()` returns 0.
  47. virtual bool is_empty() const final
  48. {
  49. return size() == 0;
  50. }
  51. /// Get the object that owns this collection.
  52. virtual const Obj& get_obj() const noexcept = 0;
  53. /// Get the column key for this collection.
  54. virtual ColKey get_col_key() const noexcept = 0;
  55. /// Return true if the collection has changed since the last call to
  56. /// `has_changed()`. Note that this function is not idempotent and updates
  57. /// the internal state of the accessor if it has changed.
  58. virtual bool has_changed() const = 0;
  59. /// Returns true if the accessor is in the attached state. By default, this
  60. /// checks if the owning object is still valid.
  61. virtual bool is_attached() const
  62. {
  63. return get_obj().is_valid();
  64. }
  65. // Note: virtual..final prevents static override.
  66. /// Get the key of the object that owns this collection.
  67. virtual ObjKey get_key() const noexcept final
  68. {
  69. return get_obj().get_key();
  70. }
  71. /// Get the table of the object that owns this collection.
  72. virtual ConstTableRef get_table() const noexcept final
  73. {
  74. return get_obj().get_table();
  75. }
  76. /// If this is a collection of links, get the target table.
  77. virtual TableRef get_target_table() const final
  78. {
  79. return get_obj().get_target_table(get_col_key());
  80. }
  81. protected:
  82. friend class Transaction;
  83. CollectionBase() noexcept = default;
  84. CollectionBase(const CollectionBase&) noexcept = default;
  85. CollectionBase(CollectionBase&&) noexcept = default;
  86. CollectionBase& operator=(const CollectionBase&) noexcept = default;
  87. CollectionBase& operator=(CollectionBase&&) noexcept = default;
  88. /// Unconditionally (re)initialize this accessor from its parent (the owner
  89. /// object). May leave the collection detached if the object is no longer
  90. /// valid. Return true if the accessor is attached.
  91. virtual bool init_from_parent() const = 0;
  92. /// If the underlying memory has changed, update this accessor to reflect
  93. /// the new state. Returns true if the accessor was actually updated.
  94. virtual bool update_if_needed() const = 0;
  95. };
  96. template <class T>
  97. inline void check_column_type(ColKey col)
  98. {
  99. if (col && col.get_type() != ColumnTypeTraits<T>::column_id) {
  100. throw LogicError(LogicError::collection_type_mismatch);
  101. }
  102. }
  103. template <>
  104. inline void check_column_type<Int>(ColKey col)
  105. {
  106. if (col && (col.get_type() != col_type_Int || col.get_attrs().test(col_attr_Nullable))) {
  107. throw LogicError(LogicError::collection_type_mismatch);
  108. }
  109. }
  110. template <>
  111. inline void check_column_type<util::Optional<Int>>(ColKey col)
  112. {
  113. if (col && (col.get_type() != col_type_Int || !col.get_attrs().test(col_attr_Nullable))) {
  114. throw LogicError(LogicError::collection_type_mismatch);
  115. }
  116. }
  117. template <>
  118. inline void check_column_type<ObjKey>(ColKey col)
  119. {
  120. if (col) {
  121. bool is_link_list = (col.get_type() == col_type_LinkList);
  122. bool is_link_set = (col.is_set() && col.get_type() == col_type_Link);
  123. if (!(is_link_list || is_link_set))
  124. throw LogicError(LogicError::collection_type_mismatch);
  125. }
  126. }
  127. template <class T, class = void>
  128. struct MinHelper {
  129. template <class U>
  130. static Mixed eval(U&, size_t*) noexcept
  131. {
  132. return Mixed{};
  133. }
  134. };
  135. template <class T>
  136. struct MinHelper<T, std::void_t<ColumnMinMaxType<T>>> {
  137. template <class U>
  138. static Mixed eval(U& tree, size_t* return_ndx)
  139. {
  140. return Mixed(bptree_minimum<T>(tree, return_ndx));
  141. }
  142. };
  143. template <class T, class Enable = void>
  144. struct MaxHelper {
  145. template <class U>
  146. static Mixed eval(U&, size_t*) noexcept
  147. {
  148. return Mixed{};
  149. }
  150. };
  151. template <class T>
  152. struct MaxHelper<T, std::void_t<ColumnMinMaxType<T>>> {
  153. template <class U>
  154. static Mixed eval(U& tree, size_t* return_ndx)
  155. {
  156. return Mixed(bptree_maximum<T>(tree, return_ndx));
  157. }
  158. };
  159. template <class T, class Enable = void>
  160. class SumHelper {
  161. public:
  162. template <class U>
  163. static Mixed eval(U&, size_t* return_cnt) noexcept
  164. {
  165. if (return_cnt)
  166. *return_cnt = 0;
  167. return Mixed{};
  168. }
  169. };
  170. template <class T>
  171. class SumHelper<T, std::void_t<ColumnSumType<T>>> {
  172. public:
  173. template <class U>
  174. static Mixed eval(U& tree, size_t* return_cnt)
  175. {
  176. return Mixed(bptree_sum<T>(tree, return_cnt));
  177. }
  178. };
  179. template <class T, class = void>
  180. struct AverageHelper {
  181. template <class U>
  182. static Mixed eval(U&, size_t* return_cnt) noexcept
  183. {
  184. if (return_cnt)
  185. *return_cnt = 0;
  186. return Mixed{};
  187. }
  188. };
  189. template <class T>
  190. struct AverageHelper<T, std::void_t<ColumnSumType<T>>> {
  191. template <class U>
  192. static Mixed eval(U& tree, size_t* return_cnt)
  193. {
  194. return Mixed(bptree_average<T>(tree, return_cnt));
  195. }
  196. };
  197. /// Convenience base class for collections, which implements most of the
  198. /// relevant interfaces for a collection that is bound to an object accessor and
  199. /// representable as a BPlusTree<T>.
  200. template <class Interface>
  201. class CollectionBaseImpl : public Interface, protected ArrayParent {
  202. public:
  203. static_assert(std::is_base_of_v<CollectionBase, Interface>);
  204. // Overriding members of CollectionBase:
  205. ColKey get_col_key() const noexcept final
  206. {
  207. return m_col_key;
  208. }
  209. const Obj& get_obj() const noexcept final
  210. {
  211. return m_obj;
  212. }
  213. bool has_changed() const final
  214. {
  215. update_if_needed();
  216. if (m_last_content_version != m_content_version) {
  217. m_last_content_version = m_content_version;
  218. return true;
  219. }
  220. return false;
  221. }
  222. using Interface::get_key;
  223. using Interface::get_target_table;
  224. protected:
  225. Obj m_obj;
  226. ColKey m_col_key;
  227. bool m_nullable = false;
  228. mutable uint_fast64_t m_content_version = 0;
  229. mutable uint_fast64_t m_last_content_version = 0;
  230. mutable bool m_valid = false;
  231. CollectionBaseImpl() = default;
  232. CollectionBaseImpl(const CollectionBaseImpl& other) = default;
  233. CollectionBaseImpl(CollectionBaseImpl&& other) = default;
  234. CollectionBaseImpl(const Obj& obj, ColKey col_key) noexcept
  235. : m_obj(obj)
  236. , m_col_key(col_key)
  237. , m_nullable(col_key.is_nullable())
  238. {
  239. }
  240. CollectionBaseImpl& operator=(const CollectionBaseImpl& other) = default;
  241. CollectionBaseImpl& operator=(CollectionBaseImpl&& other) = default;
  242. bool operator==(const CollectionBaseImpl& other) const noexcept
  243. {
  244. return get_key() == other.get_key() && get_col_key() == other.get_col_key();
  245. }
  246. bool operator!=(const CollectionBaseImpl& other) const noexcept
  247. {
  248. return !(*this == other);
  249. }
  250. // Overriding members of CollectionBase:
  251. REALM_NOINLINE bool update_if_needed() const final
  252. {
  253. if (!m_obj.is_valid())
  254. return false;
  255. auto content_version = m_obj.get_alloc().get_content_version();
  256. if (m_obj.update_if_needed() || content_version != m_content_version) {
  257. this->init_from_parent();
  258. return true;
  259. }
  260. return false;
  261. }
  262. void update_content_version() const noexcept
  263. {
  264. m_content_version = m_obj.get_alloc().get_content_version();
  265. }
  266. void bump_content_version()
  267. {
  268. m_content_version = m_obj.bump_content_version();
  269. }
  270. void ensure_writeable()
  271. {
  272. if (m_obj.ensure_writeable()) {
  273. this->init_from_parent();
  274. }
  275. }
  276. protected:
  277. // Overriding ArrayParent interface:
  278. ref_type get_child_ref(size_t child_ndx) const noexcept final
  279. {
  280. static_cast<void>(child_ndx);
  281. try {
  282. return to_ref(m_obj._get<int64_t>(m_col_key.get_index()));
  283. }
  284. catch (const KeyNotFound&) {
  285. return ref_type(0);
  286. }
  287. }
  288. void update_child_ref(size_t child_ndx, ref_type new_ref) final
  289. {
  290. static_cast<void>(child_ndx);
  291. m_obj.set_int(m_col_key, from_ref(new_ref));
  292. }
  293. };
  294. namespace _impl {
  295. /// Translate from condensed index to uncondensed index in collections that hide
  296. /// tombstones.
  297. size_t virtual2real(const std::vector<size_t>& vec, size_t ndx) noexcept;
  298. /// Translate from uncondensed index to condensed into in collections that hide
  299. /// tombstones.
  300. size_t real2virtual(const std::vector<size_t>& vec, size_t ndx) noexcept;
  301. /// Rebuild the list of unresolved keys for tombstone handling.
  302. void update_unresolved(std::vector<size_t>& vec, const BPlusTree<ObjKey>& tree);
  303. /// Clear the context flag on the tree if there are no more unresolved links.
  304. void check_for_last_unresolved(BPlusTree<ObjKey>& tree);
  305. /// Proxy class needed because the ObjList interface clobbers method names from
  306. /// CollectionBase.
  307. struct ObjListProxy : ObjList {
  308. virtual TableRef proxy_get_target_table() const = 0;
  309. TableRef get_target_table() const final
  310. {
  311. return proxy_get_target_table();
  312. }
  313. };
  314. } // namespace _impl
  315. /// Base class for collections of objects, where unresolved links (tombstones)
  316. /// can occur.
  317. template <class Interface>
  318. class ObjCollectionBase : public Interface, public _impl::ObjListProxy {
  319. public:
  320. static_assert(std::is_base_of_v<CollectionBase, Interface>);
  321. using Interface::get_table;
  322. using Interface::is_attached;
  323. using Interface::size;
  324. // Overriding methods in ObjList:
  325. void get_dependencies(TableVersions& versions) const final
  326. {
  327. if (is_attached()) {
  328. auto table = this->get_table();
  329. versions.emplace_back(table->get_key(), table->get_content_version());
  330. }
  331. }
  332. void sync_if_needed() const final
  333. {
  334. if (is_attached()) {
  335. update_if_needed();
  336. }
  337. }
  338. bool is_in_sync() const noexcept final
  339. {
  340. return true;
  341. }
  342. bool has_unresolved() const noexcept
  343. {
  344. return m_unresolved.size() != 0;
  345. }
  346. using Interface::get_target_table;
  347. protected:
  348. ObjCollectionBase() = default;
  349. ObjCollectionBase(const ObjCollectionBase&) = default;
  350. ObjCollectionBase(ObjCollectionBase&&) = default;
  351. ObjCollectionBase& operator=(const ObjCollectionBase&) = default;
  352. ObjCollectionBase& operator=(ObjCollectionBase&&) = default;
  353. /// Implementations should call `update_if_needed()` on their inner accessor
  354. /// (without `update_unresolved()`).
  355. virtual bool do_update_if_needed() const = 0;
  356. /// Implementations should call `init_from_parent()` on their inner accessor
  357. /// (without `update_unresolved()`).
  358. virtual bool do_init_from_parent() const = 0;
  359. /// Implementations should return a non-const reference to their internal
  360. /// `BPlusTree<T>`.
  361. virtual BPlusTree<ObjKey>& get_mutable_tree() const = 0;
  362. /// Calls `do_init_from_parent()` and updates the list of unresolved links.
  363. bool init_from_parent() const final
  364. {
  365. clear_unresolved();
  366. bool valid = do_init_from_parent();
  367. if (valid) {
  368. update_unresolved();
  369. }
  370. return valid;
  371. }
  372. /// Implements update_if_needed() in a way that ensures the consistency of
  373. /// the unresolved list. Derived classes should call this instead of calling
  374. /// `update_if_needed()` on their inner accessor.
  375. bool update_if_needed() const final
  376. {
  377. bool updated = do_update_if_needed();
  378. update_unresolved();
  379. return updated;
  380. }
  381. /// Translate from condensed index to uncondensed.
  382. size_t virtual2real(size_t ndx) const noexcept
  383. {
  384. return _impl::virtual2real(m_unresolved, ndx);
  385. }
  386. /// Translate from uncondensed index to condensed.
  387. size_t real2virtual(size_t ndx) const noexcept
  388. {
  389. return _impl::real2virtual(m_unresolved, ndx);
  390. }
  391. /// Rebuild the list of tombstones if there is a chance that it has changed.
  392. void update_unresolved() const
  393. {
  394. const auto& obj = this->get_obj();
  395. if (obj.is_valid()) {
  396. auto content_version = this->get_obj().get_alloc().get_content_version();
  397. if (content_version != m_content_version) {
  398. _impl::update_unresolved(m_unresolved, get_mutable_tree());
  399. m_content_version = content_version;
  400. }
  401. }
  402. else {
  403. clear_unresolved();
  404. }
  405. }
  406. void check_for_last_unresolved()
  407. {
  408. _impl::check_for_last_unresolved(get_mutable_tree());
  409. }
  410. /// Clear the list of tombstones. It will be rebuilt the next time
  411. /// `update_if_needed()` is called.
  412. void clear_unresolved() const noexcept
  413. {
  414. m_unresolved.clear();
  415. m_content_version = uint_fast64_t(-1);
  416. }
  417. /// Return the number of tombstones.
  418. size_t num_unresolved() const noexcept
  419. {
  420. return m_unresolved.size();
  421. }
  422. private:
  423. // Sorted set of indices containing unresolved links.
  424. mutable std::vector<size_t> m_unresolved;
  425. // We need to track the content version separately to keep the list of
  426. // unresolved indices up to date, and can't rely on the return value of
  427. // `do_update_if_needed()`, because the inner accessor may have been
  428. // refreshed without our knowledge.
  429. mutable uint_fast64_t m_content_version = uint_fast64_t(-1);
  430. TableRef proxy_get_target_table() const final
  431. {
  432. return Interface::get_target_table();
  433. }
  434. };
  435. /// Random-access iterator over elements of a collection.
  436. ///
  437. /// Values are cached into a member variable in order to support `operator->`
  438. /// and `operator*` returning a pointer and a reference, respectively.
  439. template <class L>
  440. struct CollectionIterator {
  441. using iterator_category = std::random_access_iterator_tag;
  442. using value_type = typename L::value_type;
  443. using difference_type = ptrdiff_t;
  444. using pointer = const value_type*;
  445. using reference = const value_type&;
  446. CollectionIterator(const L* l, size_t ndx) noexcept
  447. : m_list(l)
  448. , m_ndx(ndx)
  449. {
  450. }
  451. pointer operator->() const
  452. {
  453. m_val = m_list->get(m_ndx);
  454. return &m_val;
  455. }
  456. reference operator*() const
  457. {
  458. return *operator->();
  459. }
  460. CollectionIterator& operator++() noexcept
  461. {
  462. ++m_ndx;
  463. return *this;
  464. }
  465. CollectionIterator operator++(int) noexcept
  466. {
  467. auto tmp = *this;
  468. operator++();
  469. return tmp;
  470. }
  471. CollectionIterator& operator--() noexcept
  472. {
  473. --m_ndx;
  474. return *this;
  475. }
  476. CollectionIterator operator--(int) noexcept
  477. {
  478. auto tmp = *this;
  479. operator--();
  480. return tmp;
  481. }
  482. CollectionIterator& operator+=(ptrdiff_t n) noexcept
  483. {
  484. m_ndx += n;
  485. return *this;
  486. }
  487. CollectionIterator& operator-=(ptrdiff_t n) noexcept
  488. {
  489. m_ndx -= n;
  490. return *this;
  491. }
  492. friend ptrdiff_t operator-(const CollectionIterator& lhs, const CollectionIterator& rhs) noexcept
  493. {
  494. return ptrdiff_t(lhs.m_ndx) - ptrdiff_t(rhs.m_ndx);
  495. }
  496. friend CollectionIterator operator+(CollectionIterator lhs, ptrdiff_t rhs) noexcept
  497. {
  498. lhs.m_ndx += rhs;
  499. return lhs;
  500. }
  501. friend CollectionIterator operator+(ptrdiff_t lhs, CollectionIterator rhs) noexcept
  502. {
  503. return rhs + lhs;
  504. }
  505. bool operator!=(const CollectionIterator& rhs) const noexcept
  506. {
  507. REALM_ASSERT_DEBUG(m_list == rhs.m_list);
  508. return m_ndx != rhs.m_ndx;
  509. }
  510. bool operator==(const CollectionIterator& rhs) const noexcept
  511. {
  512. REALM_ASSERT_DEBUG(m_list == rhs.m_list);
  513. return m_ndx == rhs.m_ndx;
  514. }
  515. size_t index() const noexcept
  516. {
  517. return m_ndx;
  518. }
  519. private:
  520. mutable value_type m_val;
  521. const L* m_list;
  522. size_t m_ndx = size_t(-1);
  523. };
  524. } // namespace realm
  525. #endif // REALM_COLLECTION_HPP