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_TABLE_HPP
20 #define REALM_TABLE_HPP
29 #include <realm/util/features.h>
30 #include <realm/util/thread.hpp>
31 #include <realm/table_ref.hpp>
32 #include <realm/link_view_fwd.hpp>
33 #include <realm/row.hpp>
34 #include <realm/descriptor_fwd.hpp>
35 #include <realm/spec.hpp>
36 #include <realm/mixed.hpp>
37 #include <realm/query.hpp>
38 #include <realm/column.hpp>
39 #include <realm/column_binary.hpp>
55 class TimestampColumn;
60 struct LinkTargetInfo;
66 typedef Link LinkList;
67 typedef Link BackLink;
79 /// FIXME: Table assignment (from any group to any group) could be made aliasing
80 /// safe as follows: Start by cloning source table into target allocator. On
81 /// success, assign, and then deallocate any previous structure at the target.
83 /// FIXME: It might be desirable to have a 'table move' feature between two
84 /// places inside the same group (say from a subtable or a mixed column to group
85 /// level). This could be done in a very efficient manner.
87 /// FIXME: When compiling in debug mode, all public non-static table functions
88 /// should REALM_ASSERT(is_attached()).
91 /// Construct a new freestanding top-level table with static
94 /// This constructor should be used only when placing a table
95 /// instance on the stack, and it is then the responsibility of
96 /// the application that there are no objects of type TableRef or
97 /// ConstTableRef that refer to it, or to any of its subtables,
98 /// when it goes out of scope. To create a top-level table with
99 /// dynamic lifetime, use Table::create() instead.
100 Table(Allocator& = Allocator::get_default());
102 /// Construct a copy of the specified table as a new freestanding
103 /// top-level table with static lifetime.
105 /// This constructor should be used only when placing a table
106 /// instance on the stack, and it is then the responsibility of
107 /// the application that there are no objects of type TableRef or
108 /// ConstTableRef that refer to it, or to any of its subtables,
109 /// when it goes out of scope. To create a top-level table with
110 /// dynamic lifetime, use Table::copy() instead.
111 Table(const Table&, Allocator& = Allocator::get_default());
115 Allocator& get_alloc() const;
117 /// Construct a new freestanding top-level table with dynamic lifetime.
118 static TableRef create(Allocator& = Allocator::get_default());
120 /// Construct a copy of the specified table as a new freestanding top-level
121 /// table with dynamic lifetime.
122 TableRef copy(Allocator& = Allocator::get_default()) const;
124 /// Returns true if, and only if this accessor is currently attached to an
125 /// underlying table.
127 /// A table accessor may get detached from the underlying row for various
128 /// reasons (see below). When it does, it no longer refers to anything, and
129 /// can no longer be used, except for calling is_attached(). The
130 /// consequences of calling other non-static functions on a detached table
131 /// accessor are unspecified. Table accessors obtained by calling functions in
132 /// the Realm API are always in the 'attached' state immediately upon
133 /// return from those functions.
135 /// A table accessor of a free-standing table never becomes detached (except
136 /// during its eventual destruction). A group-level table accessor becomes
137 /// detached if the underlying table is removed from the group, or when the
138 /// group accessor is destroyed. A subtable accessor becomes detached if the
139 /// underlying subtable is removed, or if the parent table accessor is
140 /// detached. A table accessor does not become detached for any other reason
141 /// than those mentioned here.
143 /// FIXME: High level language bindings will probably want to be able to
144 /// explicitely detach a group and all tables of that group if any modifying
145 /// operation fails (e.g. memory allocation failure) (and something similar
146 /// for freestanding tables) since that leaves the group in state where any
147 /// further access is disallowed. This way they will be able to reliably
148 /// intercept any attempt at accessing such a failed group.
150 /// FIXME: The C++ documentation must state that if any modifying operation
151 /// on a group (incl. tables, subtables, and specs) or on a free standing
152 /// table (incl. subtables and specs) fails, then any further access to that
153 /// group (except ~Group()) or freestanding table (except ~Table()) has
154 /// undefined behaviour and is considered an error on behalf of the
155 /// application. Note that even Table::is_attached() is disallowed in this
157 bool is_attached() const noexcept;
159 /// Get the name of this table, if it has one. Only group-level tables have
160 /// names. For a table of any other kind, this function returns the empty
162 StringData get_name() const noexcept;
164 // Whether or not elements can be null.
165 bool is_nullable(size_t col_ndx) const;
168 /// Conventience functions for inspecting the dynamic table type.
170 /// These functions behave as if they were called on the descriptor returned
171 /// by get_descriptor().
172 size_t get_column_count() const noexcept;
173 DataType get_column_type(size_t column_ndx) const noexcept;
174 StringData get_column_name(size_t column_ndx) const noexcept;
175 size_t get_column_index(StringData name) const noexcept;
179 /// Convenience functions for manipulating the dynamic table type.
181 /// These function must be called only for tables with independent dynamic
182 /// type. A table has independent dynamic type if the function
183 /// has_shared_type() returns false. A table that is a direct member of a
184 /// group has independent dynamic type. So does a free-standing table, and a
185 /// subtable in a column of type 'mixed'. All other tables have shared
186 /// dynamic type. The consequences of calling any of these functions for a
187 /// table with shared dynamic type are undefined.
189 /// Apart from that, these functions behave as if they were called on the
190 /// descriptor returned by get_descriptor(). Note especially that the
191 /// `_link` suffixed functions must be used when inserting link-type
194 /// If you need to change the shared dynamic type of the subtables in a
195 /// subtable column, consider using the API offered by the Descriptor class.
197 /// \sa has_shared_type()
198 /// \sa get_descriptor()
200 size_t add_column(DataType type, StringData name, bool nullable = false, DescriptorRef* subdesc = nullptr);
201 void insert_column(size_t column_ndx, DataType type, StringData name, bool nullable = false,
202 DescriptorRef* subdesc = nullptr);
204 // Todo, these prototypes only exist for backwards compatibility. We should remove them because they are error
205 // prone (optional arguments and implicit bool to null-ptr conversion)
206 size_t add_column(DataType type, StringData name, DescriptorRef* subdesc)
208 return add_column(type, name, false, subdesc);
210 void insert_column(size_t column_ndx, DataType type, StringData name, DescriptorRef* subdesc)
212 insert_column(column_ndx, type, name, false, subdesc);
215 size_t add_column_link(DataType type, StringData name, Table& target, LinkType link_type = link_Weak);
216 void insert_column_link(size_t column_ndx, DataType type, StringData name, Table& target,
217 LinkType link_type = link_Weak);
218 void remove_column(size_t column_ndx);
219 void rename_column(size_t column_ndx, StringData new_name);
223 /// has_search_index() returns true if, and only if a search index has been
224 /// added to the specified column. Rather than throwing, it returns false if
225 /// the table accessor is detached or the specified index is out of range.
227 /// add_search_index() adds a search index to the specified column of the
228 /// table. It has no effect if a search index has already been added to the
229 /// specified column (idempotency).
231 /// remove_search_index() removes the search index from the specified column
232 /// of the table. It has no effect if the specified column has no search
233 /// index. The search index cannot be removed from the primary key of a
236 /// This table must be a root table; that is, it must have an independent
237 /// descriptor. Freestanding tables, group-level tables, and subtables in a
238 /// column of type 'mixed' are all examples of root tables. See add_column()
239 /// for more on this. If you want to manipulate subtable indexes, you must use
240 /// the Descriptor interface.
242 /// \param column_ndx The index of a column of the table.
244 bool has_search_index(size_t column_ndx) const noexcept;
245 void add_search_index(size_t column_ndx);
246 void remove_search_index(size_t column_ndx);
251 /// Get the dynamic type descriptor for this table.
253 /// Every table has an associated descriptor that specifies its dynamic
254 /// type. For simple tables, that is, tables without subtable columns, the
255 /// dynamic type can be inspected and modified directly using member
256 /// functions such as get_column_count() and add_column(). For more complex
257 /// tables, the type is best managed through the associated descriptor
258 /// object which is returned by this function.
260 /// \sa has_shared_type()
261 DescriptorRef get_descriptor();
262 ConstDescriptorRef get_descriptor() const;
266 /// Get the dynamic type descriptor for the column with the
267 /// specified index. That column must have type 'table'.
269 /// This is merely a shorthand for calling `get_subdescriptor(column_ndx)`
270 /// on the descriptor returned by `get_descriptor()`.
271 DescriptorRef get_subdescriptor(size_t column_ndx);
272 ConstDescriptorRef get_subdescriptor(size_t column_ndx) const;
276 /// Get access to an arbitrarily nested dynamic type descriptor.
278 /// The returned descriptor is the one you would get by calling
279 /// Descriptor::get_subdescriptor() once for each entry in the specified
280 /// path, starting with the descriptor returned by get_descriptor(). The
281 /// path is allowed to be empty.
282 typedef std::vector<size_t> path_vec;
283 DescriptorRef get_subdescriptor(const path_vec& path);
284 ConstDescriptorRef get_subdescriptor(const path_vec& path) const;
288 /// Convenience functions for manipulating nested table types.
290 /// These functions behave as if they were called on the descriptor returned
291 /// by `get_subdescriptor(path)`. These function must be called only on
292 /// tables with independent dynamic type.
294 /// \return The value returned by add_subcolumn(), is the index of
295 /// the added column within the descriptor referenced by the
298 /// \sa Descriptor::add_column()
299 /// \sa has_shared_type()
300 size_t add_subcolumn(const path_vec& path, DataType type, StringData name);
301 void insert_subcolumn(const path_vec& path, size_t column_ndx, DataType type, StringData name);
302 void remove_subcolumn(const path_vec& path, size_t column_ndx);
303 void rename_subcolumn(const path_vec& path, size_t column_ndx, StringData new_name);
306 /// Does this table share its type with other tables?
308 /// Tables that are direct members of groups have independent
309 /// dynamic types. The same is true for free-standing tables and
310 /// subtables in coulmns of type 'mixed'. For such tables, this
311 /// function returns false.
313 /// When a table has a column of type 'table', the cells in that
314 /// column contain subtables. All those subtables have the same
315 /// dynamic type, and they share a single type descriptor. For all
316 /// such subtables, this function returns true. See
317 /// Descriptor::is_root() for more on this.
319 /// Please note that Table functions that modify the dynamic type
320 /// directly, such as add_column(), are only allowed to be used on
321 /// tables with non-shared type. If you need to modify a shared
322 /// type, you will have to do that through the descriptor returned
323 /// by get_descriptor(), but note that it will then affect all the
324 /// tables sharing that descriptor.
326 /// \sa get_descriptor()
327 /// \sa Descriptor::is_root()
328 bool has_shared_type() const noexcept;
332 Columns<T> column(size_t column); // FIXME: Should this one have been declared noexcept?
334 Columns<T> column(const Table& origin, size_t origin_column_ndx);
337 SubQuery<T> column(size_t column, Query subquery);
339 SubQuery<T> column(const Table& origin, size_t origin_column_ndx, Query subquery);
341 // Table size and deletion
342 bool is_empty() const noexcept;
343 size_t size() const noexcept;
345 typedef BasicRowExpr<Table> RowExpr;
346 typedef BasicRowExpr<const Table> ConstRowExpr;
348 RowExpr get(size_t row_ndx) noexcept;
349 ConstRowExpr get(size_t row_ndx) const noexcept;
351 RowExpr front() noexcept;
352 ConstRowExpr front() const noexcept;
354 RowExpr back() noexcept;
355 ConstRowExpr back() const noexcept;
357 RowExpr operator[](size_t row_ndx) noexcept;
358 ConstRowExpr operator[](size_t row_ndx) const noexcept;
365 /// remove() removes the specified row from the table and shifts all rows at
366 /// higher index to fill the vacated slot. This operation assumes that the
367 /// table is ordered, and it is therefore allowed only on tables **without**
368 /// link columns, as link columns are only allowed in unordered tables.
370 /// move_last_over() removes the specified row from the table, and if it is
371 /// not the last row in the table, it then moves the last row into the
372 /// vacated slot. This operation assumes that the table is unordered, and it
373 /// may therfore be used on tables with link columns.
375 /// remove_recursive() will delete linked rows if the removed link was the
376 /// last one holding on to the row in question. This will be done recursively.
378 /// The removal of a row from an unordered table (move_last_over()) may
379 /// cause other linked rows to be cascade-removed. The clearing of a table
380 /// may also cause linked rows to be cascade-removed, but in this respect,
381 /// the effect is exactly as if each row had been removed individually. See
382 /// Descriptor::set_link_type() for details.
384 size_t add_empty_row(size_t num_rows = 1);
385 void insert_empty_row(size_t row_ndx, size_t num_rows = 1);
386 size_t add_row_with_key(size_t col_ndx, int64_t key);
387 void remove(size_t row_ndx);
388 void remove_recursive(size_t row_ndx);
390 void move_last_over(size_t row_ndx);
392 void swap_rows(size_t row_ndx_1, size_t row_ndx_2);
393 void move_row(size_t from_ndx, size_t to_ndx);
396 /// Replaces all links to \a row_ndx with links to \a new_row_ndx.
398 /// This operation is usually followed by Table::move_last_over()
399 /// as part of Table::set_int_unique() or Table::set_string_unique()
400 /// or Table::set_null_unique() detecting a collision.
402 /// \sa Table::move_last_over()
403 /// \sa Table::set_int_unique()
404 /// \sa Table::set_string_unique()
405 /// \sa Table::set_null_unique()
406 void merge_rows(size_t row_ndx, size_t new_row_ndx);
411 /// Will assert if the requested type does not match the column type.
413 /// When fetching from a nullable column and the value is null, a default
414 /// value will be returned, except for object like types (StringData,
415 /// BinaryData, Timestamp) which have support for storing nulls. In that
416 /// case, call the `is_null()` method on the returned object to check
417 /// whether the stored value was null. If nullability matters and returning
418 /// a default value is unacceptable, check Table::is_null() before getting a
421 /// \sa Table::is_nullable(size_t col_ndx)
422 /// \sa Table::is_null(size_t col_ndx, size_t row_ndx)
423 /// \sa StringData::is_null()
424 int64_t get_int(size_t column_ndx, size_t row_ndx) const noexcept;
425 bool get_bool(size_t column_ndx, size_t row_ndx) const noexcept;
426 OldDateTime get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept;
427 float get_float(size_t column_ndx, size_t row_ndx) const noexcept;
428 double get_double(size_t column_ndx, size_t row_ndx) const noexcept;
429 StringData get_string(size_t column_ndx, size_t row_ndx) const noexcept;
430 BinaryData get_binary(size_t column_ndx, size_t row_ndx) const noexcept;
431 BinaryIterator get_binary_iterator(size_t column_ndx, size_t row_ndx) const noexcept;
432 Mixed get_mixed(size_t column_ndx, size_t row_ndx) const noexcept;
433 DataType get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept;
434 Timestamp get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept;
438 /// Return data from position 'pos' and onwards. If the blob is distributed
439 /// across multiple arrays, you will only get data from one array. 'pos'
440 /// will be updated to be an index to next available data. It will be 0
442 BinaryData get_binary_at(size_t col_ndx, size_t ndx, size_t& pos) const noexcept;
445 T get(size_t c, size_t r) const noexcept;
447 size_t get_link(size_t column_ndx, size_t row_ndx) const noexcept;
448 bool is_null_link(size_t column_ndx, size_t row_ndx) const noexcept;
449 LinkViewRef get_linklist(size_t column_ndx, size_t row_ndx);
450 ConstLinkViewRef get_linklist(size_t column_ndx, size_t row_ndx) const;
451 size_t get_link_count(size_t column_ndx, size_t row_ndx) const noexcept;
452 bool linklist_is_empty(size_t column_ndx, size_t row_ndx) const noexcept;
453 bool is_null(size_t column_ndx, size_t row_ndx) const noexcept;
455 TableRef get_link_target(size_t column_ndx) noexcept;
456 ConstTableRef get_link_target(size_t column_ndx) const noexcept;
462 /// It is an error to specify a column index, row index, or string position
463 /// that is out of range.
465 /// The number of bytes in a string value must not exceed `max_string_size`,
466 /// and the number of bytes in a binary data value must not exceed
467 /// `max_binary_size`. String must also contain valid UTF-8 encodings. These
468 /// requirements also apply when modifying a string with insert_substring()
469 /// and remove_substring(), and for strings in a mixed columnt. Passing, or
470 /// producing an oversized string or binary data value will cause an
471 /// exception to be thrown.
473 /// The "unique" variants (set_int_unique(), set_string_unique(), set_null_unique())
474 /// are intended to be used in the implementation of primary key support. They
475 /// check if the given column already contains one or more values that are
476 /// equal to \a value, and if there are conflicts, it calls
477 /// Table::merge_rows() for the row_ndx to be replaced by the
478 /// existing row, followed by a Table::move_last_over() of row_ndx. The
479 /// return value is always a row index of a row that contains \a value in
480 /// the specified column, possibly different from \a row_ndx if a conflict
481 /// occurred. Users intending to implement primary keys must therefore
482 /// manually check for duplicates if they want to raise an error instead.
484 /// NOTE: It is an error to call either function after adding elements to a
485 /// linklist in the object. In general, calling set_int_unique() or
486 /// set_string_unique() or set_null_unique() should be the first thing that
487 /// happens after creating a row. These limitations are imposed by limitations
488 /// in the Realm Object Server and may be relaxed in the future. A violation of
489 /// these rules results in a LogicError being thrown.
491 /// add_int() adds a 64-bit signed integer to the current value of the
492 /// cell. If the addition would cause signed integer overflow or
493 /// underflow, the addition "wraps around" with semantics similar to
494 /// unsigned integer arithmetic, such that Table::max_integer + 1 ==
495 /// Table::min_integer and Table::min_integer - 1 == Table::max_integer.
496 /// Note that the wrapping is platform-independent (all platforms wrap in
497 /// the same way regardless of integer representation). If the existing
498 /// value in the cell is null, a LogicError exception is thrown.
500 /// insert_substring() inserts the specified string into the currently
501 /// stored string at the specified position. The position must be less than
502 /// or equal to the size of the currently stored string.
504 /// remove_substring() removes the specified byte range from the currently
505 /// stored string. The beginning of the range (\a pos) must be less than or
506 /// equal to the size of the currently stored string. If the specified range
507 /// extends beyond the end of the currently stored string, it will be
508 /// silently clamped.
510 /// String level modifications performed via insert_substring() and
511 /// remove_substring() are mergable and subject to operational
512 /// transformation. That is, the effect of two causally unrelated
513 /// modifications will in general both be retained during synchronization.
515 static const size_t max_string_size = 0xFFFFF8 - Array::header_size - 1;
516 static const size_t max_binary_size = 0xFFFFF8 - Array::header_size;
518 // FIXME: These limits should be chosen independently of the underlying
519 // platform's choice to define int64_t and independent of the integer
520 // representation. The current values only work for 2's complement, which is
521 // not guaranteed by the standard.
522 static constexpr int_fast64_t max_integer = std::numeric_limits<int64_t>::max();
523 static constexpr int_fast64_t min_integer = std::numeric_limits<int64_t>::min();
526 void set(size_t c, size_t r, T value, bool is_default = false);
529 size_t set_unique(size_t c, size_t r, T value);
531 void set_int(size_t column_ndx, size_t row_ndx, int_fast64_t value, bool is_default = false);
532 size_t set_int_unique(size_t column_ndx, size_t row_ndx, int_fast64_t value);
533 void set_bool(size_t column_ndx, size_t row_ndx, bool value, bool is_default = false);
534 void set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value, bool is_default = false);
535 void set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value, bool is_default = false);
537 void set_enum(size_t column_ndx, size_t row_ndx, E value);
538 void set_float(size_t column_ndx, size_t row_ndx, float value, bool is_default = false);
539 void set_double(size_t column_ndx, size_t row_ndx, double value, bool is_default = false);
540 void set_string(size_t column_ndx, size_t row_ndx, StringData value, bool is_default = false);
541 size_t set_string_unique(size_t column_ndx, size_t row_ndx, StringData value);
542 void set_binary(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false);
543 void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value, bool is_default = false);
544 void set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx, bool is_default = false);
545 void nullify_link(size_t column_ndx, size_t row_ndx);
546 void set_null(size_t column_ndx, size_t row_ndx, bool is_default = false);
547 void set_null_unique(size_t col_ndx, size_t row_ndx);
549 // Sync needs to store blobs bigger than 16 M. This function can be used for that. Data should be read
550 // out again using the get_binary_at() function. Should not be used for user data as normal get_binary()
551 // will just return null if the data is bigger than the limit.
552 void set_binary_big(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false);
554 void add_int(size_t column_ndx, size_t row_ndx, int_fast64_t value);
556 void insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData);
557 void remove_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t substring_size = realm::npos);
561 /// Assumes that the specified column is a subtable column (in
562 /// particular, not a mixed column) and that the specified table
563 /// has a spec that is compatible with that column, that is, the
564 /// number of columns must be the same, and corresponding columns
565 /// must have identical data types (as returned by
566 /// get_column_type()).
567 void set_subtable(size_t col_ndx, size_t row_ndx, const Table*);
568 void set_mixed_subtable(size_t col_ndx, size_t row_ndx, const Table*);
571 // Sub-tables (works on columns whose type is either 'subtable' or
572 // 'mixed', for a value in a mixed column that is not a subtable,
573 // get_subtable() returns null, get_subtable_size() returns zero,
574 // and clear_subtable() replaces the value with an empty table.)
575 // Currently, subtables of subtables are not supported.
576 TableRef get_subtable(size_t column_ndx, size_t row_ndx);
577 ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const;
578 size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept;
579 void clear_subtable(size_t column_ndx, size_t row_ndx);
582 size_t get_backlink_count(size_t row_ndx, bool only_strong_links = false) const noexcept;
583 size_t get_backlink_count(size_t row_ndx, const Table& origin, size_t origin_col_ndx) const noexcept;
584 size_t get_backlink(size_t row_ndx, const Table& origin, size_t origin_col_ndx, size_t backlink_ndx) const
590 /// If this accessor is attached to a subtable, then that subtable has a
591 /// parent table, and the subtable either resides in a column of type
592 /// `table` or of type `mixed` in that parent. In that case
593 /// get_parent_table() returns a reference to the accessor associated with
594 /// the parent, and get_parent_row_index() returns the index of the row in
595 /// which the subtable resides. In all other cases (free-standing and
596 /// group-level tables), get_parent_table() returns null and
597 /// get_parent_row_index() returns realm::npos.
599 /// If this accessor is attached to a subtable, and \a column_ndx_out is
600 /// specified, then `*column_ndx_out` is set to the index of the column of
601 /// the parent table in which the subtable resides. If this accessor is not
602 /// attached to a subtable, then `*column_ndx_out` will retain its original
603 /// value upon return.
605 TableRef get_parent_table(size_t* column_ndx_out = nullptr) noexcept;
606 ConstTableRef get_parent_table(size_t* column_ndx_out = nullptr) const noexcept;
607 size_t get_parent_row_index() const noexcept;
612 /// Only group-level unordered tables can be used as origins or targets of
614 bool is_group_level() const noexcept;
616 /// If this table is a group-level table, then this function returns the
617 /// index of this table within the group. Otherwise it returns realm::npos.
618 size_t get_index_in_group() const noexcept;
620 // Aggregate functions
621 size_t count_int(size_t column_ndx, int64_t value) const;
622 size_t count_string(size_t column_ndx, StringData value) const;
623 size_t count_float(size_t column_ndx, float value) const;
624 size_t count_double(size_t column_ndx, double value) const;
626 int64_t sum_int(size_t column_ndx) const;
627 double sum_float(size_t column_ndx) const;
628 double sum_double(size_t column_ndx) const;
629 int64_t maximum_int(size_t column_ndx, size_t* return_ndx = nullptr) const;
630 float maximum_float(size_t column_ndx, size_t* return_ndx = nullptr) const;
631 double maximum_double(size_t column_ndx, size_t* return_ndx = nullptr) const;
632 OldDateTime maximum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const;
633 Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const;
634 int64_t minimum_int(size_t column_ndx, size_t* return_ndx = nullptr) const;
635 float minimum_float(size_t column_ndx, size_t* return_ndx = nullptr) const;
636 double minimum_double(size_t column_ndx, size_t* return_ndx = nullptr) const;
637 OldDateTime minimum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const;
638 Timestamp minimum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const;
639 double average_int(size_t column_ndx, size_t* value_count = nullptr) const;
640 double average_float(size_t column_ndx, size_t* value_count = nullptr) const;
641 double average_double(size_t column_ndx, size_t* value_count = nullptr) const;
645 size_t find_first(size_t column_ndx, T value) const;
647 size_t find_first_link(size_t target_row_index) const;
648 size_t find_first_int(size_t column_ndx, int64_t value) const;
649 size_t find_first_bool(size_t column_ndx, bool value) const;
650 size_t find_first_olddatetime(size_t column_ndx, OldDateTime value) const;
651 size_t find_first_timestamp(size_t column_ndx, Timestamp value) const;
652 size_t find_first_float(size_t column_ndx, float value) const;
653 size_t find_first_double(size_t column_ndx, double value) const;
654 size_t find_first_string(size_t column_ndx, StringData value) const;
655 size_t find_first_binary(size_t column_ndx, BinaryData value) const;
656 size_t find_first_null(size_t column_ndx) const;
658 TableView find_all_link(size_t target_row_index);
659 ConstTableView find_all_link(size_t target_row_index) const;
660 TableView find_all_int(size_t column_ndx, int64_t value);
661 ConstTableView find_all_int(size_t column_ndx, int64_t value) const;
662 TableView find_all_bool(size_t column_ndx, bool value);
663 ConstTableView find_all_bool(size_t column_ndx, bool value) const;
664 TableView find_all_olddatetime(size_t column_ndx, OldDateTime value);
665 ConstTableView find_all_olddatetime(size_t column_ndx, OldDateTime value) const;
666 TableView find_all_float(size_t column_ndx, float value);
667 ConstTableView find_all_float(size_t column_ndx, float value) const;
668 TableView find_all_double(size_t column_ndx, double value);
669 ConstTableView find_all_double(size_t column_ndx, double value) const;
670 TableView find_all_string(size_t column_ndx, StringData value);
671 ConstTableView find_all_string(size_t column_ndx, StringData value) const;
672 TableView find_all_binary(size_t column_ndx, BinaryData value);
673 ConstTableView find_all_binary(size_t column_ndx, BinaryData value) const;
674 TableView find_all_null(size_t column_ndx);
675 ConstTableView find_all_null(size_t column_ndx) const;
677 /// The following column types are supported: String, Integer, OldDateTime, Bool
678 TableView get_distinct_view(size_t column_ndx);
679 ConstTableView get_distinct_view(size_t column_ndx) const;
681 TableView get_sorted_view(size_t column_ndx, bool ascending = true);
682 ConstTableView get_sorted_view(size_t column_ndx, bool ascending = true) const;
684 TableView get_sorted_view(SortDescriptor order);
685 ConstTableView get_sorted_view(SortDescriptor order) const;
687 TableView get_range_view(size_t begin, size_t end);
688 ConstTableView get_range_view(size_t begin, size_t end) const;
690 TableView get_backlink_view(size_t row_ndx, Table* src_table, size_t src_col_ndx);
693 // Pivot / aggregate operation types. Experimental! Please do not document method publicly.
702 // Simple pivot aggregate method. Experimental! Please do not document method publicly.
703 void aggregate(size_t group_by_column, size_t aggr_column, AggrType op, Table& result,
704 const IntegerColumn* viewrefs = nullptr) const;
706 /// Report the current versioning counter for the table. The versioning counter is guaranteed to
707 /// change when the contents of the table changes after advance_read() or promote_to_write(), or
708 /// immediately after calls to methods which change the table. The term "change" means "change of
709 /// value": The storage layout of the table may change, for example due to optimization, but this
710 /// is not considered a change of a value. This means that you *cannot* use a non-changing version
711 /// count to indicate that object addresses (e.g. strings, binary data) remain the same.
712 /// The versioning counter *may* change (but is not required to do so) when another table linked
713 /// from this table, or linking to this table, is changed. The version counter *may* also change
714 /// without any apparent reason.
715 uint_fast64_t get_version_counter() const noexcept;
719 TableView find_all(size_t column_ndx, T value);
723 /// Find the lower/upper bound according to a column that is
724 /// already sorted in ascending order.
726 /// For an integer column at index 0, and an integer value '`v`',
727 /// lower_bound_int(0,v) returns the index '`l`' of the first row
728 /// such that `get_int(0,l) ≥ v`, and upper_bound_int(0,v)
729 /// returns the index '`u`' of the first row such that
730 /// `get_int(0,u) > v`. In both cases, if no such row is found,
731 /// the returned value is the number of rows in the table.
733 /// 3 3 3 4 4 4 5 6 7 9 9 9
736 /// | | | | -- Lower and upper bound of 15
738 /// | | | -- Lower and upper bound of 8
740 /// | | -- Upper bound of 4
742 /// | -- Lower bound of 4
744 /// -- Lower and upper bound of 1
746 /// These functions are similar to std::lower_bound() and
747 /// std::upper_bound().
749 /// The string versions assume that the column is sorted according
750 /// to StringData::operator<().
751 size_t lower_bound_int(size_t column_ndx, int64_t value) const noexcept;
752 size_t upper_bound_int(size_t column_ndx, int64_t value) const noexcept;
753 size_t lower_bound_bool(size_t column_ndx, bool value) const noexcept;
754 size_t upper_bound_bool(size_t column_ndx, bool value) const noexcept;
755 size_t lower_bound_float(size_t column_ndx, float value) const noexcept;
756 size_t upper_bound_float(size_t column_ndx, float value) const noexcept;
757 size_t lower_bound_double(size_t column_ndx, double value) const noexcept;
758 size_t upper_bound_double(size_t column_ndx, double value) const noexcept;
759 size_t lower_bound_string(size_t column_ndx, StringData value) const noexcept;
760 size_t upper_bound_string(size_t column_ndx, StringData value) const noexcept;
764 // Using where(tv) is the new method to perform queries on TableView. The 'tv' can have any order; it does not
765 // need to be sorted, and, resulting view retains its order.
766 Query where(TableViewBase* tv = nullptr)
768 return Query(*this, tv);
771 // FIXME: We need a ConstQuery class or runtime check against modifications in read transaction.
772 Query where(TableViewBase* tv = nullptr) const
774 return Query(*this, tv);
777 // Perform queries on a LinkView. The returned Query holds a reference to lv.
778 Query where(const LinkViewRef& lv)
780 return Query(*this, lv);
783 Table& link(size_t link_column);
784 Table& backlink(const Table& origin, size_t origin_col_ndx);
786 // Optimizing. enforce == true will enforce enumeration of all string columns;
787 // enforce == false will auto-evaluate if they should be enumerated or not
788 void optimize(bool enforce = false);
790 /// Write this table (or a slice of this table) to the specified
793 /// The output will have the same format as any other Realm
794 /// database file, such as those produced by Group::write(). In
795 /// this case, however, the resulting database file will contain
796 /// exactly one table, and that table will contain only the
797 /// specified slice of the source table (this table).
799 /// The new table will always have the same dynamic type (see
800 /// Descriptor) as the source table (this table), and unless it is
801 /// overridden (\a override_table_name), the new table will have
802 /// the same name as the source table (see get_name()). Indexes
803 /// (see add_search_index()) will not be carried over to the new
806 /// \param out The destination output stream buffer.
808 /// \param offset Index of first row to include (if `slice_size >
809 /// 0`). Must be less than, or equal to size().
811 /// \param slice_size Number of rows to include. May be zero. If
812 /// `slice_size > size() - offset`, then the effective size of
813 /// the written slice will be `size() - offset`.
815 /// \param override_table_name Custom name to write out instead of
816 /// the actual table name.
818 /// \throw std::out_of_range If `offset > size()`.
820 /// FIXME: While this function does provided a maximally efficient
821 /// way of serializing part of a table, it offers little in terms
822 /// of general utility. This is unfortunate, because it pulls
823 /// quite a large amount of code into the core library to support
825 void write(std::ostream& out, size_t offset = 0, size_t slice_size = npos,
826 StringData override_table_name = StringData()) const;
829 void to_json(std::ostream& out, size_t link_depth = 0,
830 std::map<std::string, std::string>* renames = nullptr) const;
831 void to_string(std::ostream& out, size_t limit = 500) const;
832 void row_to_string(size_t row_ndx, std::ostream& out) const;
834 // Get a reference to this table
835 TableRef get_table_ref()
837 return TableRef(this);
839 ConstTableRef get_table_ref() const
841 return ConstTableRef(this);
844 /// \brief Compare two tables for equality.
846 /// Two tables are equal if they have equal descriptors
847 /// (`Descriptor::operator==()`) and equal contents. Equal descriptors imply
848 /// that the two tables have the same columns in the same order. Equal
849 /// contents means that the two tables must have the same number of rows,
850 /// and that for each row index, the two rows must have the same values in
853 /// In mixed columns, both the value types and the values are required to be
856 /// For a particular row and column, if the two values are themselves tables
857 /// (subtable and mixed columns) value equality implies a recursive
858 /// invocation of `Table::operator==()`.
859 bool operator==(const Table&) const;
861 /// \brief Compare two tables for inequality.
863 /// See operator==().
864 bool operator!=(const Table& t) const;
866 /// A subtable in a column of type 'table' (which shares descriptor with
867 /// other subtables in the same column) is initially in a degenerate state
868 /// where it takes up a minimal amout of space. This function returns true
869 /// if, and only if the table accessor is attached to such a subtable. This
870 /// function is mainly intended for debugging purposes.
871 bool is_degenerate() const;
873 /// Compute the sum of the sizes in number of bytes of all the array nodes
874 /// that currently make up this table. See also
875 /// Group::compute_aggregate_byte_size().
877 /// If this table accessor is the detached state, this function returns
879 size_t compute_aggregated_byte_size() const noexcept;
884 void to_dot(std::ostream&, StringData title = StringData()) const;
886 MemStats stats() const;
887 void dump_node_structure() const; // To std::cerr (for GDB)
888 void dump_node_structure(std::ostream&, int level) const;
892 using HandoverPatch = TableHandoverPatch;
893 static void generate_patch(const Table* ref, std::unique_ptr<HandoverPatch>& patch);
894 static TableRef create_from_and_consume_patch(std::unique_ptr<HandoverPatch>& patch, Group& group);
897 /// Get a pointer to the accessor of the specified subtable. The
898 /// accessor will be created if it does not already exist.
900 /// The returned table pointer must **always** end up being
901 /// wrapped in some instantiation of BasicTableRef<>.
902 TableRef get_subtable_tableref(size_t col_ndx, size_t row_ndx);
904 /// See non-const get_subtable_tableref().
905 ConstTableRef get_subtable_tableref(size_t col_ndx, size_t row_ndx) const;
907 /// Compare the rows of two tables under the assumption that the two tables
908 /// have the same number of columns, and the same data type at each column
909 /// index (as expressed through the DataType enum).
910 bool compare_rows(const Table&) const;
912 void set_into_mixed(Table* parent, size_t col_ndx, size_t row_ndx) const;
914 void check_lists_are_empty(size_t row_ndx) const;
919 // Number of rows in this table
922 // Underlying array structure. `m_top` is in use only for root tables; that
923 // is, for tables with independent descriptor. `m_columns` contains a ref
924 // for each column and search index in order of the columns. A search index
925 // ref always occurs immediately after the ref of the column to which the
926 // search index belongs.
928 // A subtable column (a column of type `type_table`) is essentially just a
929 // column of 'refs' pointing to the root node of each subtable.
931 // To save space in the database file, a subtable in such a column always
932 // starts out in a degenerate form where nothing is allocated on its behalf,
933 // and a null 'ref' is stored in the corresponding slot of the column. A
934 // subtable remains in this degenerate state until the first row is added to
937 // For this scheme to work, it must be (and is) possible to create a table
938 // accessor that refers to a degenerate subtable. A table accessor (instance
939 // of `Table`) refers to a degenerate subtable if, and only if `m_columns`
942 // FIXME: The fact that `m_columns` may be detached means that many
943 // functions (even non-modifying functions) need to check for that before
944 // accessing the contents of the table. This incurs a runtime
945 // overhead. Consider whether this overhead can be eliminated by having
946 // `Table::m_columns` always attached to something, and then detect the
947 // degenerate state in a different way.
949 Array m_columns; // 2nd slot in m_top (for root tables)
951 // Management class for the spec object. Only if the table has an independent
952 // spec, the spec object should be deleted when the table object is deleted.
953 // If the table has a shared spec, the spec object is managed by the spec object
954 // of the containing table.
961 void manage(Spec* ptr)
973 SpecPtr& operator=(Spec* ptr)
977 m_is_managed = false;
980 Spec* operator->() const
988 Spec& operator*() const
992 operator bool() const
994 return m_p != nullptr;
996 bool is_managed() const
1002 Spec* m_p = nullptr;
1003 bool m_is_managed = false;
1005 void optionally_delete()
1013 SpecPtr m_spec; // 1st slot in m_top (for root tables)
1015 // Is guaranteed to be empty for a detached accessor. Otherwise it is empty
1016 // when the table accessor is attached to a degenerate subtable (unattached
1017 // `m_columns`), otherwise it contains precisely one column accessor for
1018 // each column in the table, in order.
1020 // In some cases an entry may be null. This is currently possible only in
1021 // connection with Group::advance_transact(), but it means that several
1022 // member functions must be prepared to handle these null entries; in
1023 // particular, detach(), ~Table(), functions called on behalf of detach()
1024 // and ~Table(), and functiones called on behalf of
1025 // Group::advance_transact().
1026 typedef std::vector<ColumnBase*> column_accessors;
1027 column_accessors m_cols;
1029 mutable std::atomic<size_t> m_ref_count;
1031 // If this table is a root table (has independent descriptor),
1032 // then Table::m_descriptor refers to the accessor of its
1033 // descriptor when, and only when the descriptor accessor
1034 // exists. This is used to ensure that at most one descriptor
1035 // accessor exists for each underlying descriptor at any given
1036 // point in time. Subdescriptors are kept unique by means of a
1037 // registry in the parent descriptor. Table::m_descriptor is
1038 // always null for tables with shared descriptor.
1039 mutable std::weak_ptr<Descriptor> m_descriptor;
1041 // Table view instances
1042 // Access needs to be protected by m_accessor_mutex
1043 typedef std::vector<TableViewBase*> views;
1044 mutable views m_views;
1046 // Points to first bound row accessor, or is null if there are none.
1047 mutable RowBase* m_row_accessors = nullptr;
1049 // Mutex which must be locked any time the row accessor chain or m_views is used
1050 mutable util::Mutex m_accessor_mutex;
1052 // Used for queries: Items are added with link() method during buildup of query
1053 mutable std::vector<size_t> m_link_chain;
1055 /// Used only in connection with Group::advance_transact() and
1056 /// Table::refresh_accessor_tree().
1057 mutable bool m_mark;
1059 mutable uint_fast64_t m_version;
1061 void erase_row(size_t row_ndx, bool is_move_last_over);
1062 void batch_erase_rows(const IntegerColumn& row_indexes, bool is_move_last_over);
1063 void do_remove(size_t row_ndx, bool broken_reciprocal_backlinks);
1064 void do_move_last_over(size_t row_ndx, bool broken_reciprocal_backlinks);
1065 void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2);
1066 void do_move_row(size_t from_ndx, size_t to_ndx);
1067 void do_merge_rows(size_t row_ndx, size_t new_row_ndx);
1068 void do_clear(bool broken_reciprocal_backlinks);
1069 size_t do_set_link(size_t col_ndx, size_t row_ndx, size_t target_row_ndx);
1070 template <class ColType, class T>
1071 size_t do_find_unique(ColType& col, size_t ndx, T&& value, bool& conflict);
1072 template <class ColType>
1073 size_t do_set_unique_null(ColType& col, size_t ndx, bool& conflict);
1074 template <class ColType, class T>
1075 size_t do_set_unique(ColType& column, size_t row_ndx, T&& value, bool& conflict);
1077 void _add_search_index(size_t column_ndx);
1078 void _remove_search_index(size_t column_ndx);
1080 void rebuild_search_index(size_t current_file_format_version);
1082 // Upgrades OldDateTime columns to Timestamp columns
1083 void upgrade_olddatetime();
1085 // Indicate that the current global state version has been "observed". Until this
1086 // happens, bumping of the global version counter can be bypassed, as any query
1087 // checking for a version change will see the older version change anyways.
1088 // Also returns the table-local version.
1089 uint64_t observe_version() const noexcept;
1091 /// Update the version of this table and all tables which have links to it.
1092 /// This causes all views referring to those tables to go out of sync, so that
1093 /// calls to sync_if_needed() will bring the view up to date by reexecuting the
1096 /// \param bump_global chooses whether the global versioning counter must be
1097 /// bumped first as part of the update. This is the normal mode of operation,
1098 /// when a change is made to the table. When calling recursively (following links
1099 /// or going to the parent table), the parameter should be set to false to correctly
1100 /// prune traversal.
1101 void bump_version(bool bump_global = true) const noexcept;
1103 /// Disable copying assignment.
1105 /// It could easily be implemented by calling assign(), but the
1106 /// non-checking nature of the low-level dynamically typed API
1107 /// makes it too risky to offer this feature as an
1110 /// FIXME: assign() has not yet been implemented, but the
1111 /// intention is that it will copy the rows of the argument table
1112 /// into this table after clearing the original contents, and for
1113 /// target tables without a shared spec, it would also copy the
1114 /// spec. For target tables with shared spec, it would be an error
1115 /// to pass an argument table with an incompatible spec, but
1116 /// assign() would not check for spec compatibility. This would
1117 /// make it ideal as a basis for implementing operator=() for
1119 Table& operator=(const Table&) = delete;
1121 /// Used when constructing an accessor whose lifetime is going to be managed
1122 /// by reference counting. The lifetime of accessors of free-standing tables
1123 /// allocated on the stack by the application is not managed by reference
1124 /// counting, so that is a case where this tag must **not** be specified.
1125 class ref_count_tag {
1128 /// Create an uninitialized accessor whose lifetime is managed by reference
1130 Table(ref_count_tag, Allocator&);
1132 void init(ref_type top_ref, ArrayParent*, size_t ndx_in_parent, bool skip_create_column_accessors = false);
1133 void init(Spec* shared_spec, ArrayParent* parent_column, size_t parent_row_ndx);
1135 static void do_insert_column(Descriptor&, size_t col_ndx, DataType type, StringData name,
1136 LinkTargetInfo& link_target_info, bool nullable = false);
1137 static void do_insert_column_unless_exists(Descriptor&, size_t col_ndx, DataType type, StringData name,
1138 LinkTargetInfo& link, bool nullable = false,
1139 bool* was_inserted = nullptr);
1140 static void do_erase_column(Descriptor&, size_t col_ndx);
1141 static void do_rename_column(Descriptor&, size_t col_ndx, StringData name);
1143 static void do_add_search_index(Descriptor&, size_t col_ndx);
1144 static void do_remove_search_index(Descriptor&, size_t col_ndx);
1146 struct InsertSubtableColumns;
1147 struct EraseSubtableColumns;
1148 struct RenameSubtableColumns;
1150 void insert_root_column(size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link_target,
1151 bool nullable = false);
1152 void erase_root_column(size_t col_ndx);
1153 void do_insert_root_column(size_t col_ndx, ColumnType, StringData name, bool nullable = false);
1154 void do_erase_root_column(size_t col_ndx);
1155 void do_set_link_type(size_t col_ndx, LinkType);
1156 void insert_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx, size_t backlink_col_ndx);
1157 void erase_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx);
1158 void update_link_target_tables(size_t old_col_ndx_begin, size_t new_col_ndx_begin);
1159 void update_link_target_tables_after_column_move(size_t moved_from, size_t moved_to);
1161 struct SubtableUpdater {
1162 virtual void update(const SubtableColumn&, Array& subcolumns) = 0;
1163 virtual void update_accessor(Table&) = 0;
1164 virtual ~SubtableUpdater()
1168 static void update_subtables(Descriptor&, SubtableUpdater*);
1169 void update_subtables(const size_t* col_path_begin, const size_t* col_path_end, SubtableUpdater*);
1171 struct AccessorUpdater {
1172 virtual void update(Table&) = 0;
1173 virtual void update_parent(Table&) = 0;
1174 virtual ~AccessorUpdater()
1178 void update_accessors(const size_t* col_path_begin, const size_t* col_path_end, AccessorUpdater&);
1180 void create_degen_subtab_columns();
1181 ColumnBase* create_column_accessor(ColumnType, size_t col_ndx, size_t ndx_in_parent);
1182 void destroy_column_accessors() noexcept;
1184 /// Called in the context of Group::commit() to ensure that
1185 /// attached table accessors stay valid across a commit. Please
1186 /// note that this works only for non-transactional commits. Table
1187 /// accessors obtained during a transaction are always detached
1188 /// when the transaction ends.
1189 void update_from_parent(size_t old_baseline) noexcept;
1191 // Support function for conversions
1192 void to_string_header(std::ostream& out, std::vector<size_t>& widths) const;
1193 void to_string_row(size_t row_ndx, std::ostream& out, const std::vector<size_t>& widths) const;
1195 // recursive methods called by to_json, to follow links
1196 void to_json(std::ostream& out, size_t link_depth, std::map<std::string, std::string>& renames,
1197 std::vector<ref_type>& followed) const;
1198 void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth,
1199 std::map<std::string, std::string>& renames, std::vector<ref_type>& followed) const;
1200 void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth = 0,
1201 std::map<std::string, std::string>* renames = nullptr) const;
1203 // Detach accessor from underlying table. Caller must ensure that
1204 // a reference count exists upon return, for example by obtaining
1205 // an extra reference count before the call.
1207 // This function puts this table accessor into the detached
1208 // state. This detaches it from the underlying structure of array
1209 // nodes. It also recursively detaches accessors for subtables,
1210 // and the type descriptor accessor. When this function returns,
1211 // is_attached() will return false.
1213 // This function may be called for a table accessor that is
1214 // already in the detached state (idempotency).
1216 // It is also valid to call this function for a table accessor
1217 // that has not yet been detached, but whose underlying structure
1218 // of arrays have changed in an unpredictable/unknown way. This
1219 // kind of change generally happens when a modifying table
1220 // operation fails, and also when one transaction is ended and a
1221 // new one is started.
1222 void detach() noexcept;
1224 /// Detach and remove all attached row, link list, and subtable
1225 /// accessors. This function does not discard the descriptor accessor, if
1226 /// any, and it does not discard column accessors either.
1227 void discard_child_accessors() noexcept;
1229 void discard_row_accessors() noexcept;
1231 // Detach the type descriptor accessor if it exists.
1232 void discard_desc_accessor() noexcept;
1234 void bind_ptr() const noexcept;
1235 void unbind_ptr() const noexcept;
1237 void register_view(const TableViewBase* view);
1238 void unregister_view(const TableViewBase* view) noexcept;
1239 void move_registered_view(const TableViewBase* old_addr, const TableViewBase* new_addr) noexcept;
1240 void discard_views() noexcept;
1242 void register_row_accessor(RowBase*) const noexcept;
1243 void unregister_row_accessor(RowBase*) const noexcept;
1244 void do_unregister_row_accessor(RowBase*) const noexcept;
1248 ColumnType get_real_column_type(size_t column_ndx) const noexcept;
1250 /// If this table is a group-level table, the parent group is returned,
1251 /// otherwise null is returned.
1252 Group* get_parent_group() const noexcept;
1254 const ColumnBase& get_column_base(size_t column_ndx) const noexcept;
1255 ColumnBase& get_column_base(size_t column_ndx);
1257 const ColumnBaseWithIndex& get_column_base_indexed(size_t ndx) const noexcept;
1258 ColumnBaseWithIndex& get_column_base_indexed(size_t ndx);
1260 template <class T, ColumnType col_type>
1261 T& get_column(size_t ndx);
1263 template <class T, ColumnType col_type>
1264 const T& get_column(size_t ndx) const noexcept;
1266 IntegerColumn& get_column(size_t column_ndx);
1267 const IntegerColumn& get_column(size_t column_ndx) const noexcept;
1268 IntNullColumn& get_column_int_null(size_t column_ndx);
1269 const IntNullColumn& get_column_int_null(size_t column_ndx) const noexcept;
1270 FloatColumn& get_column_float(size_t column_ndx);
1271 const FloatColumn& get_column_float(size_t column_ndx) const noexcept;
1272 DoubleColumn& get_column_double(size_t column_ndx);
1273 const DoubleColumn& get_column_double(size_t column_ndx) const noexcept;
1274 StringColumn& get_column_string(size_t column_ndx);
1275 const StringColumn& get_column_string(size_t column_ndx) const noexcept;
1276 BinaryColumn& get_column_binary(size_t column_ndx);
1277 const BinaryColumn& get_column_binary(size_t column_ndx) const noexcept;
1278 StringEnumColumn& get_column_string_enum(size_t column_ndx);
1279 const StringEnumColumn& get_column_string_enum(size_t column_ndx) const noexcept;
1280 SubtableColumn& get_column_table(size_t column_ndx);
1281 const SubtableColumn& get_column_table(size_t column_ndx) const noexcept;
1282 MixedColumn& get_column_mixed(size_t column_ndx);
1283 const MixedColumn& get_column_mixed(size_t column_ndx) const noexcept;
1284 TimestampColumn& get_column_timestamp(size_t column_ndx);
1285 const TimestampColumn& get_column_timestamp(size_t column_ndx) const noexcept;
1286 const LinkColumnBase& get_column_link_base(size_t ndx) const noexcept;
1287 LinkColumnBase& get_column_link_base(size_t ndx);
1288 const LinkColumn& get_column_link(size_t ndx) const noexcept;
1289 LinkColumn& get_column_link(size_t ndx);
1290 const LinkListColumn& get_column_link_list(size_t ndx) const noexcept;
1291 LinkListColumn& get_column_link_list(size_t ndx);
1292 const BacklinkColumn& get_column_backlink(size_t ndx) const noexcept;
1293 BacklinkColumn& get_column_backlink(size_t ndx);
1295 void verify_column(size_t col_ndx, const ColumnBase* col) const;
1297 void instantiate_before_change();
1298 void validate_column_type(const ColumnBase& col, ColumnType expected_type, size_t ndx) const;
1300 static size_t get_size_from_ref(ref_type top_ref, Allocator&) noexcept;
1301 static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&) noexcept;
1303 const Table* get_parent_table_ptr(size_t* column_ndx_out = nullptr) const noexcept;
1304 Table* get_parent_table_ptr(size_t* column_ndx_out = nullptr) noexcept;
1306 /// Create an empty table with independent spec and return just
1307 /// the reference to the underlying memory.
1308 static ref_type create_empty_table(Allocator&);
1310 /// Create a column of the specified type, fill it with the
1311 /// specified number of default values, and return just the
1312 /// reference to the underlying memory.
1313 static ref_type create_column(ColumnType column_type, size_t num_default_values, bool nullable, Allocator&);
1315 /// Construct a copy of the columns array of this table using the
1316 /// specified allocator and return just the ref to that array.
1318 /// In the clone, no string column will be of the enumeration
1320 ref_type clone_columns(Allocator&) const;
1322 /// Construct a complete copy of this table (including its spec)
1323 /// using the specified allocator and return just the ref to the
1325 ref_type clone(Allocator&) const;
1327 /// True for `col_type_Link` and `col_type_LinkList`.
1328 static bool is_link_type(ColumnType) noexcept;
1330 void connect_opposite_link_columns(size_t link_col_ndx, Table& target_table, size_t backlink_col_ndx) noexcept;
1334 /// Cascading removal of strong links.
1336 /// cascade_break_backlinks_to() removes all backlinks pointing to the row
1337 /// at \a row_ndx. Additionally, if this causes the number of **strong**
1338 /// backlinks originating from a particular opposite row (target row of
1339 /// corresponding forward link) to drop to zero, and that row is not already
1340 /// in \a state.rows, then that row is added to \a state.rows, and
1341 /// cascade_break_backlinks_to() is called recursively for it. This
1342 /// operation is the first half of the cascading row removal operation. The
1343 /// second half is performed by passing the resulting contents of \a
1344 /// state.rows to remove_backlink_broken_rows().
1346 /// Operations that trigger cascading row removal due to explicit removal of
1347 /// one or more rows (the *initiating rows*), should add those rows to \a
1348 /// rows initially, and then call cascade_break_backlinks_to() once for each
1349 /// of them in turn. This is opposed to carrying out the explicit row
1350 /// removals independently, which is also possible, but does require that
1351 /// any initiating rows, that end up in \a state.rows due to link cycles,
1352 /// are removed before passing \a state.rows to
1353 /// remove_backlink_broken_rows(). In the case of clear(), where all rows of
1354 /// a table are explicitly removed, it is better to use
1355 /// cascade_break_backlinks_to_all_rows(), and then carry out the table
1356 /// clearing as an independent step. For operations that trigger cascading
1357 /// row removal for other reasons than explicit row removal, \a state.rows
1358 /// must be empty initially, but cascade_break_backlinks_to() must still be
1359 /// called for each of the initiating rows.
1361 /// When the last non-recursive invocation of cascade_break_backlinks_to()
1362 /// returns, all forward links originating from a row in \a state.rows have
1363 /// had their reciprocal backlinks removed, so remove_backlink_broken_rows()
1364 /// does not perform reciprocal backlink removal at all. Additionally, all
1365 /// remaining backlinks originating from rows in \a state.rows are
1366 /// guaranteed to point to rows that are **not** in \a state.rows. This is
1367 /// true because any backlink that was pointing to a row in \a state.rows
1368 /// has been removed by one of the invocations of
1369 /// cascade_break_backlinks_to(). The set of forward links, that correspond
1370 /// to these remaining backlinks, is precisely the set of forward links that
1371 /// need to be removed/nullified by remove_backlink_broken_rows(), which it
1372 /// does by way of reciprocal forward link removal. Note also, that while
1373 /// all the rows in \a state.rows can have remaining **weak** backlinks
1374 /// originating from them, only the initiating rows in \a state.rows can
1375 /// have remaining **strong** backlinks originating from them. This is true
1376 /// because a non-initiating row is added to \a state.rows only when the
1377 /// last backlink originating from it is lost.
1379 /// Each row removal is replicated individually (as opposed to one
1380 /// replication instruction for the entire cascading operation). This is
1381 /// done because it provides an easy way for Group::advance_transact() to
1382 /// know which tables are affected by the cascade. Note that this has
1383 /// several important consequences: First of all, the replication log
1384 /// receiver must execute the row removal instructions in a non-cascading
1385 /// fashion, meaning that there will be an asymmetry between the two sides
1386 /// in how the effect of the cascade is brought about. While this is fine
1387 /// for simple 1-to-1 replication, it may end up interfering badly with
1388 /// *transaction merging*, when that feature is introduced. Imagine for
1389 /// example that the cascade initiating operation gets canceled during
1390 /// conflict resolution, but some, or all of the induced row removals get to
1391 /// stay. That would break causal consistency. It is important, however, for
1392 /// transaction merging that the cascaded row removals are explicitly
1393 /// mentioned in the replication log, such that they can be used to adjust
1394 /// row indexes during the *operational transform*.
1396 /// cascade_break_backlinks_to_all_rows() has the same affect as calling
1397 /// cascade_break_backlinks_to() once for each row in the table. When
1398 /// calling this function, \a state.stop_on_table must be set to the origin
1399 /// table (origin table of corresponding forward links), and \a
1400 /// state.stop_on_link_list_column must be null.
1402 /// It is immaterial which table remove_backlink_broken_rows() is called on,
1403 /// as long it that table is in the same group as the removed rows.
1405 void cascade_break_backlinks_to(size_t row_ndx, CascadeState& state);
1406 void cascade_break_backlinks_to_all_rows(CascadeState& state);
1407 void remove_backlink_broken_rows(const CascadeState&);
1411 /// Used by query. Follows chain of link columns and returns final target table
1412 const Table* get_link_chain_target(const std::vector<size_t>& link_chain) const;
1414 /// Remove the specified row by the 'move last over' method.
1415 void do_move_last_over(size_t row_ndx);
1417 // Precondition: 1 <= end - begin
1418 size_t* record_subtable_path(size_t* begin, size_t* end) const noexcept;
1420 /// Check if an accessor exists for the specified subtable. If it does,
1421 /// return a pointer to it, otherwise return null. This function assumes
1422 /// that the specified column index in a valid index into `m_cols` but does
1423 /// not otherwise assume more than minimal accessor consistency (see
1424 /// AccessorConsistencyLevels.)
1425 TableRef get_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept;
1427 /// Unless the column accessor is missing, this function returns the
1428 /// accessor for the target table of the specified link-type column. The
1429 /// column accessor is said to be missing if `m_cols[col_ndx]` is null, and
1430 /// this can happen only during certain operations such as the updating of
1431 /// the accessor tree when a read transaction is advanced. Note that for
1432 /// link type columns, the target table accessor exists when, and only when
1433 /// the origin table accessor exists. This function assumes that the
1434 /// specified column index in a valid index into `m_cols` and that the
1435 /// column is a link-type column. Beyond that, it assume nothing more than
1436 /// minimal accessor consistency (see AccessorConsistencyLevels.)
1437 Table* get_link_target_table_accessor(size_t col_ndx) noexcept;
1439 void discard_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept;
1441 void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept;
1442 void adj_acc_erase_row(size_t row_ndx) noexcept;
1443 void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
1444 void adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept;
1445 void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept;
1447 /// Adjust this table accessor and its subordinates after move_last_over()
1448 /// (or its inverse).
1450 /// First, any row, subtable, or link list accessors registered as being at
1451 /// \a to_row_ndx will be detached, as that row is assumed to have been
1452 /// replaced. Next, any row, subtable, or link list accessors registered as
1453 /// being at \a from_row_ndx, will be reregistered as being at \a
1454 /// to_row_ndx, as the row at \a from_row_ndx is assumed to have been moved
1455 /// to \a to_row_ndx.
1457 /// Crucially, if \a to_row_ndx is equal to \a from_row_ndx, then row,
1458 /// subtable, or link list accessors at that row are **still detached**.
1460 /// Additionally, this function causes all link-adjacent tables to be marked
1461 /// (dirty). Two tables are link-adjacent if one is the target table of a
1462 /// link column of the other table. Note that this marking follows these
1463 /// relations in both directions, but only to a depth of one.
1465 /// When this function is used in connection with move_last_over(), set \a
1466 /// to_row_ndx to the index of the row to be removed, and set \a
1467 /// from_row_ndx to the index of the last row in the table. As mentioned
1468 /// earlier, this function can also be used in connection with the **inverse
1469 /// of** move_last_over(), which is an operation that vacates a row by
1470 /// moving its contents into a new last row of the table. In that case, set
1471 /// \a to_row_ndx to one plus the index of the last row in the table, and
1472 /// set \a from_row_ndx to the index of the row to be vacated.
1474 /// This function is used as part of Table::refresh_accessor_tree() to
1475 /// promote the state of the accessors from Minimal Consistency into
1476 /// Structural Correspondence, so it must be able to execute without
1477 /// accessing the underlying array nodes.
1478 void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
1480 void adj_acc_clear_root_table() noexcept;
1481 void adj_acc_clear_nonroot_table() noexcept;
1482 void adj_row_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept;
1483 void adj_row_acc_erase_row(size_t row_ndx) noexcept;
1484 void adj_row_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
1485 void adj_row_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept;
1486 void adj_row_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept;
1488 /// Called by adj_acc_move_over() to adjust row accessors.
1489 void adj_row_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
1491 void adj_insert_column(size_t col_ndx);
1492 void adj_erase_column(size_t col_ndx) noexcept;
1494 bool is_marked() const noexcept;
1495 void mark() noexcept;
1496 void unmark() noexcept;
1497 void recursive_mark() noexcept;
1498 void mark_link_target_tables(size_t col_ndx_begin) noexcept;
1499 void mark_opposite_link_tables() noexcept;
1501 Replication* get_repl() noexcept;
1503 void set_ndx_in_parent(size_t ndx_in_parent) noexcept;
1505 /// Refresh the part of the accessor tree that is rooted at this
1506 /// table. Subtable accessors will be refreshed only if they are marked
1507 /// (Table::m_mark), and this applies recursively to subtables of
1508 /// subtables. All refreshed table accessors (including this one) will be
1509 /// unmarked upon return.
1511 /// The following conditions are necessary and sufficient for the proper
1512 /// operation of this function:
1514 /// - This table must be a group-level table, or a subtable. It must not be
1515 /// a free-standing table (because a free-standing table has no parent).
1517 /// - The `index in parent` property is correct. The `index in parent`
1518 /// property of the table is the `index in parent` property of
1519 /// `m_columns` for subtables with shared descriptor, and the `index in
1520 /// parent` property of `m_top` for all other tables.
1522 /// - If this table has shared descriptor, then the `index in parent`
1523 /// property of the contained spec accessor is correct.
1525 /// - The parent accessor is in a valid state (already refreshed). If the
1526 /// parent is a group, then the group accessor (excluding its table
1527 /// accessors) must be in a valid state. If the parent is a table, then
1528 /// the table accessor (excluding its subtable accessors) must be in a
1531 /// - Every descendant subtable accessor is marked if it needs to be
1532 /// refreshed, or if it has a descendant accessor that needs to be
1535 /// - This table accessor, as well as all its descendant accessors, are in
1536 /// structural correspondence with the underlying node hierarchy whose
1537 /// root ref is stored in the parent (see AccessorConsistencyLevels).
1538 void refresh_accessor_tree();
1540 void refresh_spec_accessor();
1542 void refresh_column_accessors(size_t col_ndx_begin = 0);
1544 // Look for link columns starting from col_ndx_begin.
1545 // If a link column is found, follow the link and update it's
1546 // backlink column accessor if it is in different table.
1547 void refresh_link_target_accessors(size_t col_ndx_begin = 0);
1549 bool is_cross_table_link_target() const noexcept;
1550 std::recursive_mutex* get_parent_accessor_management_lock() const;
1552 void to_dot_internal(std::ostream&) const;
1555 friend class SubtableNode;
1556 friend class _impl::TableFriend;
1558 friend class metrics::QueryInfo;
1560 friend class util::bind_ptr;
1562 friend class SimpleQuerySupport;
1563 friend class LangBindHelper;
1564 friend class TableViewBase;
1566 friend class Columns;
1567 friend class Columns<StringData>;
1568 friend class ParentNode;
1570 friend class SequentialGetter;
1571 friend class RowBase;
1572 friend class LinksToNode;
1573 friend class LinkMap;
1574 friend class LinkView;
1578 class Table::Parent : public ArrayParent {
1580 ~Parent() noexcept override
1585 virtual StringData get_child_name(size_t child_ndx) const noexcept;
1587 /// If children are group-level tables, then this function returns the
1588 /// group. Otherwise it returns null.
1589 virtual Group* get_parent_group() noexcept;
1591 /// If children are subtables, then this function returns the
1592 /// parent table. Otherwise it returns null.
1594 /// If \a column_ndx_out is not null, this function must assign the index of
1595 /// the column within the parent table to `*column_ndx_out` when , and only
1596 /// when this table parent is a column in a parent table.
1597 virtual Table* get_parent_table(size_t* column_ndx_out = nullptr) noexcept;
1599 virtual Spec* get_subtable_spec() noexcept;
1601 /// Must be called whenever a child table accessor is about to be destroyed.
1603 /// Note that the argument is a pointer to the child Table rather than its
1604 /// `ndx_in_parent` property. This is because only minimal accessor
1605 /// consistency can be assumed by this function.
1606 virtual void child_accessor_destroyed(Table* child) noexcept = 0;
1609 virtual size_t* record_subtable_path(size_t* begin, size_t* end) noexcept;
1610 virtual std::recursive_mutex* get_accessor_management_lock() noexcept = 0;
1619 inline uint_fast64_t Table::get_version_counter() const noexcept
1621 return observe_version();
1624 inline uint64_t Table::observe_version() const noexcept
1626 m_top.get_alloc().observe_version();
1630 inline void Table::bump_version(bool bump_global) const noexcept
1633 // This is only set on initial entry through an operation on the same
1634 // table. recursive calls (via parent or via backlinks) must be done
1635 // with bump_global=false.
1636 m_top.get_alloc().bump_global_version();
1638 if (m_top.get_alloc().should_propagate_version(m_version)) {
1639 if (const Table* parent = get_parent_table_ptr())
1640 parent->bump_version(false);
1641 // Recurse through linked tables, use m_mark to avoid infinite recursion
1642 for (auto& column_ptr : m_cols) {
1643 // We may meet a null pointer in place of a backlink column, pending
1644 // replacement with a new one. This can happen ONLY when creation of
1645 // the corresponding forward link column in the origin table is
1646 // pending as well. In this case it is ok to just ignore the zeroed
1647 // backlink column, because the origin table is guaranteed to also
1648 // be refreshed/marked dirty and hence have it's version bumped.
1649 if (column_ptr != nullptr)
1650 column_ptr->bump_link_origin_table_version();
1655 inline void Table::remove(size_t row_ndx)
1657 bool is_move_last_over = false;
1658 erase_row(row_ndx, is_move_last_over); // Throws
1661 inline void Table::move_last_over(size_t row_ndx)
1663 bool is_move_last_over = true;
1664 erase_row(row_ndx, is_move_last_over); // Throws
1667 inline void Table::remove_last()
1673 // A good place to start if you want to understand the memory ordering
1674 // chosen for the operations below is http://preshing.com/20130922/acquire-and-release-fences/
1675 inline void Table::bind_ptr() const noexcept
1677 m_ref_count.fetch_add(1, std::memory_order_relaxed);
1680 inline void Table::unbind_ptr() const noexcept
1682 // The delete operation runs the destructor, and the destructor
1683 // must always see all changes to the object being deleted.
1684 // Within each thread, we know that unbind_ptr will always happen after
1685 // any changes, so it is a convenient place to do a release.
1686 // The release will then be observed by the acquire fence in
1687 // the case where delete is actually called (the count reaches 0)
1688 if (m_ref_count.fetch_sub(1, std::memory_order_release) != 1) {
1692 std::atomic_thread_fence(std::memory_order_acquire);
1694 std::recursive_mutex* lock = get_parent_accessor_management_lock();
1696 std::lock_guard<std::recursive_mutex> lg(*lock);
1697 if (m_ref_count == 0)
1705 inline void Table::register_view(const TableViewBase* view)
1707 util::LockGuard lock(m_accessor_mutex);
1708 // Casting away constness here - operations done on tableviews
1709 // through m_views are all internal and preserving "some" kind
1710 // of logical constness.
1711 m_views.push_back(const_cast<TableViewBase*>(view));
1714 inline bool Table::is_attached() const noexcept
1716 // Note that it is not possible to tie the state of attachment of a table to
1717 // the state of attachment of m_top, because tables with shared spec do not
1718 // have a 'top' array. Neither is it possible to tie it to the state of
1719 // attachment of m_columns, because subtables with shared spec start out in
1720 // a degenerate form where they do not have a 'columns' array. For these
1721 // reasons, it is neccessary to define the notion of attachment for a table
1722 // as follows: A table is attached if, and ony if m_column stores a non-null
1723 // parent pointer. This works because even for degenerate subtables,
1724 // m_columns is initialized with the correct parent pointer.
1725 return m_columns.has_parent();
1728 inline StringData Table::get_name() const noexcept
1730 REALM_ASSERT(is_attached());
1731 const Array& real_top = m_top.is_attached() ? m_top : m_columns;
1732 ArrayParent* parent = real_top.get_parent();
1734 return StringData("");
1735 size_t index_in_parent = real_top.get_ndx_in_parent();
1736 REALM_ASSERT(dynamic_cast<Parent*>(parent));
1737 return static_cast<Parent*>(parent)->get_child_name(index_in_parent);
1740 inline size_t Table::get_column_count() const noexcept
1742 REALM_ASSERT(is_attached());
1743 return m_spec->get_public_column_count();
1746 inline StringData Table::get_column_name(size_t ndx) const noexcept
1748 REALM_ASSERT_3(ndx, <, get_column_count());
1749 return m_spec->get_column_name(ndx);
1752 inline size_t Table::get_column_index(StringData name) const noexcept
1754 REALM_ASSERT(is_attached());
1755 return m_spec->get_column_index(name);
1758 inline ColumnType Table::get_real_column_type(size_t ndx) const noexcept
1760 REALM_ASSERT_3(ndx, <, m_spec->get_column_count());
1761 return m_spec->get_column_type(ndx);
1764 inline DataType Table::get_column_type(size_t ndx) const noexcept
1766 REALM_ASSERT_3(ndx, <, m_spec->get_column_count());
1767 return m_spec->get_public_column_type(ndx);
1770 template <class Col, ColumnType col_type>
1771 inline Col& Table::get_column(size_t ndx)
1773 ColumnBase& col = get_column_base(ndx);
1775 validate_column_type(col, col_type, ndx);
1777 REALM_ASSERT(typeid(Col) == typeid(col));
1778 return static_cast<Col&>(col);
1781 template <class Col, ColumnType col_type>
1782 inline const Col& Table::get_column(size_t ndx) const noexcept
1784 const ColumnBase& col = get_column_base(ndx);
1786 validate_column_type(col, col_type, ndx);
1788 REALM_ASSERT(typeid(Col) == typeid(col));
1789 return static_cast<const Col&>(col);
1792 inline bool Table::has_shared_type() const noexcept
1794 REALM_ASSERT(is_attached());
1795 return !m_top.is_attached();
1798 inline void Table::verify_column(size_t col_ndx, const ColumnBase* col) const
1800 // Check if the column exists at the expected location
1801 if (REALM_LIKELY(col_ndx < m_cols.size() && m_cols[col_ndx] == col))
1803 // The column might be elsewhere in the list
1804 for (auto c : m_cols) {
1808 throw LogicError(LogicError::column_does_not_exist);
1811 class Table::UnbindGuard {
1813 UnbindGuard(Table* table) noexcept
1818 ~UnbindGuard() noexcept
1821 m_table->unbind_ptr();
1824 Table& operator*() const noexcept
1829 Table* operator->() const noexcept
1834 Table* get() const noexcept
1839 Table* release() noexcept
1841 Table* table = m_table;
1851 inline Table::Table(Allocator& alloc)
1855 m_ref_count = 1; // Explicitly managed lifetime
1857 ref_type ref = create_empty_table(alloc); // Throws
1858 Parent* parent = nullptr;
1859 size_t ndx_in_parent = 0;
1860 init(ref, parent, ndx_in_parent);
1863 inline Table::Table(const Table& t, Allocator& alloc)
1867 m_ref_count = 1; // Explicitly managed lifetime
1869 ref_type ref = t.clone(alloc); // Throws
1870 Parent* parent = nullptr;
1871 size_t ndx_in_parent = 0;
1872 init(ref, parent, ndx_in_parent);
1875 inline Table::Table(ref_count_tag, Allocator& alloc)
1879 m_ref_count = 0; // Lifetime managed by reference counting
1882 inline Allocator& Table::get_alloc() const
1884 return m_top.get_alloc();
1887 inline TableRef Table::create(Allocator& alloc)
1889 std::unique_ptr<Table> table(new Table(ref_count_tag(), alloc)); // Throws
1890 ref_type ref = create_empty_table(alloc); // Throws
1891 Parent* parent = nullptr;
1892 size_t ndx_in_parent = 0;
1893 table->init(ref, parent, ndx_in_parent); // Throws
1894 return table.release()->get_table_ref();
1897 inline TableRef Table::copy(Allocator& alloc) const
1899 std::unique_ptr<Table> table(new Table(ref_count_tag(), alloc)); // Throws
1900 ref_type ref = clone(alloc); // Throws
1901 Parent* parent = nullptr;
1902 size_t ndx_in_parent = 0;
1903 table->init(ref, parent, ndx_in_parent); // Throws
1904 return table.release()->get_table_ref();
1907 // For use by queries
1909 inline Columns<T> Table::column(size_t column_ndx)
1911 std::vector<size_t> link_chain = std::move(m_link_chain);
1912 m_link_chain.clear();
1914 // Check if user-given template type equals Realm type. Todo, we should clean up and reuse all our
1915 // type traits (all the is_same() cases below).
1916 const Table* table = get_link_chain_target(link_chain);
1918 realm::DataType ct = table->get_column_type(column_ndx);
1919 if (std::is_same<T, int64_t>::value && ct != type_Int)
1920 throw(LogicError::type_mismatch);
1921 else if (std::is_same<T, bool>::value && ct != type_Bool)
1922 throw(LogicError::type_mismatch);
1923 else if (std::is_same<T, realm::OldDateTime>::value && ct != type_OldDateTime)
1924 throw(LogicError::type_mismatch);
1925 else if (std::is_same<T, float>::value && ct != type_Float)
1926 throw(LogicError::type_mismatch);
1927 else if (std::is_same<T, double>::value && ct != type_Double)
1928 throw(LogicError::type_mismatch);
1930 if (std::is_same<T, Link>::value || std::is_same<T, LinkList>::value || std::is_same<T, BackLink>::value) {
1931 link_chain.push_back(column_ndx);
1934 return Columns<T>(column_ndx, this, std::move(link_chain));
1938 inline Columns<T> Table::column(const Table& origin, size_t origin_col_ndx)
1940 static_assert(std::is_same<T, BackLink>::value, "");
1942 size_t origin_table_ndx = origin.get_index_in_group();
1943 const Table& current_target_table = *get_link_chain_target(m_link_chain);
1944 size_t backlink_col_ndx = current_target_table.m_spec->find_backlink_column(origin_table_ndx, origin_col_ndx);
1946 std::vector<size_t> link_chain = std::move(m_link_chain);
1947 m_link_chain.clear();
1948 link_chain.push_back(backlink_col_ndx);
1950 return Columns<T>(backlink_col_ndx, this, std::move(link_chain));
1954 SubQuery<T> Table::column(size_t column_ndx, Query subquery)
1956 static_assert(std::is_same<T, LinkList>::value, "A subquery must involve a link list or backlink column");
1957 return SubQuery<T>(column<T>(column_ndx), std::move(subquery));
1961 SubQuery<T> Table::column(const Table& origin, size_t origin_col_ndx, Query subquery)
1963 static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
1964 return SubQuery<T>(column<T>(origin, origin_col_ndx), std::move(subquery));
1967 // For use by queries
1968 inline Table& Table::link(size_t link_column)
1970 m_link_chain.push_back(link_column);
1974 inline Table& Table::backlink(const Table& origin, size_t origin_col_ndx)
1976 size_t origin_table_ndx = origin.get_index_in_group();
1977 const Table& current_target_table = *get_link_chain_target(m_link_chain);
1978 size_t backlink_col_ndx = current_target_table.m_spec->find_backlink_column(origin_table_ndx, origin_col_ndx);
1979 return link(backlink_col_ndx);
1982 inline bool Table::is_empty() const noexcept
1987 inline size_t Table::size() const noexcept
1992 inline Table::RowExpr Table::get(size_t row_ndx) noexcept
1994 REALM_ASSERT_3(row_ndx, <, size());
1995 return RowExpr(this, row_ndx);
1998 inline Table::ConstRowExpr Table::get(size_t row_ndx) const noexcept
2000 REALM_ASSERT_3(row_ndx, <, size());
2001 return ConstRowExpr(this, row_ndx);
2004 inline Table::RowExpr Table::front() noexcept
2009 inline Table::ConstRowExpr Table::front() const noexcept
2014 inline Table::RowExpr Table::back() noexcept
2016 return get(m_size - 1);
2019 inline Table::ConstRowExpr Table::back() const noexcept
2021 return get(m_size - 1);
2024 inline Table::RowExpr Table::operator[](size_t row_ndx) noexcept
2026 return get(row_ndx);
2029 inline Table::ConstRowExpr Table::operator[](size_t row_ndx) const noexcept
2031 return get(row_ndx);
2034 inline size_t Table::add_empty_row(size_t num_rows)
2036 size_t row_ndx = m_size;
2037 insert_empty_row(row_ndx, num_rows); // Throws
2038 return row_ndx; // Return index of first new row
2041 inline ConstTableRef Table::get_subtable_tableref(size_t col_ndx, size_t row_ndx) const
2043 return const_cast<Table*>(this)->get_subtable_tableref(col_ndx, row_ndx); // Throws
2046 inline bool Table::is_null_link(size_t col_ndx, size_t row_ndx) const noexcept
2048 return get_link(col_ndx, row_ndx) == realm::npos;
2051 inline ConstTableRef Table::get_link_target(size_t col_ndx) const noexcept
2053 return const_cast<Table*>(this)->get_link_target(col_ndx);
2057 inline void Table::set_enum(size_t column_ndx, size_t row_ndx, E value)
2059 set_int(column_ndx, row_ndx, value);
2062 inline void Table::nullify_link(size_t col_ndx, size_t row_ndx)
2064 set_link(col_ndx, row_ndx, realm::npos);
2067 inline TableRef Table::get_subtable(size_t column_ndx, size_t row_ndx)
2069 return get_subtable_tableref(column_ndx, row_ndx);
2072 inline ConstTableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) const
2074 return get_subtable_tableref(column_ndx, row_ndx);
2077 inline ConstTableRef Table::get_parent_table(size_t* column_ndx_out) const noexcept
2079 return ConstTableRef(get_parent_table_ptr(column_ndx_out));
2082 inline TableRef Table::get_parent_table(size_t* column_ndx_out) noexcept
2084 return TableRef(get_parent_table_ptr(column_ndx_out));
2087 inline bool Table::is_group_level() const noexcept
2089 return bool(get_parent_group());
2092 inline bool Table::operator==(const Table& t) const
2094 return *m_spec == *t.m_spec && compare_rows(t); // Throws
2097 inline bool Table::operator!=(const Table& t) const
2099 return !(*this == t); // Throws
2102 inline bool Table::is_degenerate() const
2104 if (!is_attached()) {
2105 throw LogicError{LogicError::detached_accessor};
2108 return !m_columns.is_attached();
2111 inline void Table::set_into_mixed(Table* parent, size_t col_ndx, size_t row_ndx) const
2113 parent->set_mixed_subtable(col_ndx, row_ndx, this);
2116 inline size_t Table::get_size_from_ref(ref_type top_ref, Allocator& alloc) noexcept
2118 const char* top_header = alloc.translate(top_ref);
2119 std::pair<int_least64_t, int_least64_t> p = Array::get_two(top_header, 0);
2120 ref_type spec_ref = to_ref(p.first), columns_ref = to_ref(p.second);
2121 return get_size_from_ref(spec_ref, columns_ref, alloc);
2124 inline Table* Table::get_parent_table_ptr(size_t* column_ndx_out) noexcept
2126 const Table* parent = const_cast<const Table*>(this)->get_parent_table_ptr(column_ndx_out);
2127 return const_cast<Table*>(parent);
2130 inline bool Table::is_link_type(ColumnType col_type) noexcept
2132 return col_type == col_type_Link || col_type == col_type_LinkList;
2135 inline size_t* Table::record_subtable_path(size_t* begin, size_t* end) const noexcept
2137 const Array& real_top = m_top.is_attached() ? m_top : m_columns;
2138 size_t index_in_parent = real_top.get_ndx_in_parent();
2139 REALM_ASSERT_3(begin, <, end);
2140 *begin++ = index_in_parent;
2141 ArrayParent* parent = real_top.get_parent();
2142 REALM_ASSERT(parent);
2143 REALM_ASSERT(dynamic_cast<Parent*>(parent));
2144 return static_cast<Parent*>(parent)->record_subtable_path(begin, end);
2147 inline size_t* Table::Parent::record_subtable_path(size_t* begin, size_t*) noexcept
2152 inline bool Table::is_marked() const noexcept
2157 inline void Table::mark() noexcept
2162 inline void Table::unmark() noexcept
2167 inline Replication* Table::get_repl() noexcept
2169 return m_top.get_alloc().get_replication();
2172 inline void Table::set_ndx_in_parent(size_t ndx_in_parent) noexcept
2174 if (m_top.is_attached()) {
2175 // Root table (independent descriptor)
2176 m_top.set_ndx_in_parent(ndx_in_parent);
2179 // Subtable with shared descriptor
2180 m_columns.set_ndx_in_parent(ndx_in_parent);
2184 // Declare our explicit specializations so that the inline wrappers don't try
2185 // to instantiate them
2186 template<> int64_t Table::get<int64_t>(size_t, size_t) const noexcept;
2187 template<> util::Optional<int64_t> Table::get<util::Optional<int64_t>>(size_t, size_t) const noexcept;
2188 template<> bool Table::get<bool>(size_t, size_t) const noexcept;
2189 template<> Optional<bool> Table::get<Optional<bool>>(size_t, size_t) const noexcept;
2190 template<> float Table::get<float>(size_t, size_t) const noexcept;
2191 template<> util::Optional<float> Table::get<util::Optional<float>>(size_t, size_t) const noexcept;
2192 template<> double Table::get<double>(size_t, size_t) const noexcept;
2193 template<> util::Optional<double> Table::get<util::Optional<double>>(size_t, size_t) const noexcept;
2194 template<> OldDateTime Table::get<OldDateTime>(size_t, size_t) const noexcept;
2195 template<> Timestamp Table::get<Timestamp>(size_t, size_t) const noexcept;
2196 template<> StringData Table::get<StringData>(size_t, size_t) const noexcept;
2197 template<> BinaryData Table::get<BinaryData>(size_t, size_t) const noexcept;
2198 template<> BinaryIterator Table::get<BinaryIterator>(size_t, size_t) const noexcept;
2199 template<> Mixed Table::get<Mixed>(size_t, size_t) const noexcept;
2201 template<> void Table::set<int64_t>(size_t, size_t, int64_t, bool);
2202 template<> void Table::set<bool>(size_t, size_t, bool, bool);
2203 template<> void Table::set<float>(size_t, size_t, float, bool);
2204 template<> void Table::set<double>(size_t, size_t, double, bool);
2205 template<> void Table::set<OldDateTime>(size_t, size_t, OldDateTime, bool);
2206 template<> void Table::set<Timestamp>(size_t, size_t, Timestamp, bool);
2207 template<> void Table::set<StringData>(size_t, size_t, StringData, bool);
2208 template<> void Table::set<BinaryData>(size_t, size_t, BinaryData, bool);
2209 template<> void Table::set<Mixed>(size_t, size_t, Mixed, bool);
2210 template<> void Table::set<null>(size_t, size_t, null, bool);
2212 template<> size_t Table::set_unique<int64_t>(size_t, size_t, int64_t);
2213 template<> size_t Table::set_unique<StringData>(size_t, size_t, StringData);
2214 template<> size_t Table::set_unique<null>(size_t, size_t, null);
2217 inline int64_t Table::get_int(size_t col_ndx, size_t ndx) const noexcept
2219 if (is_nullable(col_ndx))
2220 return get<util::Optional<int64_t>>(col_ndx, ndx).value_or(0);
2222 return get<int64_t>(col_ndx, ndx);
2225 inline size_t Table::set_int_unique(size_t col_ndx, size_t ndx, int_fast64_t value)
2227 return set_unique(col_ndx, ndx, value);
2230 inline void Table::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, bool is_default)
2232 return set(col_ndx, ndx, value, is_default);
2235 inline Timestamp Table::get_timestamp(size_t col_ndx, size_t ndx) const noexcept
2237 return get<Timestamp>(col_ndx, ndx);
2240 inline void Table::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, bool is_default)
2242 return set(col_ndx, ndx, value, is_default);
2245 inline bool Table::get_bool(size_t col_ndx, size_t ndx) const noexcept
2247 if (is_nullable(col_ndx))
2248 return get<util::Optional<bool>>(col_ndx, ndx).value_or(false);
2250 return get<bool>(col_ndx, ndx);
2253 inline void Table::set_bool(size_t col_ndx, size_t ndx, bool value, bool is_default)
2255 return set(col_ndx, ndx, value, is_default);
2258 inline OldDateTime Table::get_olddatetime(size_t col_ndx, size_t ndx) const noexcept
2260 return get<OldDateTime>(col_ndx, ndx);
2263 inline void Table::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, bool is_default)
2265 return set(col_ndx, ndx, value, is_default);
2268 inline float Table::get_float(size_t col_ndx, size_t ndx) const noexcept
2270 float f = get<float>(col_ndx, ndx);
2271 return null::is_null_float(f) ? 0.0f : f;
2274 inline void Table::set_float(size_t col_ndx, size_t ndx, float value, bool is_default)
2276 return set(col_ndx, ndx, value, is_default);
2279 inline double Table::get_double(size_t col_ndx, size_t ndx) const noexcept
2281 double d = get<double>(col_ndx, ndx);
2282 return null::is_null_float(d) ? 0.0 : d;
2285 inline void Table::set_double(size_t col_ndx, size_t ndx, double value, bool is_default)
2287 return set(col_ndx, ndx, value, is_default);
2290 inline StringData Table::get_string(size_t col_ndx, size_t ndx) const noexcept
2292 return get<StringData>(col_ndx, ndx);
2295 inline void Table::set_string(size_t col_ndx, size_t ndx, StringData value, bool is_default)
2297 return set(col_ndx, ndx, value, is_default);
2300 inline size_t Table::set_string_unique(size_t col_ndx, size_t ndx, StringData value)
2302 return set_unique(col_ndx, ndx, value);
2305 inline BinaryData Table::get_binary(size_t col_ndx, size_t ndx) const noexcept
2307 return get<BinaryData>(col_ndx, ndx);
2310 inline BinaryIterator Table::get_binary_iterator(size_t col_ndx, size_t ndx) const noexcept
2312 return get<BinaryIterator>(col_ndx, ndx);
2315 inline void Table::set_binary(size_t col_ndx, size_t ndx, BinaryData value, bool is_default)
2317 set(col_ndx, ndx, value, is_default);
2320 inline Mixed Table::get_mixed(size_t col_ndx, size_t ndx) const noexcept
2322 return get<Mixed>(col_ndx, ndx);
2325 inline void Table::set_mixed(size_t col_ndx, size_t ndx, Mixed value, bool is_default)
2327 set(col_ndx, ndx, value, is_default);
2330 inline void Table::set_null(size_t col_ndx, size_t ndx, bool is_default)
2332 set(col_ndx, ndx, null(), is_default);
2335 inline void Table::set_null_unique(size_t col_ndx, size_t ndx)
2337 set_unique(col_ndx, ndx, null());
2341 // This class groups together information about the target of a link column
2342 // This is not a valid link if the target table == nullptr
2343 struct LinkTargetInfo {
2344 LinkTargetInfo(Table* target = nullptr, size_t backlink_ndx = realm::npos)
2345 : m_target_table(target)
2346 , m_backlink_col_ndx(backlink_ndx)
2349 bool is_valid() const
2351 return (m_target_table != nullptr);
2353 Table* m_target_table;
2354 size_t m_backlink_col_ndx; // a value of npos indicates the backlink should be appended
2357 // The purpose of this class is to give internal access to some, but
2358 // not all of the non-public parts of the Table class.
2359 class _impl::TableFriend {
2361 typedef Table::UnbindGuard UnbindGuard;
2363 static ref_type create_empty_table(Allocator& alloc)
2365 return Table::create_empty_table(alloc); // Throws
2368 static ref_type clone(const Table& table, Allocator& alloc)
2370 return table.clone(alloc); // Throws
2373 static ref_type clone_columns(const Table& table, Allocator& alloc)
2375 return table.clone_columns(alloc); // Throws
2378 static Table* create_accessor(Allocator& alloc, ref_type top_ref, Table::Parent* parent, size_t ndx_in_parent)
2380 std::unique_ptr<Table> table(new Table(Table::ref_count_tag(), alloc)); // Throws
2381 table->init(top_ref, parent, ndx_in_parent); // Throws
2382 return table.release();
2385 static Table* create_accessor(Spec* shared_spec, Table::Parent* parent_column, size_t parent_row_ndx)
2387 Allocator& alloc = shared_spec->get_alloc();
2388 std::unique_ptr<Table> table(new Table(Table::ref_count_tag(), alloc)); // Throws
2389 table->init(shared_spec, parent_column, parent_row_ndx); // Throws
2390 return table.release();
2393 // Intended to be used only by Group::create_table_accessor()
2394 static Table* create_incomplete_accessor(Allocator& alloc, ref_type top_ref, Table::Parent* parent,
2395 size_t ndx_in_parent)
2397 std::unique_ptr<Table> table(new Table(Table::ref_count_tag(), alloc)); // Throws
2398 bool skip_create_column_accessors = true;
2399 table->init(top_ref, parent, ndx_in_parent, skip_create_column_accessors); // Throws
2400 return table.release();
2403 // Intended to be used only by Group::create_table_accessor()
2404 static void complete_accessor(Table& table)
2406 table.refresh_column_accessors(); // Throws
2409 static void set_top_parent(Table& table, ArrayParent* parent, size_t ndx_in_parent) noexcept
2411 table.m_top.set_parent(parent, ndx_in_parent);
2414 static void update_from_parent(Table& table, size_t old_baseline) noexcept
2416 table.update_from_parent(old_baseline);
2419 static void detach(Table& table) noexcept
2424 static void discard_row_accessors(Table& table) noexcept
2426 table.discard_row_accessors();
2429 static void discard_child_accessors(Table& table) noexcept
2431 table.discard_child_accessors();
2434 static void discard_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept
2436 table.discard_subtable_accessor(col_ndx, row_ndx);
2439 static void bind_ptr(Table& table) noexcept
2444 static void unbind_ptr(Table& table) noexcept
2449 static bool compare_rows(const Table& a, const Table& b)
2451 return a.compare_rows(b); // Throws
2454 static size_t get_size_from_ref(ref_type ref, Allocator& alloc) noexcept
2456 return Table::get_size_from_ref(ref, alloc);
2459 static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator& alloc) noexcept
2461 return Table::get_size_from_ref(spec_ref, columns_ref, alloc);
2464 static Spec& get_spec(Table& table) noexcept
2466 return *table.m_spec;
2469 static const Spec& get_spec(const Table& table) noexcept
2471 return *table.m_spec;
2474 static ColumnBase& get_column(const Table& table, size_t col_ndx)
2476 return *table.m_cols[col_ndx];
2479 static void do_remove(Table& table, size_t row_ndx)
2481 bool broken_reciprocal_backlinks = false;
2482 table.do_remove(row_ndx, broken_reciprocal_backlinks); // Throws
2485 static void do_move_last_over(Table& table, size_t row_ndx)
2487 bool broken_reciprocal_backlinks = false;
2488 table.do_move_last_over(row_ndx, broken_reciprocal_backlinks); // Throws
2491 static void do_swap_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2)
2493 table.do_swap_rows(row_ndx_1, row_ndx_2); // Throws
2496 static void do_move_row(Table& table, size_t from_ndx, size_t to_ndx)
2498 table.do_move_row(from_ndx, to_ndx); // Throws
2501 static void do_merge_rows(Table& table, size_t row_ndx, size_t new_row_ndx)
2503 table.do_merge_rows(row_ndx, new_row_ndx); // Throws
2506 static void do_clear(Table& table)
2508 bool broken_reciprocal_backlinks = false;
2509 table.do_clear(broken_reciprocal_backlinks); // Throws
2512 static void do_set_link(Table& table, size_t col_ndx, size_t row_ndx, size_t target_row_ndx)
2514 table.do_set_link(col_ndx, row_ndx, target_row_ndx); // Throws
2517 static size_t get_backlink_count(const Table& table, size_t row_ndx, bool only_strong_links) noexcept
2519 return table.get_backlink_count(row_ndx, only_strong_links);
2522 static void cascade_break_backlinks_to(Table& table, size_t row_ndx, CascadeState& state)
2524 table.cascade_break_backlinks_to(row_ndx, state); // Throws
2527 static void remove_backlink_broken_rows(Table& table, const CascadeState& rows)
2529 table.remove_backlink_broken_rows(rows); // Throws
2532 static size_t* record_subtable_path(const Table& table, size_t* begin, size_t* end) noexcept
2534 return table.record_subtable_path(begin, end);
2537 static void insert_column(Descriptor& desc, size_t column_ndx, DataType type, StringData name,
2538 LinkTargetInfo& link, bool nullable = false)
2540 Table::do_insert_column(desc, column_ndx, type, name, link, nullable); // Throws
2543 static void insert_column_unless_exists(Descriptor& desc, size_t column_ndx, DataType type, StringData name,
2544 LinkTargetInfo link, bool nullable = false, bool* was_inserted = nullptr)
2546 Table::do_insert_column_unless_exists(desc, column_ndx, type, name, link, nullable, was_inserted); // Throws
2549 static void erase_column(Descriptor& desc, size_t column_ndx)
2551 Table::do_erase_column(desc, column_ndx); // Throws
2554 static void rename_column(Descriptor& desc, size_t column_ndx, StringData name)
2556 Table::do_rename_column(desc, column_ndx, name); // Throws
2559 static void add_search_index(Descriptor& desc, size_t column_ndx)
2561 Table::do_add_search_index(desc, column_ndx); // Throws
2564 static void remove_search_index(Descriptor& desc, size_t column_ndx)
2566 Table::do_remove_search_index(desc, column_ndx); // Throws
2569 static void set_link_type(Table& table, size_t column_ndx, LinkType link_type)
2571 table.do_set_link_type(column_ndx, link_type); // Throws
2574 static void erase_row(Table& table, size_t row_ndx, bool is_move_last_over)
2576 table.erase_row(row_ndx, is_move_last_over); // Throws
2579 static void batch_erase_rows(Table& table, const IntegerColumn& row_indexes, bool is_move_last_over)
2581 table.batch_erase_rows(row_indexes, is_move_last_over); // Throws
2584 static TableRef get_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept
2586 return table.get_subtable_accessor(col_ndx, row_ndx);
2589 static const Table* get_link_target_table_accessor(const Table& table, size_t col_ndx) noexcept
2591 return const_cast<Table&>(table).get_link_target_table_accessor(col_ndx);
2594 static Table* get_link_target_table_accessor(Table& table, size_t col_ndx) noexcept
2596 return table.get_link_target_table_accessor(col_ndx);
2599 static void adj_acc_insert_rows(Table& table, size_t row_ndx, size_t num_rows) noexcept
2601 table.adj_acc_insert_rows(row_ndx, num_rows);
2604 static void adj_acc_erase_row(Table& table, size_t row_ndx) noexcept
2606 table.adj_acc_erase_row(row_ndx);
2609 static void adj_acc_swap_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept
2611 table.adj_acc_swap_rows(row_ndx_1, row_ndx_2);
2614 static void adj_acc_move_row(Table& table, size_t from_ndx, size_t to_ndx) noexcept
2616 table.adj_acc_move_row(from_ndx, to_ndx);
2619 static void adj_acc_merge_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept
2621 table.adj_acc_merge_rows(row_ndx_1, row_ndx_2);
2624 static void adj_acc_move_over(Table& table, size_t from_row_ndx, size_t to_row_ndx) noexcept
2626 table.adj_acc_move_over(from_row_ndx, to_row_ndx);
2629 static void adj_acc_clear_root_table(Table& table) noexcept
2631 table.adj_acc_clear_root_table();
2634 static void adj_acc_clear_nonroot_table(Table& table) noexcept
2636 table.adj_acc_clear_nonroot_table();
2639 static void adj_insert_column(Table& table, size_t col_ndx)
2641 table.adj_insert_column(col_ndx); // Throws
2644 static void adj_add_column(Table& table)
2646 size_t num_cols = table.m_cols.size();
2647 table.adj_insert_column(num_cols); // Throws
2650 static void adj_erase_column(Table& table, size_t col_ndx) noexcept
2652 table.adj_erase_column(col_ndx);
2655 static bool is_marked(const Table& table) noexcept
2657 return table.is_marked();
2660 static void mark(Table& table) noexcept
2665 static void unmark(Table& table) noexcept
2670 static void recursive_mark(Table& table) noexcept
2672 table.recursive_mark();
2675 static void mark_link_target_tables(Table& table, size_t col_ndx_begin) noexcept
2677 table.mark_link_target_tables(col_ndx_begin);
2680 static void mark_opposite_link_tables(Table& table) noexcept
2682 table.mark_opposite_link_tables();
2685 static DescriptorRef get_root_table_desc_accessor(Table& root_table) noexcept
2687 return root_table.m_descriptor.lock();
2690 typedef Table::AccessorUpdater AccessorUpdater;
2691 static void update_accessors(Table& table, const size_t* col_path_begin, const size_t* col_path_end,
2692 AccessorUpdater& updater)
2694 table.update_accessors(col_path_begin, col_path_end, updater); // Throws
2697 static void refresh_accessor_tree(Table& table)
2699 table.refresh_accessor_tree(); // Throws
2702 static void refresh_spec_accessor(Table& table)
2704 table.refresh_spec_accessor(); // Throws
2707 static void set_ndx_in_parent(Table& table, size_t ndx_in_parent) noexcept
2709 table.set_ndx_in_parent(ndx_in_parent);
2712 static bool is_link_type(ColumnType type) noexcept
2714 return Table::is_link_type(type);
2717 static void bump_version(Table& table, bool bump_global = true) noexcept
2719 table.bump_version(bump_global);
2722 static bool is_cross_table_link_target(const Table& table)
2724 return table.is_cross_table_link_target();
2727 static Group* get_parent_group(const Table& table) noexcept
2729 return table.get_parent_group();
2732 static Replication* get_repl(Table& table) noexcept
2734 return table.get_repl();
2737 static void register_view(Table& table, const TableViewBase* view)
2739 table.register_view(view); // Throws
2742 static void unregister_view(Table& table, const TableViewBase* view) noexcept
2744 table.unregister_view(view);
2749 } // namespace realm
2751 #endif // REALM_TABLE_HPP