spec.hpp
14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
/*************************************************************************
*
* REALM CONFIDENTIAL
* __________________
*
* [2011] - [2015] Realm Inc
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Realm Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Realm Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Realm Incorporated.
*
**************************************************************************/
#ifndef REALM_SPEC_HPP
#define REALM_SPEC_HPP
#include <realm/util/features.h>
#include <realm/array.hpp>
#include <realm/array_string.hpp>
#include <realm/array_integer.hpp>
#include <realm/data_type.hpp>
#include <realm/column_type.hpp>
namespace realm {
class Table;
class SubspecRef;
class ConstSubspecRef;
class Spec {
public:
Spec(SubspecRef) noexcept;
~Spec() noexcept;
Allocator& get_alloc() const noexcept;
bool has_strong_link_columns() noexcept;
void insert_column(size_t column_ndx, ColumnType type, StringData name,
ColumnAttr attr = col_attr_None);
void rename_column(size_t column_ndx, StringData new_name);
void move_column(size_t from, size_t to);
/// Erase the column at the specified index, and move columns at
/// succeeding indexes to the next lower index.
///
/// This function is guaranteed to *never* throw if the spec is
/// used in a non-transactional context, or if the spec has
/// already been successfully modified within the current write
/// transaction.
void erase_column(size_t column_ndx);
//@{
// If a new Spec is constructed from the returned subspec
// reference, it is the responsibility of the application that the
// parent Spec object (this) is kept alive for at least as long as
// the new Spec object.
SubspecRef get_subtable_spec(size_t column_ndx) noexcept;
ConstSubspecRef get_subtable_spec(size_t column_ndx) const noexcept;
//@}
// Column info
size_t get_column_count() const noexcept;
size_t get_public_column_count() const noexcept;
DataType get_public_column_type(size_t column_ndx) const noexcept;
ColumnType get_column_type(size_t column_ndx) const noexcept;
StringData get_column_name(size_t column_ndx) const noexcept;
/// Returns size_t(-1) if the specified column is not found.
size_t get_column_index(StringData name) const noexcept;
// Column Attributes
ColumnAttr get_column_attr(size_t column_ndx) const noexcept;
size_t get_subspec_ndx(size_t column_ndx) const noexcept;
ref_type get_subspec_ref(size_t subspec_ndx) const noexcept;
SubspecRef get_subspec_by_ndx(size_t subspec_ndx) noexcept;
ConstSubspecRef get_subspec_by_ndx(size_t subspec_ndx) const noexcept;
// Auto Enumerated string columns
void upgrade_string_to_enum(size_t column_ndx, ref_type keys_ref,
ArrayParent*& keys_parent, size_t& keys_ndx);
size_t get_enumkeys_ndx(size_t column_ndx) const noexcept;
ref_type get_enumkeys_ref(size_t column_ndx, ArrayParent** keys_parent = nullptr,
size_t* keys_ndx = nullptr) noexcept;
// Links
size_t get_opposite_link_table_ndx(size_t column_ndx) const noexcept;
void set_opposite_link_table_ndx(size_t column_ndx, size_t table_ndx);
bool has_backlinks() const noexcept;
void set_backlink_origin_column(size_t backlink_col_ndx, size_t origin_col_ndx);
size_t get_origin_column_ndx(size_t backlink_col_ndx) const noexcept;
size_t find_backlink_column(size_t origin_table_ndx,
size_t origin_col_ndx) const noexcept;
/// Get position in `Table::m_columns` of the specified column. It may be
/// different from the specified logical column index due to the presence of
/// search indexes, since their top refs are stored in Table::m_columns as
/// well.
size_t get_column_ndx_in_parent(size_t column_ndx) const;
//@{
/// Compare two table specs for equality.
bool operator==(const Spec&) const noexcept;
bool operator!=(const Spec&) const noexcept;
//@}
void destroy() noexcept;
size_t get_ndx_in_parent() const noexcept;
void set_ndx_in_parent(size_t) noexcept;
#ifdef REALM_DEBUG
void verify() const; // Must be upper case to avoid conflict with macro in ObjC
void to_dot(std::ostream&, StringData title = StringData()) const;
#endif
private:
// Underlying array structure.
//
// `m_subspecs` contains one entry for each subtable column, one entry for
// each link or link list columns, two entries for each backlink column, and
// zero entries for all other column types. For subtable columns the entry
// is a ref pointing to the subtable spec, for link and link list columns it
// is the group-level table index of the target table, and for backlink
// columns the first entry is the group-level table index of the origin
// table, and the second entry is the index of the origin column in the
// origin table.
Array m_top;
ArrayInteger m_types;// 1st slot in m_top
ArrayString m_names; // 2nd slot in m_top
ArrayInteger m_attr; // 3rd slot in m_top
Array m_subspecs; // 4th slot in m_top (optional)
Array m_enumkeys; // 5th slot in m_top (optional)
bool m_has_strong_link_columns;
Spec(Allocator&) noexcept; // Unattached
void init(ref_type) noexcept;
void init(MemRef) noexcept;
void init(SubspecRef) noexcept;
void update_has_strong_link_columns() noexcept;
// Similar in function to Array::init_from_parent().
void init_from_parent() noexcept;
ref_type get_ref() const noexcept;
/// Called in the context of Group::commit() to ensure that
/// attached table accessors stay valid across a commit. Please
/// note that this works only for non-transactional commits. Table
/// accessors obtained during a transaction are always detached
/// when the transaction ends.
void update_from_parent(size_t old_baseline) noexcept;
void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept;
void set_column_type(size_t column_ndx, ColumnType type);
void set_column_attr(size_t column_ndx, ColumnAttr attr);
/// Construct an empty spec and return just the reference to the
/// underlying memory.
static MemRef create_empty_spec(Allocator&);
struct ColumnInfo {
size_t m_column_ref_ndx = 0; ///< Index within Table::m_columns
bool m_has_search_index = false;
};
ColumnInfo get_column_info(size_t column_ndx) const noexcept;
size_t get_subspec_ndx_after(size_t column_ndx) const noexcept;
bool has_subspec() const noexcept;
// Returns false if the spec has no columns, otherwise it returns
// true and sets `type` to the type of the first column.
static bool get_first_column_type_from_ref(ref_type, Allocator&,
ColumnType& type) noexcept;
friend class Replication;
friend class Group;
friend class Table;
};
class SubspecRef {
public:
struct const_cast_tag {};
SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept;
~SubspecRef() noexcept {}
Allocator& get_alloc() const noexcept { return m_parent->get_alloc(); }
private:
Array* const m_parent;
size_t const m_ndx_in_parent;
SubspecRef(Array* parent, size_t ndx_in_parent) noexcept;
friend class Spec;
friend class ConstSubspecRef;
};
class ConstSubspecRef {
public:
ConstSubspecRef(SubspecRef r) noexcept;
~ConstSubspecRef() noexcept {}
Allocator& get_alloc() const noexcept { return m_parent->get_alloc(); }
private:
const Array* const m_parent;
size_t const m_ndx_in_parent;
ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept;
friend class Spec;
friend class SubspecRef;
};
// Implementation:
inline Allocator& Spec::get_alloc() const noexcept
{
return m_top.get_alloc();
}
inline bool Spec::has_strong_link_columns() noexcept
{
return m_has_strong_link_columns;
}
inline ref_type Spec::get_subspec_ref(size_t subspec_ndx) const noexcept
{
REALM_ASSERT(subspec_ndx < m_subspecs.size());
// Note that this addresses subspecs directly, indexing
// by number of sub-table columns
return m_subspecs.get_as_ref(subspec_ndx);
}
inline Spec::Spec(SubspecRef r) noexcept:
m_top(r.m_parent->get_alloc()),
m_types(r.m_parent->get_alloc()),
m_names(r.m_parent->get_alloc()),
m_attr(r.m_parent->get_alloc()),
m_subspecs(r.m_parent->get_alloc()),
m_enumkeys(r.m_parent->get_alloc())
{
init(r);
}
// Uninitialized Spec (call init() to init)
inline Spec::Spec(Allocator& alloc) noexcept:
m_top(alloc),
m_types(alloc),
m_names(alloc),
m_attr(alloc),
m_subspecs(alloc),
m_enumkeys(alloc)
{
}
inline SubspecRef Spec::get_subtable_spec(size_t column_ndx) noexcept
{
REALM_ASSERT(column_ndx < get_column_count());
REALM_ASSERT(get_column_type(column_ndx) == col_type_Table);
size_t subspec_ndx = get_subspec_ndx(column_ndx);
return SubspecRef(&m_subspecs, subspec_ndx);
}
inline ConstSubspecRef Spec::get_subtable_spec(size_t column_ndx) const noexcept
{
REALM_ASSERT(column_ndx < get_column_count());
REALM_ASSERT(get_column_type(column_ndx) == col_type_Table);
size_t subspec_ndx = get_subspec_ndx(column_ndx);
return ConstSubspecRef(&m_subspecs, subspec_ndx);
}
inline SubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) noexcept
{
return SubspecRef(&m_subspecs, subspec_ndx);
}
inline ConstSubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept
{
return const_cast<Spec*>(this)->get_subspec_by_ndx(subspec_ndx);
}
inline void Spec::init(ref_type ref) noexcept
{
MemRef mem(ref, get_alloc());
init(mem);
}
inline void Spec::init(SubspecRef r) noexcept
{
m_top.set_parent(r.m_parent, r.m_ndx_in_parent);
ref_type ref = r.m_parent->get_as_ref(r.m_ndx_in_parent);
init(ref);
}
inline void Spec::init_from_parent() noexcept
{
ref_type ref = m_top.get_ref_from_parent();
init(ref);
}
inline void Spec::destroy() noexcept
{
m_top.destroy_deep();
}
inline size_t Spec::get_ndx_in_parent() const noexcept
{
return m_top.get_ndx_in_parent();
}
inline void Spec::set_ndx_in_parent(size_t ndx) noexcept
{
m_top.set_ndx_in_parent(ndx);
}
inline ref_type Spec::get_ref() const noexcept
{
return m_top.get_ref();
}
inline void Spec::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
{
m_top.set_parent(parent, ndx_in_parent);
}
inline void Spec::rename_column(size_t column_ndx, StringData new_name)
{
REALM_ASSERT(column_ndx < m_types.size());
m_names.set(column_ndx, new_name);
}
inline size_t Spec::get_column_count() const noexcept
{
// This is the total count of columns, including backlinks (not public)
return m_types.size();
}
inline size_t Spec::get_public_column_count() const noexcept
{
// Backlinks are the last columns, and do not have names, so getting
// the number of names gives us the count of user facing columns
return m_names.size();
}
inline ColumnType Spec::get_column_type(size_t ndx) const noexcept
{
REALM_ASSERT(ndx < get_column_count());
return ColumnType(m_types.get(ndx));
}
inline void Spec::set_column_type(size_t column_ndx, ColumnType type)
{
REALM_ASSERT(column_ndx < get_column_count());
// At this point we only support upgrading to string enum
REALM_ASSERT(ColumnType(m_types.get(column_ndx)) == col_type_String);
REALM_ASSERT(type == col_type_StringEnum);
m_types.set(column_ndx, type); // Throws
update_has_strong_link_columns();
}
inline ColumnAttr Spec::get_column_attr(size_t ndx) const noexcept
{
REALM_ASSERT(ndx < get_column_count());
return ColumnAttr(m_attr.get(ndx));
}
inline void Spec::set_column_attr(size_t column_ndx, ColumnAttr attr)
{
REALM_ASSERT(column_ndx < get_column_count());
// At this point we only allow one attr at a time
// so setting it will overwrite existing. In the future
// we will allow combinations.
m_attr.set(column_ndx, attr);
update_has_strong_link_columns();
}
inline StringData Spec::get_column_name(size_t ndx) const noexcept
{
REALM_ASSERT(ndx < get_column_count());
return m_names.get(ndx);
}
inline size_t Spec::get_column_index(StringData name) const noexcept
{
return m_names.find_first(name);
}
inline bool Spec::get_first_column_type_from_ref(ref_type top_ref, Allocator& alloc,
ColumnType& type) noexcept
{
const char* top_header = alloc.translate(top_ref);
ref_type types_ref = to_ref(Array::get(top_header, 0));
const char* types_header = alloc.translate(types_ref);
if (Array::get_size_from_header(types_header) == 0)
return false;
type = ColumnType(Array::get(types_header, 0));
return true;
}
inline bool Spec::has_backlinks() const noexcept
{
// backlinks are always last and do not have names.
return m_names.size() < m_types.size();
// Fixme: It's bad design that backlinks are stored and recognized like this. Backlink columns
// should be a column type like any other, and we should find another way to hide them away from
// the user.
}
// Spec will have a subspec when it contains a column which is one of:
// link, linklist, backlink, or subtable. It is possible for m_top.size()
// to contain an entry for m_subspecs (at index 3) but this reference
// may be empty if the spec contains enumkeys (at index 4) but no subspec types.
inline bool Spec::has_subspec() const noexcept
{
return (m_top.size() >= 4) && (m_top.get_as_ref(3) != 0);
}
inline bool Spec::operator!=(const Spec &s) const noexcept
{
return !(*this == s);
}
inline SubspecRef::SubspecRef(Array* parent, size_t ndx_in_parent) noexcept:
m_parent(parent),
m_ndx_in_parent(ndx_in_parent)
{
}
inline SubspecRef::SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept:
m_parent(const_cast<Array*>(r.m_parent)),
m_ndx_in_parent(r.m_ndx_in_parent)
{
}
inline ConstSubspecRef::ConstSubspecRef(const Array* parent,
size_t ndx_in_parent) noexcept:
m_parent(parent),
m_ndx_in_parent(ndx_in_parent)
{
}
inline ConstSubspecRef::ConstSubspecRef(SubspecRef r) noexcept:
m_parent(r.m_parent),
m_ndx_in_parent(r.m_ndx_in_parent)
{
}
} // namespace realm
#endif // REALM_SPEC_HPP