added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / spec.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_SPEC_HPP
20 #define REALM_SPEC_HPP
21
22 #include <realm/util/features.h>
23 #include <realm/array.hpp>
24 #include <realm/array_string.hpp>
25 #include <realm/array_integer.hpp>
26 #include <realm/data_type.hpp>
27 #include <realm/column_type.hpp>
28
29 namespace realm {
30
31 class Table;
32
33 class Spec {
34 public:
35     ~Spec() noexcept;
36
37     Allocator& get_alloc() const noexcept;
38
39     bool has_strong_link_columns() noexcept;
40
41     void insert_column(size_t column_ndx, ColumnType type, StringData name, ColumnAttr attr = col_attr_None);
42     void rename_column(size_t column_ndx, StringData new_name);
43
44     /// Erase the column at the specified index, and move columns at
45     /// succeeding indexes to the next lower index.
46     ///
47     /// This function is guaranteed to *never* throw if the spec is
48     /// used in a non-transactional context, or if the spec has
49     /// already been successfully modified within the current write
50     /// transaction.
51     void erase_column(size_t column_ndx);
52
53     //@{
54     // If a new Spec is constructed from the returned subspec
55     // reference, it is the responsibility of the application that the
56     // parent Spec object (this) is kept alive for at least as long as
57     // the new Spec object.
58     Spec* get_subtable_spec(size_t column_ndx) noexcept;
59     //@}
60
61     // Column info
62     size_t get_column_count() const noexcept;
63     size_t get_public_column_count() const noexcept;
64     DataType get_public_column_type(size_t column_ndx) const noexcept;
65     ColumnType get_column_type(size_t column_ndx) const noexcept;
66     StringData get_column_name(size_t column_ndx) const noexcept;
67
68     /// Returns size_t(-1) if the specified column is not found.
69     size_t get_column_index(StringData name) const noexcept;
70
71     // Column Attributes
72     ColumnAttr get_column_attr(size_t column_ndx) const noexcept;
73
74     size_t get_subspec_ndx(size_t column_ndx) const noexcept;
75     ref_type get_subspec_ref(size_t subspec_ndx) const noexcept;
76     Spec* get_subspec_by_ndx(size_t subspec_ndx) noexcept;
77     const Spec* get_subspec_by_ndx(size_t subspec_ndx) const noexcept;
78
79     // Auto Enumerated string columns
80     void upgrade_string_to_enum(size_t column_ndx, ref_type keys_ref, ArrayParent*& keys_parent, size_t& keys_ndx);
81     size_t get_enumkeys_ndx(size_t column_ndx) const noexcept;
82     ref_type get_enumkeys_ref(size_t column_ndx, ArrayParent** keys_parent = nullptr,
83                               size_t* keys_ndx = nullptr) noexcept;
84
85     // Links
86     size_t get_opposite_link_table_ndx(size_t column_ndx) const noexcept;
87     void set_opposite_link_table_ndx(size_t column_ndx, size_t table_ndx);
88
89     // Backlinks
90     bool has_backlinks() const noexcept;
91     size_t first_backlink_column_index() const noexcept;
92     size_t backlink_column_count() const noexcept;
93     void set_backlink_origin_column(size_t backlink_col_ndx, size_t origin_col_ndx);
94     size_t get_origin_column_ndx(size_t backlink_col_ndx) const noexcept;
95     size_t find_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx) const noexcept;
96
97     /// Get position in `Table::m_columns` of the specified column. It may be
98     /// different from the specified logical column index due to the presence of
99     /// search indexes, since their top refs are stored in Table::m_columns as
100     /// well.
101     size_t get_column_ndx_in_parent(size_t column_ndx) const;
102
103     //@{
104     /// Compare two table specs for equality.
105     bool operator==(const Spec&) const noexcept;
106     bool operator!=(const Spec&) const noexcept;
107     //@}
108
109     void detach() noexcept;
110     void destroy() noexcept;
111
112     size_t get_ndx_in_parent() const noexcept;
113     void set_ndx_in_parent(size_t) noexcept;
114
115 #ifdef REALM_DEBUG
116     void verify() const;
117     void to_dot(std::ostream&, StringData title = StringData()) const;
118 #endif
119
120 private:
121     // Underlying array structure.
122     //
123     // `m_subspecs` contains one entry for each subtable column, one entry for
124     // each link or link list columns, two entries for each backlink column, and
125     // zero entries for all other column types. For subtable columns the entry
126     // is a ref pointing to the subtable spec, for link and link list columns it
127     // is the group-level table index of the target table, and for backlink
128     // columns the first entry is the group-level table index of the origin
129     // table, and the second entry is the index of the origin column in the
130     // origin table.
131     Array m_top;
132     ArrayInteger m_types; // 1st slot in m_top
133     ArrayString m_names;  // 2nd slot in m_top
134     ArrayInteger m_attr;  // 3rd slot in m_top
135     Array m_subspecs;     // 4th slot in m_top (optional)
136     Array m_enumkeys;     // 5th slot in m_top (optional)
137     struct SubspecPtr {
138         SubspecPtr(bool is_spec_ptr = false)
139             : m_is_spec_ptr(is_spec_ptr)
140         {
141         }
142         std::unique_ptr<Spec> m_spec;
143         bool m_is_spec_ptr;
144     };
145     using SubspecPtrs = std::vector<SubspecPtr>;
146     SubspecPtrs m_subspec_ptrs;
147     bool m_has_strong_link_columns;
148
149     Spec(Allocator&) noexcept; // Unattached
150
151     bool init(ref_type) noexcept;
152     void init(MemRef) noexcept;
153     void update_has_strong_link_columns() noexcept;
154     void reset_subspec_ptrs();
155     void adj_subspec_ptrs();
156
157     // Returns true in case the ref has changed.
158     bool init_from_parent() noexcept;
159
160     ref_type get_ref() const noexcept;
161
162     /// Called in the context of Group::commit() to ensure that
163     /// attached table accessors stay valid across a commit. Please
164     /// note that this works only for non-transactional commits. Table
165     /// accessors obtained during a transaction are always detached
166     /// when the transaction ends.
167     bool update_from_parent(size_t old_baseline) noexcept;
168
169     void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept;
170
171     void set_column_type(size_t column_ndx, ColumnType type);
172     void set_column_attr(size_t column_ndx, ColumnAttr attr);
173
174     /// Construct an empty spec and return just the reference to the
175     /// underlying memory.
176     static MemRef create_empty_spec(Allocator&);
177
178     struct ColumnInfo {
179         size_t m_column_ref_ndx = 0; ///< Index within Table::m_columns
180         bool m_has_search_index = false;
181     };
182
183     ColumnInfo get_column_info(size_t column_ndx) const noexcept;
184
185     size_t get_subspec_ndx_after(size_t column_ndx, size_t skip_column_ndx) const noexcept;
186     size_t get_subspec_entries_for_col_type(ColumnType type) const noexcept;
187     bool has_subspec() const noexcept;
188
189     // Returns false if the spec has no columns, otherwise it returns
190     // true and sets `type` to the type of the first column.
191     static bool get_first_column_type_from_ref(ref_type, Allocator&, ColumnType& type) noexcept;
192
193     friend class Replication;
194     friend class Group;
195     friend class Table;
196 };
197
198 // Implementation:
199
200 inline Allocator& Spec::get_alloc() const noexcept
201 {
202     return m_top.get_alloc();
203 }
204
205 inline bool Spec::has_strong_link_columns() noexcept
206 {
207     return m_has_strong_link_columns;
208 }
209
210 inline ref_type Spec::get_subspec_ref(size_t subspec_ndx) const noexcept
211 {
212     REALM_ASSERT(subspec_ndx < m_subspecs.size());
213
214     // Note that this addresses subspecs directly, indexing
215     // by number of sub-table columns
216     return m_subspecs.get_as_ref(subspec_ndx);
217 }
218
219 // Uninitialized Spec (call init() to init)
220 inline Spec::Spec(Allocator& alloc) noexcept
221     : m_top(alloc)
222     , m_types(alloc)
223     , m_names(alloc)
224     , m_attr(alloc)
225     , m_subspecs(alloc)
226     , m_enumkeys(alloc)
227 {
228 }
229
230 inline Spec* Spec::get_subtable_spec(size_t column_ndx) noexcept
231 {
232     REALM_ASSERT(column_ndx < get_column_count());
233     REALM_ASSERT(get_column_type(column_ndx) == col_type_Table);
234     size_t subspec_ndx = get_subspec_ndx(column_ndx);
235     return get_subspec_by_ndx(subspec_ndx);
236 }
237
238 inline const Spec* Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept
239 {
240     return const_cast<Spec*>(this)->get_subspec_by_ndx(subspec_ndx);
241 }
242
243 inline bool Spec::init_from_parent() noexcept
244 {
245     ref_type ref = m_top.get_ref_from_parent();
246     return init(ref);
247 }
248
249 inline void Spec::destroy() noexcept
250 {
251     m_top.destroy_deep();
252 }
253
254 inline size_t Spec::get_ndx_in_parent() const noexcept
255 {
256     return m_top.get_ndx_in_parent();
257 }
258
259 inline void Spec::set_ndx_in_parent(size_t ndx) noexcept
260 {
261     m_top.set_ndx_in_parent(ndx);
262 }
263
264 inline ref_type Spec::get_ref() const noexcept
265 {
266     return m_top.get_ref();
267 }
268
269 inline void Spec::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
270 {
271     m_top.set_parent(parent, ndx_in_parent);
272 }
273
274 inline void Spec::rename_column(size_t column_ndx, StringData new_name)
275 {
276     REALM_ASSERT(column_ndx < m_types.size());
277     m_names.set(column_ndx, new_name);
278 }
279
280 inline size_t Spec::get_column_count() const noexcept
281 {
282     // This is the total count of columns, including backlinks (not public)
283     return m_types.size();
284 }
285
286 inline size_t Spec::get_public_column_count() const noexcept
287 {
288     // Backlinks are the last columns, and do not have names, so getting
289     // the number of names gives us the count of user facing columns
290     return m_names.size();
291 }
292
293 inline ColumnType Spec::get_column_type(size_t ndx) const noexcept
294 {
295     REALM_ASSERT(ndx < get_column_count());
296     return ColumnType(m_types.get(ndx));
297 }
298
299 inline void Spec::set_column_type(size_t column_ndx, ColumnType type)
300 {
301     REALM_ASSERT(column_ndx < get_column_count());
302
303     // At this point we only support upgrading to string enum
304     REALM_ASSERT(ColumnType(m_types.get(column_ndx)) == col_type_String);
305     REALM_ASSERT(type == col_type_StringEnum);
306
307     m_types.set(column_ndx, type); // Throws
308
309     update_has_strong_link_columns();
310 }
311
312 inline ColumnAttr Spec::get_column_attr(size_t ndx) const noexcept
313 {
314     REALM_ASSERT(ndx < get_column_count());
315     return ColumnAttr(m_attr.get(ndx));
316 }
317
318 inline void Spec::set_column_attr(size_t column_ndx, ColumnAttr attr)
319 {
320     REALM_ASSERT(column_ndx < get_column_count());
321
322     // At this point we only allow one attr at a time
323     // so setting it will overwrite existing. In the future
324     // we will allow combinations.
325     m_attr.set(column_ndx, attr);
326
327     update_has_strong_link_columns();
328 }
329
330 inline StringData Spec::get_column_name(size_t ndx) const noexcept
331 {
332     REALM_ASSERT(ndx < get_column_count());
333     return m_names.get(ndx);
334 }
335
336 inline size_t Spec::get_column_index(StringData name) const noexcept
337 {
338     return m_names.find_first(name);
339 }
340
341 inline bool Spec::get_first_column_type_from_ref(ref_type top_ref, Allocator& alloc, ColumnType& type) noexcept
342 {
343     const char* top_header = alloc.translate(top_ref);
344     ref_type types_ref = to_ref(Array::get(top_header, 0));
345     const char* types_header = alloc.translate(types_ref);
346     if (Array::get_size_from_header(types_header) == 0)
347         return false;
348     type = ColumnType(Array::get(types_header, 0));
349     return true;
350 }
351
352 inline bool Spec::has_backlinks() const noexcept
353 {
354     // backlinks are always last and do not have names.
355     return m_names.size() < m_types.size();
356
357     // Fixme: It's bad design that backlinks are stored and recognized like this. Backlink columns
358     // should be a column type like any other, and we should find another way to hide them away from
359     // the user.
360 }
361
362 inline size_t Spec::first_backlink_column_index() const noexcept
363 {
364     return m_names.size();
365 }
366
367 inline size_t Spec::backlink_column_count() const noexcept
368 {
369     return m_types.size() - m_names.size();
370 }
371
372 // Spec will have a subspec when it contains a column which is one of:
373 // link, linklist, backlink, or subtable. It is possible for m_top.size()
374 // to contain an entry for m_subspecs (at index 3) but this reference
375 // may be empty if the spec contains enumkeys (at index 4) but no subspec types.
376 inline bool Spec::has_subspec() const noexcept
377 {
378     return (m_top.size() >= 4) && (m_top.get_as_ref(3) != 0);
379 }
380
381 inline bool Spec::operator!=(const Spec& s) const noexcept
382 {
383     return !(*this == s);
384 }
385
386 } // namespace realm
387
388 #endif // REALM_SPEC_HPP