added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / descriptor.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
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
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_DESCRIPTOR_HPP
20 #define REALM_DESCRIPTOR_HPP
21
22 #include <cstddef>
23
24 #include <realm/util/assert.hpp>
25 #include <realm/descriptor_fwd.hpp>
26 #include <realm/table.hpp>
27
28
29 namespace realm {
30
31 namespace _impl {
32 class DescriptorFriend;
33 }
34
35
36 /// Accessor for table type descriptors.
37 ///
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.
45 ///
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.
49 ///
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
53 /// more on this.
54 ///
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:
60 ///
61 ///     table->add_column(type, name);
62 ///     table->get_descriptor()->add_column(type, name);
63 ///
64 /// Note, however, that this equivalence holds only as long as no
65 /// shared subtable descriptors are involved.
66 ///
67 /// \sa Table::get_descriptor()
68 class Descriptor : public std::enable_shared_from_this<Descriptor> {
69 public:
70     /// Get the number of columns in the associated tables.
71     size_t get_column_count() const noexcept;
72
73     /// Get the type of the column at the specified index.
74     ///
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;
78
79     /// Get the name of the column at the specified index.
80     ///
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;
84
85     /// Search for a column with the specified name.
86     ///
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;
91
92     /// Get the index of the table to which links in the column at the specified
93     /// index refer.
94     ///
95     /// The consequences of specifying a column index that is out of
96     /// range, are undefined.
97     ///
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;
101
102     /// Get whether or not the specified column is nullable.
103     ///
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;
107
108     /// \defgroup descriptor_column_accessors Accessing Columns Via A Descriptor
109     ///
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.
114     ///
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.
120     ///
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
125     /// descriptor.
126     ///
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.
135     ///
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
140     /// be thrown.
141     ///
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
146     /// `*subdesc`.
147     ///
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.
152     ///
153     /// \param link_type See set_link_type().
154     ///
155     /// \sa Table::add_column()
156     /// \sa Table::insert_column()
157     /// \sa Table::add_column_link()
158     /// \sa Table::insert_column_link()
159     /// \sa is_root()
160     //@{
161
162     static const size_t max_column_name_length = 63;
163
164     size_t add_column(DataType type, StringData name, DescriptorRef* subdesc = nullptr, bool nullable = false);
165
166     void insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc = nullptr,
167                        bool nullable = false);
168
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);
171     //@}
172
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.
177     ///
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.
183     ///
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.
190     ///
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
193     /// columns.
194     ///
195     /// \sa is_root()
196     /// \sa Table::remove_column()
197     void remove_column(size_t col_ndx);
198
199     /// Rename the specified column.
200     ///
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.
204     ///
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
208     /// descriptor.
209     ///
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
212     /// columns.
213     ///
214     /// \param new_name The new name of the column.
215     ///
216     /// \sa is_root()
217     /// \sa Table::rename_column()
218     void rename_column(size_t col_ndx, StringData new_name);
219
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);
227
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
235     /// disappears.
236     ///
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
241     /// cascade-removed.
242     ///
243     /// A link is considered broken if
244     ///
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
248     ///
249     ///  - the origin row is explicitly removed (Row::move_last_over(),
250     ///    Table::clear()), or if
251     ///
252     ///  - the origin row is cascade-removed, or if
253     ///
254     ///  - the origin column is removed from the table (Table::remove_column()),
255     ///    or if
256     ///
257     ///  - the origin table is removed from the group.
258     ///
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.
262     ///
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
267     /// the list.
268     ///
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.
271     ///
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.
276     ///
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:
284     ///
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
287     ///
288     /// To be safe, applications, that may encounter cycles, are advised to
289     /// adopt the following pattern:
290     ///
291     ///     Row row = table[row_ndx];
292     ///     row.set_link(col_ndx_1, ...);
293     ///     if (row)
294     ///         row.set_int(col_ndx_2, ...); // Ok, because we check whether the row has disappeared
295     ///
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.
300     ///
301     /// \param link_type The type of links the column should store.
302     void set_link_type(size_t col_ndx, LinkType link_type);
303
304     //@{
305     /// Get the descriptor for the specified subtable column.
306     ///
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.
312     ///
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.
318     ///
319     /// \sa is_root()
320     DescriptorRef get_subdescriptor(size_t column_ndx);
321     ConstDescriptorRef get_subdescriptor(size_t column_ndx) const;
322     //@}
323
324     //@{
325     /// Returns the parent table descriptor, if any.
326     ///
327     /// If this descriptor is the *root descriptor*, then this
328     /// function returns null. Otherwise it returns the accessor of
329     /// the parent descriptor.
330     ///
331     /// \sa is_root()
332     DescriptorRef get_parent() noexcept;
333     ConstDescriptorRef get_parent() const noexcept;
334     //@}
335
336     //@{
337     /// Get the table associated with the root descriptor.
338     ///
339     /// \sa get_parent()
340     /// \sa is_root()
341     TableRef get_root_table() noexcept;
342     ConstTableRef get_root_table() const noexcept;
343     //@}
344
345     //@{
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;
351     //@}
352
353     /// Is this a root descriptor?
354     ///
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'.
360     ///
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
365     /// descriptors.
366     ///
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.
374     ///
375     /// \sa Table::has_shared_type()
376     bool is_root() const noexcept;
377
378     /// Determine whether this accessor is still attached.
379     ///
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.
388     ///
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;
397
398     //@{
399     /// \brief Compare two table descriptors.
400     ///
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.
404     ///
405     /// For link columns (`type_Link` and `type_LinkList`), the target table
406     /// (get_link_target()) of the two columns must be the same.
407     ///
408     /// For subtable columns (`type_Table`), the two corresponding
409     /// subdescriptors must themselves be equal, as if by a recursive call to
410     /// operator==().
411     ///
412     /// The consequences of comparing a detached descriptor are
413     /// undefined.
414     bool operator==(const Descriptor&) const noexcept;
415     bool operator!=(const Descriptor&) const noexcept;
416     //@}
417
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;
423
424     ~Descriptor() noexcept;
425
426 private:
427     // for initialization through make_shared
428     struct PrivateTag {
429     };
430
431 public:
432     Descriptor(const PrivateTag&)
433         : Descriptor()
434     {
435     }
436
437 private:
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`.
442
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.
448     //
449     // It also enables the necessary recursive detaching of descriptor
450     // objects.
451     struct subdesc_entry {
452         size_t m_column_ndx;
453         std::weak_ptr<Descriptor> m_subdesc;
454         subdesc_entry(size_t column_ndx, DescriptorRef);
455     };
456     typedef std::vector<subdesc_entry> subdesc_map;
457     mutable subdesc_map m_subdesc_map;
458
459     Descriptor() noexcept;
460
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.
464     //
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.
470     //
471     // Not idempotent.
472     //
473     // The specified table is not allowed to be a subtable with a
474     // shareable spec. That is, Table::has_shared_spec() must return
475     // false.
476     //
477     // The specified spec must be the spec of the specified table or
478     // of one of its direct or indirect subtable columns.
479     //
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;
484
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.
488     //
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.
491     //
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.
496     //
497     // Not idempotent.
498     void detach() noexcept;
499
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;
504
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;
512
513     // Returns a pointer to the accessor of the specified
514     // subdescriptor if that accessor exists, otherwise this function
515     // return null.
516     DescriptorRef get_subdesc_accessor(size_t column_ndx) noexcept;
517
518     void adj_insert_column(size_t col_ndx) noexcept;
519     void adj_erase_column(size_t col_ndx) noexcept;
520
521     friend class util::bind_ptr<Descriptor>;
522     friend class util::bind_ptr<const Descriptor>;
523     friend class _impl::DescriptorFriend;
524 };
525
526
527 // Implementation:
528
529 inline size_t Descriptor::get_column_count() const noexcept
530 {
531     REALM_ASSERT(is_attached());
532     return m_spec->get_public_column_count();
533 }
534
535 inline StringData Descriptor::get_column_name(size_t ndx) const noexcept
536 {
537     REALM_ASSERT(is_attached());
538     return m_spec->get_column_name(ndx);
539 }
540
541 inline DataType Descriptor::get_column_type(size_t ndx) const noexcept
542 {
543     REALM_ASSERT(is_attached());
544     return m_spec->get_public_column_type(ndx);
545 }
546
547 inline bool Descriptor::is_nullable(size_t ndx) const noexcept
548 {
549     REALM_ASSERT(is_attached());
550     return m_spec->get_column_attr(ndx) & col_attr_Nullable;
551 }
552
553 inline size_t Descriptor::get_column_index(StringData name) const noexcept
554 {
555     REALM_ASSERT(is_attached());
556     return m_spec->get_column_index(name);
557 }
558
559 inline size_t Descriptor::get_column_link_target(size_t column_ndx) const noexcept
560 {
561     REALM_ASSERT(is_attached());
562     return m_spec->get_opposite_link_table_ndx(column_ndx);
563 }
564
565 inline size_t Descriptor::add_column(DataType type, StringData name, DescriptorRef* subdesc, bool nullable)
566 {
567     size_t col_ndx = m_spec->get_public_column_count();
568     insert_column(col_ndx, type, name, subdesc, nullable); // Throws
569     return col_ndx;
570 }
571
572 inline void Descriptor::insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc,
573                                       bool nullable)
574 {
575     typedef _impl::TableFriend tf;
576
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);
583
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);
589 }
590
591 inline size_t Descriptor::add_column_link(DataType type, StringData name, Table& target, LinkType link_type)
592 {
593     size_t col_ndx = m_spec->get_public_column_count();
594     insert_column_link(col_ndx, type, name, target, link_type); // Throws
595     return col_ndx;
596 }
597
598 inline void Descriptor::insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target,
599                                            LinkType link_type)
600 {
601     typedef _impl::TableFriend tf;
602
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);
618
619     LinkTargetInfo link(&target);
620     tf::insert_column(*this, col_ndx, type, name, link); // Throws
621     adj_insert_column(col_ndx);
622
623     tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws
624 }
625
626 inline void Descriptor::remove_column(size_t col_ndx)
627 {
628     typedef _impl::TableFriend tf;
629
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);
634
635     tf::erase_column(*this, col_ndx); // Throws
636     adj_erase_column(col_ndx);
637 }
638
639 inline void Descriptor::rename_column(size_t col_ndx, StringData name)
640 {
641     typedef _impl::TableFriend tf;
642
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);
647
648     tf::rename_column(*this, col_ndx, name); // Throws
649 }
650
651 inline void Descriptor::set_link_type(size_t col_ndx, LinkType link_type)
652 {
653     typedef _impl::TableFriend tf;
654
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);
661
662     tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws
663 }
664
665 inline ConstDescriptorRef Descriptor::get_subdescriptor(size_t column_ndx) const
666 {
667     return const_cast<Descriptor*>(this)->get_subdescriptor(column_ndx);
668 }
669
670 inline DescriptorRef Descriptor::get_parent() noexcept
671 {
672     return m_parent;
673 }
674
675 inline ConstDescriptorRef Descriptor::get_parent() const noexcept
676 {
677     return const_cast<Descriptor*>(this)->get_parent();
678 }
679
680 inline TableRef Descriptor::get_root_table() noexcept
681 {
682     return m_root_table;
683 }
684
685 inline ConstTableRef Descriptor::get_root_table() const noexcept
686 {
687     return const_cast<Descriptor*>(this)->get_root_table();
688 }
689
690 inline TableRef Descriptor::get_link_target(size_t col_ndx) noexcept
691 {
692     REALM_ASSERT(is_attached());
693     REALM_ASSERT(is_root());
694     return get_root_table()->get_link_target(col_ndx);
695 }
696
697 inline ConstTableRef Descriptor::get_link_target(size_t col_ndx) const noexcept
698 {
699     REALM_ASSERT(is_attached());
700     REALM_ASSERT(is_root());
701     return get_root_table()->get_link_target(col_ndx);
702 }
703
704 inline bool Descriptor::is_root() const noexcept
705 {
706     return !m_parent;
707 }
708
709 inline Descriptor::Descriptor() noexcept
710 {
711 }
712
713 inline void Descriptor::attach(Table* table, DescriptorRef parent, Spec* spec) noexcept
714 {
715     REALM_ASSERT(!is_attached());
716     REALM_ASSERT(!table->has_shared_type());
717     m_root_table.reset(table);
718     m_parent = parent;
719     m_spec = spec;
720 }
721
722 inline bool Descriptor::is_attached() const noexcept
723 {
724     return bool(m_root_table);
725 }
726
727 inline Descriptor::subdesc_entry::subdesc_entry(size_t n, DescriptorRef d)
728     : m_column_ndx(n)
729     , m_subdesc(d)
730 {
731 }
732
733 inline bool Descriptor::operator==(const Descriptor& d) const noexcept
734 {
735     REALM_ASSERT(is_attached());
736     REALM_ASSERT(d.is_attached());
737     return *m_spec == *d.m_spec;
738 }
739
740 inline bool Descriptor::operator!=(const Descriptor& d) const noexcept
741 {
742     return !(*this == d);
743 }
744
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 {
748 public:
749     static DescriptorRef create()
750     {
751         return std::make_shared<Descriptor>(Descriptor::PrivateTag()); // Throws
752     }
753
754     static void attach(Descriptor& desc, Table* table, DescriptorRef parent, Spec* spec) noexcept
755     {
756         desc.attach(table, parent, spec);
757     }
758
759     static void detach(Descriptor& desc) noexcept
760     {
761         desc.detach();
762     }
763
764     static void detach_subdesc_accessors(Descriptor& desc) noexcept
765     {
766         desc.detach_subdesc_accessors();
767     }
768
769     static Table& get_root_table(Descriptor& desc) noexcept
770     {
771         return *desc.m_root_table;
772     }
773
774     static const Table& get_root_table(const Descriptor& desc) noexcept
775     {
776         return *desc.m_root_table;
777     }
778
779     static Spec& get_spec(Descriptor& desc) noexcept
780     {
781         return *desc.m_spec;
782     }
783
784     static const Spec& get_spec(const Descriptor& desc) noexcept
785     {
786         return *desc.m_spec;
787     }
788
789     static size_t* record_subdesc_path(const Descriptor& desc, size_t* begin, size_t* end) noexcept
790     {
791         return desc.record_subdesc_path(begin, end);
792     }
793
794     static DescriptorRef get_subdesc_accessor(Descriptor& desc, size_t column_ndx) noexcept
795     {
796         return desc.get_subdesc_accessor(column_ndx);
797     }
798
799     static void adj_insert_column(Descriptor& desc, size_t col_ndx) noexcept
800     {
801         desc.adj_insert_column(col_ndx);
802     }
803
804     static void adj_erase_column(Descriptor& desc, size_t col_ndx) noexcept
805     {
806         desc.adj_erase_column(col_ndx);
807     }
808 };
809
810 } // namespace realm
811
812 #endif // REALM_DESCRIPTOR_HPP