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_DESCRIPTOR_HPP
20 #define REALM_DESCRIPTOR_HPP
24 #include <realm/util/assert.hpp>
25 #include <realm/descriptor_fwd.hpp>
26 #include <realm/table.hpp>
32 class DescriptorFriend;
36 /// Accessor for table type descriptors.
38 /// A table type descriptor is an entity that specifies the dynamic
39 /// type of a Realm table. Objects of this class are accessors
40 /// through which the descriptor can be inspected and
41 /// changed. Accessors can become detached, see is_attached() for more
42 /// on this. The descriptor itself is stored inside the database file,
43 /// or elsewhere in case of a free-standing table or a table in a
44 /// free-standing group.
46 /// The dynamic type consists first, and foremost of an ordered list
47 /// of column descriptors. Each column descriptor specifies the name
48 /// and type of the column.
50 /// When a table has a subtable column, every cell in than column
51 /// contains a subtable. All those subtables have the same dynamic
52 /// type, and therefore have a shared descriptor. See is_root() for
55 /// The Table class contains convenience methods, such as
56 /// Table::get_column_count() and Table::add_column(), that allow you
57 /// to inspect and change the dynamic type of simple tables without
58 /// resorting to use of descriptors. For example, the following two
59 /// statements have the same effect:
61 /// table->add_column(type, name);
62 /// table->get_descriptor()->add_column(type, name);
64 /// Note, however, that this equivalence holds only as long as no
65 /// shared subtable descriptors are involved.
67 /// \sa Table::get_descriptor()
68 class Descriptor : public std::enable_shared_from_this<Descriptor> {
70 /// Get the number of columns in the associated tables.
71 size_t get_column_count() const noexcept;
73 /// Get the type of the column at the specified index.
75 /// The consequences of specifying a column index that is out of
76 /// range, are undefined.
77 DataType get_column_type(size_t column_ndx) const noexcept;
79 /// Get the name of the column at the specified index.
81 /// The consequences of specifying a column index that is out of
82 /// range, are undefined.
83 StringData get_column_name(size_t column_ndx) const noexcept;
85 /// Search for a column with the specified name.
87 /// This function finds the first column with the specified name,
88 /// and returns its index. If there are no such columns, it
89 /// returns `not_found`.
90 size_t get_column_index(StringData name) const noexcept;
92 /// Get the index of the table to which links in the column at the specified
95 /// The consequences of specifying a column index that is out of
96 /// range, are undefined.
98 /// The consequences of specifying a column index that does not refer
99 /// to a link column, are undefined.
100 size_t get_column_link_target(size_t column_ndx) const noexcept;
102 /// Get whether or not the specified column is nullable.
104 /// The consequences of specifying a column index that is out of
105 /// range, are undefined.
106 bool is_nullable(size_t column_ndx) const noexcept;
108 /// \defgroup descriptor_column_accessors Accessing Columns Via A Descriptor
110 /// add_column() and add_column_link() are a shorthands for calling
111 /// insert_column() and insert_column_link(), respectively, with a column
112 /// index equal to the original number of columns. The returned value is
113 /// that column index.
115 /// insert_column() inserts a new column into all the tables associated with
116 /// this descriptor. If any of the tables are not empty, the new column will
117 /// be filled with the default value associated with the specified data
118 /// type. This function cannot be used to insert link-type columns. For
119 /// that, you have to use insert_column_link() instead.
121 /// This function modifies the dynamic type of all the tables that
122 /// share this descriptor. It does this by inserting a new column
123 /// with the specified name and type into the descriptor at the
124 /// specified index, and into each of the tables that share this
127 /// insert_column_link() is like insert_column(), but inserts a link-type
128 /// column to a group-level table. It is not possible to add link-type
129 /// columns to tables that are not group-level tables. This functions must
130 /// be used in place of insert_column() when the column type is `type_Link`
131 /// or `type_LinkList`. A link-type column is associated with a particular
132 /// target table. All links in a link-type column refer to rows in the
133 /// target table of that column. The target table must also be a group-level
134 /// table, and it must belong to the same group as the origin table.
136 /// \param name Name of new column. All strings are valid column names as
137 /// long as they are valid UTF-8 encodings and the number of bytes does not
138 /// exceed `max_column_name_length`. An attempt to add a column with a name
139 /// that is longer than `max_column_name_length` will cause an exception to
142 /// \param subdesc If a non-null pointer is passed, and the
143 /// specified type is `type_Table`, then this function
144 /// automatically retrieves the descriptor associated with the new
145 /// subtable column, and stores a reference to its accessor in
148 /// \param col_ndx Insert the new column at this index. Preexisting columns
149 /// at indexes equal to, or greater than `col_ndx` will be shifted to the
150 /// next higher index. It is an error to specify an index that is greater
151 /// than the number of columns prior to the insertion.
153 /// \param link_type See set_link_type().
155 /// \sa Table::add_column()
156 /// \sa Table::insert_column()
157 /// \sa Table::add_column_link()
158 /// \sa Table::insert_column_link()
162 static const size_t max_column_name_length = 63;
164 size_t add_column(DataType type, StringData name, DescriptorRef* subdesc = nullptr, bool nullable = false);
166 void insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc = nullptr,
167 bool nullable = false);
169 size_t add_column_link(DataType type, StringData name, Table& target, LinkType = link_Weak);
170 void insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target, LinkType = link_Weak);
173 /// Remove the specified column from each of the associated
174 /// tables. If the removed column is the only column in the
175 /// descriptor, then the table size will drop to zero for all
176 /// tables that were not already empty.
178 /// This function modifies the dynamic type of all the tables that
179 /// share this descriptor. It does this by removing the column at
180 /// the specified index from the descriptor, and from each of the
181 /// tables that share this descriptor. The consequences of
182 /// specifying a column index that is out of range, are undefined.
184 /// If the removed column was a subtable column, then the
185 /// associated descriptor accessor will be detached, if it
186 /// exists. This function will also detach all accessors of
187 /// subtables of the root table. Only the accessor of the root
188 /// table will remain attached. The root table is the table
189 /// associated with the root descriptor.
191 /// \param col_ndx The index of the column to be removed. It is an error to
192 /// specify an index that is greater than, or equal to the number of
196 /// \sa Table::remove_column()
197 void remove_column(size_t col_ndx);
199 /// Rename the specified column.
201 /// This function modifies the dynamic type of all the tables that
202 /// share this descriptor. The consequences of specifying a column
203 /// index that is out of range, are undefined.
205 /// This function will detach all accessors of subtables of the
206 /// root table. Only the accessor of the root table will remain
207 /// attached. The root table is the table associated with the root
210 /// \param col_ndx The index of the column to be renamed. It is an error to
211 /// specify an index that is greater than, or equal to the number of
214 /// \param new_name The new name of the column.
217 /// \sa Table::rename_column()
218 void rename_column(size_t col_ndx, StringData new_name);
220 /// If the descriptor is describing a subtable column, the add_search_index()
221 /// and remove_search_index() will add or remove search indexes of *all*
222 /// subtables of the subtable column. This may take a while if there are many
223 /// subtables with many rows each.
224 bool has_search_index(size_t column_ndx) const noexcept;
225 void add_search_index(size_t column_ndx);
226 void remove_search_index(size_t column_ndx);
228 /// There are two kinds of links, 'weak' and 'strong'. A strong link is one
229 /// that implies ownership, i.e., that the origin row (parent) owns the
230 /// target row (child). Simply stated, this means that when the origin row
231 /// (parent) is removed, so is the target row (child). If there are multiple
232 /// strong links to a target row, the origin rows share ownership, and the
233 /// target row is removed when the last owner disappears. Weak links do not
234 /// imply ownership, and will be nullified or removed when the target row
237 /// To put this in precise terms; when a strong link is broken, and the
238 /// target row has no other strong links to it, the target row is removed. A
239 /// row that is implicitly removed in this way, is said to be
240 /// *cascade-removed*. When a weak link is broken, nothing is
243 /// A link is considered broken if
245 /// - the link is nullified, removed, or replaced by a different link
246 /// (Row::nullify_link(), Row::set_link(), LinkView::remove_link(),
247 /// LinkView::set_link(), LinkView::clear()), or if
249 /// - the origin row is explicitly removed (Row::move_last_over(),
250 /// Table::clear()), or if
252 /// - the origin row is cascade-removed, or if
254 /// - the origin column is removed from the table (Table::remove_column()),
257 /// - the origin table is removed from the group.
259 /// Note that a link is *not* considered broken when it is replaced by a
260 /// link to the same target row. I.e., no no rows will be cascade-removed
261 /// due to such an operation.
263 /// When a row is explicitly removed (such as by Table::move_last_over()),
264 /// all links to it are automatically removed or nullified. For single link
265 /// columns (type_Link), links to the removed row are nullified. For link
266 /// list columns (type_LinkList), links to the removed row are removed from
269 /// When a row is cascade-removed there can no longer be any strong links to
270 /// it, but if there are any weak links, they will be removed or nullified.
272 /// It is important to understand that this cascade-removal scheme is too
273 /// simplistic to enable detection and removal of orphaned link-cycles. In
274 /// this respect, it suffers from the same limitations as a reference
275 /// counting scheme generally does.
277 /// It is also important to understand, that the possible presence of a link
278 /// cycle can cause a row to be cascade-removed as a consequence of being
279 /// modified. This happens, for example, if two rows, A and B, have strong
280 /// links to each other, and there are no other strong links to either of
281 /// them. In this case, if A->B is changed to A->C, then both A and B will
282 /// be cascade-removed. This can lead to obscure bugs in some applications,
283 /// such as in the following case:
285 /// table.set_link(col_ndx_1, row_ndx, ...);
286 /// table.set_int(col_ndx_2, row_ndx, ...); // Oops, `row_ndx` may no longer refer to the same row
288 /// To be safe, applications, that may encounter cycles, are advised to
289 /// adopt the following pattern:
291 /// Row row = table[row_ndx];
292 /// row.set_link(col_ndx_1, ...);
294 /// row.set_int(col_ndx_2, ...); // Ok, because we check whether the row has disappeared
296 /// \param col_ndx The index of the link column (`type_Link` or
297 /// `type_LinkList`) to be modified. It is an error to specify an index that
298 /// is greater than, or equal to the number of columns, or to specify the
299 /// index of a non-link column.
301 /// \param link_type The type of links the column should store.
302 void set_link_type(size_t col_ndx, LinkType link_type);
305 /// Get the descriptor for the specified subtable column.
307 /// This function provides access to the shared subtable
308 /// descriptor for the subtables in the specified column. The
309 /// specified column must be a column whose type is 'table'. The
310 /// consequences of specifying a column of a different type, or
311 /// specifying an index that is out of range, are undefined.
313 /// Note that this function cannot be used with 'mixed' columns,
314 /// since subtables of that kind have independent dynamic types,
315 /// and therefore, have independent descriptors. You can only get
316 /// access to the descriptor of a subtable in a mixed column by
317 /// first getting access to the subtable itself.
320 DescriptorRef get_subdescriptor(size_t column_ndx);
321 ConstDescriptorRef get_subdescriptor(size_t column_ndx) const;
325 /// Returns the parent table descriptor, if any.
327 /// If this descriptor is the *root descriptor*, then this
328 /// function returns null. Otherwise it returns the accessor of
329 /// the parent descriptor.
332 DescriptorRef get_parent() noexcept;
333 ConstDescriptorRef get_parent() const noexcept;
337 /// Get the table associated with the root descriptor.
341 TableRef get_root_table() noexcept;
342 ConstTableRef get_root_table() const noexcept;
346 /// Get the target table associated with the specified link column. This
347 /// descriptor must be a root descriptor (is_root()), and the specified
348 /// column must be a link column (`type_Link` or `type_LinkList`).
349 TableRef get_link_target(size_t col_ndx) noexcept;
350 ConstTableRef get_link_target(size_t col_ndx) const noexcept;
353 /// Is this a root descriptor?
355 /// Descriptors of tables with independent dynamic type are root
356 /// descriptors. Root descriptors are never shared. Tables that
357 /// are direct members of groups have independent dynamic
358 /// types. The same is true for free-standing tables and subtables
359 /// in columns of type 'mixed'.
361 /// When a table has a column of type 'table', the cells in that
362 /// column contain subtables. All those subtables have the same
363 /// dynamic type, and they share a single dynamic type
364 /// descriptor. Such shared descriptors are never root
367 /// A type descriptor can even be shared by subtables with
368 /// different parent tables, but only if the parent tables
369 /// themselves have a shared type descriptor. For example, if a
370 /// table has a column `foo` of type 'table', and each of the
371 /// subtables in `foo` has a column `bar` of type 'table', then
372 /// all the subtables in all the `bar` columns share the same
373 /// dynamic type descriptor.
375 /// \sa Table::has_shared_type()
376 bool is_root() const noexcept;
378 /// Determine whether this accessor is still attached.
380 /// A table descriptor accessor may get detached from the
381 /// underlying descriptor for various reasons (see below). When it
382 /// does, it no longer refers to that descriptor, and can no
383 /// longer be used, except for calling is_attached(). The
384 /// consequences of calling other methods on a detached accessor
385 /// are undefined. Descriptor accessors obtained by calling
386 /// functions in the Realm API are always in the 'attached'
387 /// state immediately upon return from those functions.
389 /// A descriptor accessor that is obtained directly from a table
390 /// becomes detached if the table becomes detached. A shared
391 /// subtable descriptor accessor that is obtained by a call to
392 /// get_subdescriptor() becomes detached if the parent descriptor
393 /// accessor becomes detached, or if the corresponding subtable
394 /// column is removed. A descriptor accessor does not get detached
395 /// under any other circumstances.
396 bool is_attached() const noexcept;
399 /// \brief Compare two table descriptors.
401 /// Two table descriptors are equal if they have the same number of columns,
402 /// and for each column index, the two columns have the same name, data
403 /// type, and set of attributes.
405 /// For link columns (`type_Link` and `type_LinkList`), the target table
406 /// (get_link_target()) of the two columns must be the same.
408 /// For subtable columns (`type_Table`), the two corresponding
409 /// subdescriptors must themselves be equal, as if by a recursive call to
412 /// The consequences of comparing a detached descriptor are
414 bool operator==(const Descriptor&) const noexcept;
415 bool operator!=(const Descriptor&) const noexcept;
418 /// If the specified column is optimized to store only unique values, then
419 /// this function returns the number of unique values currently
420 /// stored. Otherwise it returns zero. This function is mainly intended for
421 /// debugging purposes.
422 size_t get_num_unique_values(size_t column_ndx) const;
424 ~Descriptor() noexcept;
427 // for initialization through make_shared
432 Descriptor(const PrivateTag&)
438 // Table associated with root descriptor. Detached iff null.
439 TableRef m_root_table;
440 DescriptorRef m_parent; // Null iff detached or root descriptor.
441 Spec* m_spec; // Valid if attached. Owned iff valid and `m_parent`.
443 // Whenever a subtable descriptor accessor is created, it is
444 // stored in this map. This ensures that when get_subdescriptor()
445 // is called to created multiple DescriptorRef objects that
446 // overlap in time, then they will all refer to the same
447 // descriptor object.
449 // It also enables the necessary recursive detaching of descriptor
451 struct subdesc_entry {
453 std::weak_ptr<Descriptor> m_subdesc;
454 subdesc_entry(size_t column_ndx, DescriptorRef);
456 typedef std::vector<subdesc_entry> subdesc_map;
457 mutable subdesc_map m_subdesc_map;
459 Descriptor() noexcept;
461 // Called by the root table if this becomes the root
462 // descriptor. Otherwise it is called by the descriptor that
463 // becomes its parent.
465 // Puts this descriptor accessor into the attached state. This
466 // attaches it to the underlying structure of array nodes. It does
467 // not establish the parents reference to this descriptor, that is
468 // the job of the parent. When this function returns,
469 // is_attached() will return true.
473 // The specified table is not allowed to be a subtable with a
474 // shareable spec. That is, Table::has_shared_spec() must return
477 // The specified spec must be the spec of the specified table or
478 // of one of its direct or indirect subtable columns.
480 // When the specified spec is the spec of the root table, the
481 // parent must be specified as null. When the specified spec is
482 // not the root spec, a proper parent must be specified.
483 void attach(Table*, DescriptorRef parent, Spec*) noexcept;
485 // Detach accessor from underlying descriptor. Caller must ensure
486 // that a reference count exists upon return, for example by
487 // obtaining an extra reference count before the call.
489 // This function is called either by the root table if this is the
490 // root descriptor, or by the parent descriptor, if it is not.
492 // Puts this descriptor accessor into the detached state. This
493 // detaches it from the underlying structure of array nodes. It
494 // also calls detach_subdesc_accessors(). When this function
495 // returns, is_attached() will return false.
498 void detach() noexcept;
500 // Recursively detach all subtable descriptor accessors that
501 // exist, that is, all subtable descriptor accessors that have
502 // this descriptor as ancestor.
503 void detach_subdesc_accessors() noexcept;
505 // Record the path in terms of subtable column indexes from the
506 // root descriptor to this descriptor. If this descriptor is a
507 // root descriptor, the path is empty. Returns zero if the path is
508 // too long to fit in the specified buffer. Otherwise the path
509 // indexes will be stored between `begin_2`and `end`, where
510 // `begin_2` is the returned pointer.
511 size_t* record_subdesc_path(size_t* begin, size_t* end) const noexcept;
513 // Returns a pointer to the accessor of the specified
514 // subdescriptor if that accessor exists, otherwise this function
516 DescriptorRef get_subdesc_accessor(size_t column_ndx) noexcept;
518 void adj_insert_column(size_t col_ndx) noexcept;
519 void adj_erase_column(size_t col_ndx) noexcept;
521 friend class util::bind_ptr<Descriptor>;
522 friend class util::bind_ptr<const Descriptor>;
523 friend class _impl::DescriptorFriend;
529 inline size_t Descriptor::get_column_count() const noexcept
531 REALM_ASSERT(is_attached());
532 return m_spec->get_public_column_count();
535 inline StringData Descriptor::get_column_name(size_t ndx) const noexcept
537 REALM_ASSERT(is_attached());
538 return m_spec->get_column_name(ndx);
541 inline DataType Descriptor::get_column_type(size_t ndx) const noexcept
543 REALM_ASSERT(is_attached());
544 return m_spec->get_public_column_type(ndx);
547 inline bool Descriptor::is_nullable(size_t ndx) const noexcept
549 REALM_ASSERT(is_attached());
550 return m_spec->get_column_attr(ndx) & col_attr_Nullable;
553 inline size_t Descriptor::get_column_index(StringData name) const noexcept
555 REALM_ASSERT(is_attached());
556 return m_spec->get_column_index(name);
559 inline size_t Descriptor::get_column_link_target(size_t column_ndx) const noexcept
561 REALM_ASSERT(is_attached());
562 return m_spec->get_opposite_link_table_ndx(column_ndx);
565 inline size_t Descriptor::add_column(DataType type, StringData name, DescriptorRef* subdesc, bool nullable)
567 size_t col_ndx = m_spec->get_public_column_count();
568 insert_column(col_ndx, type, name, subdesc, nullable); // Throws
572 inline void Descriptor::insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc,
575 typedef _impl::TableFriend tf;
577 if (REALM_UNLIKELY(!is_attached()))
578 throw LogicError(LogicError::detached_accessor);
579 if (REALM_UNLIKELY(col_ndx > get_column_count()))
580 throw LogicError(LogicError::column_index_out_of_range);
581 if (REALM_UNLIKELY(tf::is_link_type(ColumnType(type))))
582 throw LogicError(LogicError::illegal_type);
584 LinkTargetInfo invalid_link;
585 tf::insert_column(*this, col_ndx, type, name, invalid_link, nullable); // Throws
586 adj_insert_column(col_ndx);
587 if (subdesc && type == type_Table)
588 *subdesc = get_subdescriptor(col_ndx);
591 inline size_t Descriptor::add_column_link(DataType type, StringData name, Table& target, LinkType link_type)
593 size_t col_ndx = m_spec->get_public_column_count();
594 insert_column_link(col_ndx, type, name, target, link_type); // Throws
598 inline void Descriptor::insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target,
601 typedef _impl::TableFriend tf;
603 if (REALM_UNLIKELY(!is_attached() || !target.is_attached()))
604 throw LogicError(LogicError::detached_accessor);
605 if (REALM_UNLIKELY(col_ndx > get_column_count()))
606 throw LogicError(LogicError::column_index_out_of_range);
607 if (REALM_UNLIKELY(!tf::is_link_type(ColumnType(type))))
608 throw LogicError(LogicError::illegal_type);
609 if (REALM_UNLIKELY(!is_root()))
610 throw LogicError(LogicError::wrong_kind_of_descriptor);
611 // Both origin and target must be group-level tables, and in the same group.
612 Group* origin_group = tf::get_parent_group(*get_root_table());
613 Group* target_group = tf::get_parent_group(target);
614 if (!origin_group || !target_group)
615 throw LogicError(LogicError::wrong_kind_of_table);
616 if (origin_group != target_group)
617 throw LogicError(LogicError::group_mismatch);
619 LinkTargetInfo link(&target);
620 tf::insert_column(*this, col_ndx, type, name, link); // Throws
621 adj_insert_column(col_ndx);
623 tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws
626 inline void Descriptor::remove_column(size_t col_ndx)
628 typedef _impl::TableFriend tf;
630 if (REALM_UNLIKELY(!is_attached()))
631 throw LogicError(LogicError::detached_accessor);
632 if (REALM_UNLIKELY(col_ndx >= get_column_count()))
633 throw LogicError(LogicError::column_index_out_of_range);
635 tf::erase_column(*this, col_ndx); // Throws
636 adj_erase_column(col_ndx);
639 inline void Descriptor::rename_column(size_t col_ndx, StringData name)
641 typedef _impl::TableFriend tf;
643 if (REALM_UNLIKELY(!is_attached()))
644 throw LogicError(LogicError::detached_accessor);
645 if (REALM_UNLIKELY(col_ndx >= get_column_count()))
646 throw LogicError(LogicError::column_index_out_of_range);
648 tf::rename_column(*this, col_ndx, name); // Throws
651 inline void Descriptor::set_link_type(size_t col_ndx, LinkType link_type)
653 typedef _impl::TableFriend tf;
655 if (REALM_UNLIKELY(!is_attached()))
656 throw LogicError(LogicError::detached_accessor);
657 if (REALM_UNLIKELY(col_ndx >= get_column_count()))
658 throw LogicError(LogicError::column_index_out_of_range);
659 if (REALM_UNLIKELY(!tf::is_link_type(ColumnType(get_column_type(col_ndx)))))
660 throw LogicError(LogicError::illegal_type);
662 tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws
665 inline ConstDescriptorRef Descriptor::get_subdescriptor(size_t column_ndx) const
667 return const_cast<Descriptor*>(this)->get_subdescriptor(column_ndx);
670 inline DescriptorRef Descriptor::get_parent() noexcept
675 inline ConstDescriptorRef Descriptor::get_parent() const noexcept
677 return const_cast<Descriptor*>(this)->get_parent();
680 inline TableRef Descriptor::get_root_table() noexcept
685 inline ConstTableRef Descriptor::get_root_table() const noexcept
687 return const_cast<Descriptor*>(this)->get_root_table();
690 inline TableRef Descriptor::get_link_target(size_t col_ndx) noexcept
692 REALM_ASSERT(is_attached());
693 REALM_ASSERT(is_root());
694 return get_root_table()->get_link_target(col_ndx);
697 inline ConstTableRef Descriptor::get_link_target(size_t col_ndx) const noexcept
699 REALM_ASSERT(is_attached());
700 REALM_ASSERT(is_root());
701 return get_root_table()->get_link_target(col_ndx);
704 inline bool Descriptor::is_root() const noexcept
709 inline Descriptor::Descriptor() noexcept
713 inline void Descriptor::attach(Table* table, DescriptorRef parent, Spec* spec) noexcept
715 REALM_ASSERT(!is_attached());
716 REALM_ASSERT(!table->has_shared_type());
717 m_root_table.reset(table);
722 inline bool Descriptor::is_attached() const noexcept
724 return bool(m_root_table);
727 inline Descriptor::subdesc_entry::subdesc_entry(size_t n, DescriptorRef d)
733 inline bool Descriptor::operator==(const Descriptor& d) const noexcept
735 REALM_ASSERT(is_attached());
736 REALM_ASSERT(d.is_attached());
737 return *m_spec == *d.m_spec;
740 inline bool Descriptor::operator!=(const Descriptor& d) const noexcept
742 return !(*this == d);
745 // The purpose of this class is to give internal access to some, but
746 // not all of the non-public parts of the Descriptor class.
747 class _impl::DescriptorFriend {
749 static DescriptorRef create()
751 return std::make_shared<Descriptor>(Descriptor::PrivateTag()); // Throws
754 static void attach(Descriptor& desc, Table* table, DescriptorRef parent, Spec* spec) noexcept
756 desc.attach(table, parent, spec);
759 static void detach(Descriptor& desc) noexcept
764 static void detach_subdesc_accessors(Descriptor& desc) noexcept
766 desc.detach_subdesc_accessors();
769 static Table& get_root_table(Descriptor& desc) noexcept
771 return *desc.m_root_table;
774 static const Table& get_root_table(const Descriptor& desc) noexcept
776 return *desc.m_root_table;
779 static Spec& get_spec(Descriptor& desc) noexcept
784 static const Spec& get_spec(const Descriptor& desc) noexcept
789 static size_t* record_subdesc_path(const Descriptor& desc, size_t* begin, size_t* end) noexcept
791 return desc.record_subdesc_path(begin, end);
794 static DescriptorRef get_subdesc_accessor(Descriptor& desc, size_t column_ndx) noexcept
796 return desc.get_subdesc_accessor(column_ndx);
799 static void adj_insert_column(Descriptor& desc, size_t col_ndx) noexcept
801 desc.adj_insert_column(col_ndx);
804 static void adj_erase_column(Descriptor& desc, size_t col_ndx) noexcept
806 desc.adj_erase_column(col_ndx);
812 #endif // REALM_DESCRIPTOR_HPP