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_IMPL_TRANSACT_LOG_HPP
20 #define REALM_IMPL_TRANSACT_LOG_HPP
24 #include <realm/string_data.hpp>
25 #include <realm/data_type.hpp>
26 #include <realm/binary_data.hpp>
27 #include <realm/olddatetime.hpp>
28 #include <realm/mixed.hpp>
29 #include <realm/util/buffer.hpp>
30 #include <realm/util/string_buffer.hpp>
31 #include <realm/impl/input_stream.hpp>
33 #include <realm/group.hpp>
34 #include <realm/descriptor.hpp>
35 #include <realm/link_view.hpp>
40 /// Transaction log instruction encoding
41 /// NOTE: Any change to this enum is a file-format breaking change.
43 instr_InsertGroupLevelTable = 1,
44 instr_EraseGroupLevelTable = 2, // Remove columnless table from group
45 instr_RenameGroupLevelTable = 3,
46 instr_MoveGroupLevelTable = 4, // UNSUPPORTED/UNUSED. FIXME: remove in next breaking change
47 instr_SelectTable = 5,
51 instr_AddInteger = 9, // Add value to integer field
52 instr_NullifyLink = 10, // Set link to null due to target being erased
53 instr_InsertSubstring = 11,
54 instr_EraseFromString = 12,
55 instr_InsertEmptyRows = 13,
56 instr_EraseRows = 14, // Remove (multiple) rows
59 instr_MergeRows = 17, // Replace links pointing to row A with links to row B
60 instr_ClearTable = 18, // Remove all rows in selected table
61 instr_OptimizeTable = 19,
62 instr_SelectDescriptor = 20, // Select descriptor from currently selected root table
64 21, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn)
65 instr_InsertLinkColumn = 22, // do, but for a link-type column
66 instr_InsertNullableColumn = 23, // Insert nullable column
67 instr_EraseColumn = 24, // Remove column from selected descriptor
68 instr_EraseLinkColumn = 25, // Remove link-type column from selected descriptor
69 instr_RenameColumn = 26, // Rename column in selected descriptor
70 instr_MoveColumn = 27, // Move column in selected descriptor (UNSUPPORTED/UNUSED) FIXME: remove
71 instr_AddSearchIndex = 28, // Add a search index to a column
72 instr_RemoveSearchIndex = 29, // Remove a search index from a column
73 instr_SetLinkType = 30, // Strong/weak
74 instr_SelectLinkList = 31,
75 instr_LinkListSet = 32, // Assign to link list entry
76 instr_LinkListInsert = 33, // Insert entry into link list
77 instr_LinkListMove = 34, // Move an entry within a link list
78 instr_LinkListSwap = 35, // Swap two entries within a link list
79 instr_LinkListErase = 36, // Remove an entry from a link list
80 instr_LinkListNullify = 37, // Remove an entry from a link list due to linked row being erased
81 instr_LinkListClear = 38, // Ramove all entries from a link list
82 instr_LinkListSetAll = 39, // Assign to link list entry
83 instr_AddRowWithKey = 40, // Insert a row with a given key
86 class TransactLogStream {
88 /// Ensure contiguous free space in the transaction log
89 /// buffer. This method must update `out_free_begin`
90 /// and `out_free_end` such that they refer to a chunk
91 /// of free space whose size is at least \a n.
93 /// \param n The required amount of contiguous free space. Must be
94 /// small (probably not greater than 1024)
95 /// \param n Must be small (probably not greater than 1024)
96 virtual void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) = 0;
98 /// Copy the specified data into the transaction log buffer. This
99 /// function should be called only when the specified data does
100 /// not fit inside the chunk of free space currently referred to
101 /// by `out_free_begin` and `out_free_end`.
103 /// This method must update `out_begin` and
104 /// `out_end` such that, upon return, they still
105 /// refer to a (possibly empty) chunk of free space.
106 virtual void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) = 0;
109 class TransactLogBufferStream : public TransactLogStream {
111 void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) override;
112 void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) override;
114 const char* transact_log_data() const;
116 util::Buffer<char> m_buffer;
120 // LCOV_EXCL_START (because the NullInstructionObserver is trivial)
121 class NullInstructionObserver {
123 /// The following methods are also those that TransactLogParser expects
124 /// to find on the `InstructionHandler`.
126 // No selection needed:
127 bool select_table(size_t, size_t, const size_t*)
131 bool select_descriptor(size_t, const size_t*)
135 bool select_link_list(size_t, size_t, size_t)
139 bool insert_group_level_table(size_t, size_t, StringData)
143 bool erase_group_level_table(size_t, size_t)
147 bool rename_group_level_table(size_t, StringData)
152 // Must have table selected:
153 bool insert_empty_rows(size_t, size_t, size_t, bool)
157 bool add_row_with_key(size_t, size_t, size_t, int64_t)
161 bool erase_rows(size_t, size_t, size_t, bool)
165 bool swap_rows(size_t, size_t)
169 bool move_row(size_t, size_t)
173 bool merge_rows(size_t, size_t)
177 bool clear_table(size_t)
181 bool set_int(size_t, size_t, int_fast64_t, Instruction, size_t)
185 bool add_int(size_t, size_t, int_fast64_t)
189 bool set_bool(size_t, size_t, bool, Instruction)
193 bool set_float(size_t, size_t, float, Instruction)
197 bool set_double(size_t, size_t, double, Instruction)
201 bool set_string(size_t, size_t, StringData, Instruction, size_t)
205 bool set_binary(size_t, size_t, BinaryData, Instruction)
209 bool set_olddatetime(size_t, size_t, OldDateTime, Instruction)
213 bool set_timestamp(size_t, size_t, Timestamp, Instruction)
217 bool set_table(size_t, size_t, Instruction)
221 bool set_mixed(size_t, size_t, const Mixed&, Instruction)
225 bool set_link(size_t, size_t, size_t, size_t, Instruction)
229 bool set_null(size_t, size_t, Instruction, size_t)
233 bool nullify_link(size_t, size_t, size_t)
237 bool insert_substring(size_t, size_t, size_t, StringData)
241 bool erase_substring(size_t, size_t, size_t, size_t)
245 bool optimize_table()
250 // Must have descriptor selected:
251 bool insert_link_column(size_t, DataType, StringData, size_t, size_t)
255 bool insert_column(size_t, DataType, StringData, bool)
259 bool erase_link_column(size_t, size_t, size_t)
263 bool erase_column(size_t)
267 bool rename_column(size_t, StringData)
271 bool add_search_index(size_t)
275 bool remove_search_index(size_t)
279 bool set_link_type(size_t, LinkType)
284 // Must have linklist selected:
285 bool link_list_set(size_t, size_t, size_t)
289 bool link_list_insert(size_t, size_t, size_t)
293 bool link_list_move(size_t, size_t)
297 bool link_list_swap(size_t, size_t)
301 bool link_list_erase(size_t, size_t)
305 bool link_list_nullify(size_t, size_t)
309 bool link_list_clear(size_t)
314 void parse_complete()
318 // LCOV_EXCL_STOP (NullInstructionObserver)
321 /// See TransactLogConvenientEncoder for information about the meaning of the
322 /// arguments of each of the functions in this class.
323 class TransactLogEncoder {
325 /// The following methods are also those that TransactLogParser expects
326 /// to find on the `InstructionHandler`.
328 // No selection needed:
329 bool select_table(size_t group_level_ndx, size_t levels, const size_t* path);
330 bool select_descriptor(size_t levels, const size_t* path);
331 bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx);
332 bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name);
333 bool erase_group_level_table(size_t table_ndx, size_t num_tables);
334 bool rename_group_level_table(size_t table_ndx, StringData new_name);
336 /// Must have table selected.
337 bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered);
338 bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, int64_t key);
339 bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered);
340 bool swap_rows(size_t row_ndx_1, size_t row_ndx_2);
341 bool move_row(size_t from_ndx, size_t to_ndx);
342 bool merge_rows(size_t row_ndx, size_t new_row_ndx);
343 bool clear_table(size_t old_table_size);
345 bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t, Instruction = instr_Set, size_t = 0);
346 bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t);
347 bool set_bool(size_t col_ndx, size_t row_ndx, bool, Instruction = instr_Set);
348 bool set_float(size_t col_ndx, size_t row_ndx, float, Instruction = instr_Set);
349 bool set_double(size_t col_ndx, size_t row_ndx, double, Instruction = instr_Set);
350 bool set_string(size_t col_ndx, size_t row_ndx, StringData, Instruction = instr_Set, size_t = 0);
351 bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData, Instruction = instr_Set);
352 bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime, Instruction = instr_Set);
353 bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp, Instruction = instr_Set);
354 bool set_table(size_t col_ndx, size_t row_ndx, Instruction = instr_Set);
355 bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed&, Instruction = instr_Set);
356 bool set_link(size_t col_ndx, size_t row_ndx, size_t, size_t target_group_level_ndx, Instruction = instr_Set);
357 bool set_null(size_t col_ndx, size_t row_ndx, Instruction = instr_Set, size_t = 0);
358 bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx);
359 bool insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData);
360 bool erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size);
361 bool optimize_table();
363 // Must have descriptor selected:
364 bool insert_link_column(size_t col_ndx, DataType, StringData name, size_t link_target_table_ndx,
365 size_t backlink_col_ndx);
366 bool insert_column(size_t col_ndx, DataType, StringData name, bool nullable = false);
367 bool erase_link_column(size_t col_ndx, size_t link_target_table_ndx, size_t backlink_col_ndx);
368 bool erase_column(size_t col_ndx);
369 bool rename_column(size_t col_ndx, StringData new_name);
370 bool add_search_index(size_t col_ndx);
371 bool remove_search_index(size_t col_ndx);
372 bool set_link_type(size_t col_ndx, LinkType);
374 // Must have linklist selected:
375 bool link_list_set(size_t link_ndx, size_t value, size_t prior_size);
376 bool link_list_set_all(const IntegerColumn& values);
377 bool link_list_insert(size_t link_ndx, size_t value, size_t prior_size);
378 bool link_list_move(size_t from_link_ndx, size_t to_link_ndx);
379 bool link_list_swap(size_t link1_ndx, size_t link2_ndx);
380 bool link_list_erase(size_t link_ndx, size_t prior_size);
381 bool link_list_nullify(size_t link_ndx, size_t prior_size);
382 bool link_list_clear(size_t old_list_size);
384 /// End of methods expected by parser.
387 TransactLogEncoder(TransactLogStream& out_stream);
388 void set_buffer(char* new_free_begin, char* new_free_end);
389 char* write_position() const
391 return m_transact_log_free_begin;
395 using IntegerList = std::tuple<IntegerColumnIterator, IntegerColumnIterator>;
396 using UnsignedList = std::tuple<const size_t*, const size_t*>;
398 // Make sure this is in agreement with the actual integer encoding
399 // scheme (see encode_int()).
400 static constexpr int max_enc_bytes_per_int = 10;
401 static constexpr int max_enc_bytes_per_double = sizeof(double);
402 static constexpr int max_enc_bytes_per_num =
403 max_enc_bytes_per_int < max_enc_bytes_per_double ? max_enc_bytes_per_double : max_enc_bytes_per_int;
404 // Space is reserved in chunks to avoid excessive over allocation.
406 static constexpr int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode
408 static constexpr int max_numbers_per_chunk = 8;
411 // This value is used in Set* instructions in place of the 'type' field in
412 // the stream to indicate that the value of the Set* instruction is NULL,
413 // which doesn't have a type.
414 static constexpr int set_null_sentinel()
419 TransactLogStream& m_stream;
421 // These two delimit a contiguous region of free space in a
422 // transaction log buffer following the last written data. It may
424 char* m_transact_log_free_begin = nullptr;
425 char* m_transact_log_free_end = nullptr;
427 char* reserve(size_t size);
428 /// \param ptr Must be in the range [m_transact_log_free_begin, m_transact_log_free_end]
429 void advance(char* ptr) noexcept;
434 size_t max_size_list()
439 template <class T, class... Args>
440 size_t max_size_list(T val, Args... args)
442 return max_size(val) + max_size_list(args...);
446 char* encode(char* ptr, T value);
448 char* encode_list(char* ptr)
454 template <class T, class... Args>
455 char* encode_list(char* ptr, T value, Args... args)
457 return encode_list(encode(ptr, value), args...);
460 template <class... L>
461 void append_simple_instr(L... numbers);
463 template <class... L>
464 void append_mixed_instr(Instruction instr, const Mixed& value, L... numbers);
467 static char* encode_int(char*, T value);
468 friend class TransactLogParser;
471 class TransactLogConvenientEncoder {
473 virtual void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name);
474 virtual void erase_group_level_table(size_t table_ndx, size_t num_tables);
475 virtual void rename_group_level_table(size_t table_ndx, StringData new_name);
476 virtual void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link,
477 bool nullable = false);
478 virtual void erase_column(const Descriptor&, size_t col_ndx);
479 virtual void rename_column(const Descriptor&, size_t col_ndx, StringData name);
481 virtual void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set);
482 virtual void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value);
483 virtual void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set);
484 virtual void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set);
485 virtual void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set);
486 virtual void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set);
487 virtual void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set);
488 virtual void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value,
489 Instruction variant = instr_Set);
490 virtual void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set);
491 virtual void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set);
492 virtual void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set);
493 virtual void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set);
494 virtual void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set);
495 virtual void set_link_list(const LinkView&, const IntegerColumn& values);
496 virtual void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData);
497 virtual void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size);
499 /// \param prior_num_rows The number of rows in the table prior to the
501 virtual void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows);
502 virtual void add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx,
505 /// \param prior_num_rows The number of rows in the table prior to the
507 virtual void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
508 bool is_move_last_over);
510 virtual void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2);
511 virtual void move_row(const Table*, size_t from_ndx, size_t to_ndx);
512 virtual void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx);
513 virtual void add_search_index(const Descriptor&, size_t col_ndx);
514 virtual void remove_search_index(const Descriptor&, size_t col_ndx);
515 virtual void set_link_type(const Table*, size_t col_ndx, LinkType);
516 virtual void clear_table(const Table*, size_t prior_num_rows);
517 virtual void optimize_table(const Table*);
519 virtual void link_list_set(const LinkView&, size_t link_ndx, size_t value);
520 virtual void link_list_insert(const LinkView&, size_t link_ndx, size_t value);
521 virtual void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx);
522 virtual void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2);
523 virtual void link_list_erase(const LinkView&, size_t link_ndx);
524 virtual void link_list_clear(const LinkView&);
528 /// Implicit nullifications due to removal of target row. This is redundant
529 /// information from the point of view of replication, as the removal of the
530 /// target row will reproduce the implicit nullifications in the target
531 /// Realm anyway. The purpose of this instruction is to allow observers
532 /// (reactor pattern) to be explicitly notified about the implicit
535 virtual void nullify_link(const Table*, size_t col_ndx, size_t ndx);
536 virtual void link_list_nullify(const LinkView&, size_t link_ndx);
540 void on_table_destroyed(const Table*) noexcept;
541 void on_spec_destroyed(const Spec*) noexcept;
542 void on_link_list_destroyed(const LinkView&) noexcept;
545 TransactLogConvenientEncoder(TransactLogStream& encoder);
547 void reset_selection_caches() noexcept;
548 void set_buffer(char* new_free_begin, char* new_free_end)
550 m_encoder.set_buffer(new_free_begin, new_free_end);
552 char* write_position() const
554 return m_encoder.write_position();
558 TransactLogEncoder m_encoder;
559 // These are mutable because they are caches.
560 mutable util::Buffer<size_t> m_subtab_path_buf;
561 mutable const Table* m_selected_table;
562 mutable const Spec* m_selected_spec;
563 // Has to be atomic to support concurrent reset when a linklist
564 // is unselected. This can happen on a different thread. In case
565 // of races, setting of a new value must win.
566 mutable std::atomic<const LinkView*> m_selected_link_list;
568 void unselect_all() noexcept;
569 void select_table(const Table*); // unselects descriptor and link list
570 void select_desc(const Descriptor&); // unselects link list
571 void select_link_list(const LinkView&); // unselects descriptor
573 void record_subtable_path(const Table&, size_t*& out_begin, size_t*& out_end);
574 void do_select_table(const Table*);
575 void do_select_desc(const Descriptor&);
576 void do_select_link_list(const LinkView&);
578 friend class TransactReverser;
582 class TransactLogParser {
584 class BadTransactLog; // Exception
587 ~TransactLogParser() noexcept;
589 /// See `TransactLogEncoder` for a list of methods that the `InstructionHandler` must define.
590 /// parse() promises that the path passed by reference to
591 /// InstructionHandler::select_descriptor() will remain valid
592 /// during subsequent calls to all descriptor modifying functions.
593 template <class InstructionHandler>
594 void parse(InputStream&, InstructionHandler&);
596 template <class InstructionHandler>
597 void parse(NoCopyInputStream&, InstructionHandler&);
600 util::Buffer<char> m_input_buffer;
602 // The input stream is assumed to consist of chunks of memory organised such that
603 // every instruction resides in a single chunk only.
604 NoCopyInputStream* m_input;
605 // pointer into transaction log, each instruction is parsed from m_input_begin and onwards.
606 // Each instruction are assumed to be contiguous in memory.
607 const char* m_input_begin;
608 // pointer to one past current instruction log chunk. If m_input_begin reaches m_input_end,
609 // a call to next_input_buffer will move m_input_begin and m_input_end to a new chunk of
610 // memory. Setting m_input_end to 0 disables this check, and is used if it is already known
611 // that all of the instructions are in memory.
612 const char* m_input_end;
613 util::StringBuffer m_string_buffer;
614 static const int m_max_levels = 1024;
615 util::Buffer<size_t> m_path;
617 REALM_NORETURN void parser_error() const;
619 template <class InstructionHandler>
620 void parse_one(InstructionHandler&);
621 bool has_next() noexcept;
626 void read_bytes(char* data, size_t size);
627 BinaryData read_buffer(util::StringBuffer&, size_t size);
631 double read_double();
633 StringData read_string(util::StringBuffer&);
634 BinaryData read_binary(util::StringBuffer&);
635 Timestamp read_timestamp();
636 void read_mixed(Mixed*);
638 // Advance m_input_begin and m_input_end to reflect the next block of instructions
639 // Returns false if no more input was available
640 bool next_input_buffer();
642 // return true if input was available
643 bool read_char(char&); // throws
645 bool is_valid_data_type(int type);
646 bool is_valid_link_type(int type);
650 class TransactLogParser::BadTransactLog : public std::exception {
652 const char* what() const noexcept override
654 return "Bad transaction log";
661 inline void TransactLogBufferStream::transact_log_reserve(size_t n, char** inout_new_begin, char** out_new_end)
663 char* data = m_buffer.data();
664 REALM_ASSERT(*inout_new_begin >= data);
665 REALM_ASSERT(*inout_new_begin <= (data + m_buffer.size()));
666 size_t size = *inout_new_begin - data;
667 m_buffer.reserve_extra(size, n);
668 data = m_buffer.data(); // May have changed
669 *inout_new_begin = data + size;
670 *out_new_end = data + m_buffer.size();
673 inline void TransactLogBufferStream::transact_log_append(const char* data, size_t size, char** out_new_begin,
676 transact_log_reserve(size, out_new_begin, out_new_end);
677 *out_new_begin = realm::safe_copy_n(data, size, *out_new_begin);
680 inline const char* TransactLogBufferStream::transact_log_data() const
682 return m_buffer.data();
685 inline TransactLogEncoder::TransactLogEncoder(TransactLogStream& stream)
690 inline void TransactLogEncoder::set_buffer(char* free_begin, char* free_end)
692 REALM_ASSERT(free_begin <= free_end);
693 m_transact_log_free_begin = free_begin;
694 m_transact_log_free_end = free_end;
697 inline void TransactLogConvenientEncoder::reset_selection_caches() noexcept
702 inline char* TransactLogEncoder::reserve(size_t n)
704 if (size_t(m_transact_log_free_end - m_transact_log_free_begin) < n) {
705 m_stream.transact_log_reserve(n, &m_transact_log_free_begin, &m_transact_log_free_end);
707 return m_transact_log_free_begin;
710 inline void TransactLogEncoder::advance(char* ptr) noexcept
712 REALM_ASSERT_DEBUG(m_transact_log_free_begin <= ptr);
713 REALM_ASSERT_DEBUG(ptr <= m_transact_log_free_end);
714 m_transact_log_free_begin = ptr;
718 // The integer encoding is platform independent. Also, it does not
719 // depend on the type of the specified integer. Integers of any type
720 // can be encoded as long as the specified buffer is large enough (see
721 // below). The decoding does not have to use the same type. Decoding
722 // will fail if, and only if the encoded value falls outside the range
723 // of the requested destination type.
725 // The encoding uses one or more bytes. It never uses more than 8 bits
726 // per byte. The last byte in the sequence is the first one that has
727 // its 8th bit set to zero.
729 // Consider a particular non-negative value V. Let W be the number of
730 // bits needed to encode V using the trivial binary encoding of
731 // integers. The total number of bytes produced is then
732 // ceil((W+1)/7). The first byte holds the 7 least significant bits of
733 // V. The last byte holds at most 6 bits of V including the most
734 // significant one. The value of the first bit of the last byte is
735 // always 2**((N-1)*7) where N is the total number of bytes.
737 // A negative value W is encoded by setting the sign bit to one and
738 // then encoding the positive result of -(W+1) as described above. The
739 // advantage of this representation is that it converts small negative
740 // values to small positive values which require a small number of
741 // bytes. This would not have been true for 2's complements
742 // representation, for example. The sign bit is always stored as the
743 // 7th bit of the last byte.
745 // value bits value + sign max bytes
746 // --------------------------------------------------
757 char* TransactLogEncoder::encode_int(char* ptr, T value)
759 static_assert(std::numeric_limits<T>::is_integer, "Integer required");
760 bool negative = util::is_negative(value);
762 // The following conversion is guaranteed by C++11 to never
763 // overflow (contrast this with "-value" which indeed could
764 // overflow). See C99+TC3 section 6.2.6.2 paragraph 2.
766 REALM_DIAG_IGNORE_UNSIGNED_MINUS();
767 value = -(value + 1);
770 // At this point 'value' is always a positive number. Also, small
771 // negative numbers have been converted to small positive numbers.
772 REALM_ASSERT(!util::is_negative(value));
773 // One sign bit plus number of value bits
774 const int num_bits = 1 + std::numeric_limits<T>::digits;
775 // Only the first 7 bits are available per byte. Had it not been
776 // for the fact that maximum guaranteed bit width of a char is 8,
777 // this value could have been increased to 15 (one less than the
778 // number of value bits in 'unsigned').
779 const int bits_per_byte = 7;
780 const int max_bytes = (num_bits + (bits_per_byte - 1)) / bits_per_byte;
781 static_assert(max_bytes <= max_enc_bytes_per_int, "Bad max_enc_bytes_per_int");
782 // An explicit constant maximum number of iterations is specified
783 // in the hope that it will help the optimizer (to do loop
784 // unrolling, for example).
785 typedef unsigned char uchar;
786 for (int i = 0; i < max_bytes; ++i) {
787 if (value >> (bits_per_byte - 1) == 0)
789 *reinterpret_cast<uchar*>(ptr) = uchar((1U << bits_per_byte) | unsigned(value & ((1U << bits_per_byte) - 1)));
791 value >>= bits_per_byte;
793 *reinterpret_cast<uchar*>(ptr) = uchar(negative ? (1U << (bits_per_byte - 1)) | unsigned(value) : value);
798 char* TransactLogEncoder::encode(char* ptr, T value)
800 auto value_2 = value + 0; // Perform integral promotion
801 return encode_int(ptr, value_2);
805 inline char* TransactLogEncoder::encode<char>(char* ptr, char value)
807 // Write the char as-is without encoding.
813 inline char* TransactLogEncoder::encode<Instruction>(char* ptr, Instruction inst)
815 return encode<char>(ptr, inst);
819 inline char* TransactLogEncoder::encode<bool>(char* ptr, bool value)
821 return encode<char>(ptr, value);
825 inline char* TransactLogEncoder::encode<float>(char* ptr, float value)
827 static_assert(std::numeric_limits<float>::is_iec559 &&
828 sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
829 "Unsupported 'float' representation");
830 const char* val_ptr = reinterpret_cast<char*>(&value);
831 return realm::safe_copy_n(val_ptr, sizeof value, ptr);
835 inline char* TransactLogEncoder::encode<double>(char* ptr, double value)
837 static_assert(std::numeric_limits<double>::is_iec559 &&
838 sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
839 "Unsupported 'double' representation");
840 const char* val_ptr = reinterpret_cast<char*>(&value);
841 return realm::safe_copy_n(val_ptr, sizeof value, ptr);
845 inline char* TransactLogEncoder::encode<DataType>(char* ptr, DataType type)
847 return encode<char>(ptr, type);
851 inline char* TransactLogEncoder::encode<StringData>(char* ptr, StringData s)
853 ptr = encode_int(ptr, s.size());
854 return std::copy_n(s.data(), s.size(), ptr);
858 inline char* TransactLogEncoder::encode<TransactLogEncoder::IntegerList>(char* ptr,
859 TransactLogEncoder::IntegerList list)
861 IntegerColumnIterator i = std::get<0>(list);
862 IntegerColumnIterator end = std::get<1>(list);
864 while (end - i > max_numbers_per_chunk) {
865 for (int j = 0; j < max_numbers_per_chunk; ++j)
866 ptr = encode_int(ptr, *i++);
868 size_t max_required_bytes_2 = max_enc_bytes_per_num * max_numbers_per_chunk;
869 ptr = reserve(max_required_bytes_2); // Throws
873 ptr = encode_int(ptr, *i++);
879 inline char* TransactLogEncoder::encode<TransactLogEncoder::UnsignedList>(char* ptr,
880 TransactLogEncoder::UnsignedList list)
882 const size_t* i = std::get<0>(list);
883 const size_t* end = std::get<1>(list);
886 ptr = encode_int(ptr, *i++);
892 size_t TransactLogEncoder::max_size(T)
894 return max_enc_bytes_per_num;
898 inline size_t TransactLogEncoder::max_size(char)
904 inline size_t TransactLogEncoder::max_size(bool)
910 inline size_t TransactLogEncoder::max_size(Instruction)
916 inline size_t TransactLogEncoder::max_size(DataType)
922 inline size_t TransactLogEncoder::max_size(StringData s)
924 return max_enc_bytes_per_num + s.size();
928 inline size_t TransactLogEncoder::max_size<TransactLogEncoder::IntegerList>(IntegerList)
930 // We only allocate space for 'max_numbers_per_chunk' at a time
931 return max_enc_bytes_per_num * max_numbers_per_chunk;
935 inline size_t TransactLogEncoder::max_size<TransactLogEncoder::UnsignedList>(UnsignedList list)
937 const size_t* begin = std::get<0>(list);
938 const size_t* end = std::get<1>(list);
939 // list contains (end - begin) elements
940 return max_enc_bytes_per_num * (end - begin);
943 template <class... L>
944 void TransactLogEncoder::append_simple_instr(L... numbers)
946 size_t max_required_bytes = max_size_list(numbers...);
947 char* ptr = reserve(max_required_bytes); // Throws
948 encode_list(ptr, numbers...);
951 template <class... L>
952 void TransactLogEncoder::append_mixed_instr(Instruction instr, const Mixed& value, L... numbers)
954 DataType type = value.get_type();
957 append_simple_instr(instr, numbers..., type, value.get_int()); // Throws
960 append_simple_instr(instr, numbers..., type, value.get_bool()); // Throws
963 append_simple_instr(instr, numbers..., type, value.get_float()); // Throws
966 append_simple_instr(instr, numbers..., type, value.get_double()); // Throws
968 case type_OldDateTime: {
969 auto value_2 = value.get_olddatetime().get_olddatetime();
970 append_simple_instr(instr, numbers..., type, value_2); // Throws
974 append_simple_instr(instr, numbers..., type, value.get_string()); // Throws
978 BinaryData value_2 = value.get_binary();
979 StringData value_3(value_2.data(), value_2.size());
980 append_simple_instr(instr, numbers..., type, value_3); // Throws
983 case type_Timestamp: {
984 Timestamp ts = value.get_timestamp();
985 int64_t seconds = ts.get_seconds();
986 int32_t nano_seconds = ts.get_nanoseconds();
987 append_simple_instr(instr, numbers..., type, seconds, nano_seconds); // Throws
991 append_simple_instr(instr, numbers..., type); // Throws
994 // Mixed in mixed is not possible
995 REALM_TERMINATE("Mixed in Mixed not possible");
998 // FIXME: Need to handle new link types here.
999 REALM_TERMINATE("Link types in Mixed not supported.");
1001 REALM_TERMINATE("Invalid Mixed.");
1004 inline void TransactLogConvenientEncoder::unselect_all() noexcept
1006 m_selected_table = nullptr;
1007 m_selected_spec = nullptr;
1008 // no race with on_link_list_destroyed since both are setting to nullptr
1009 m_selected_link_list = nullptr;
1012 inline void TransactLogConvenientEncoder::select_table(const Table* table)
1014 if (table != m_selected_table)
1015 do_select_table(table); // Throws
1016 m_selected_spec = nullptr;
1017 // no race with on_link_list_destroyed since both are setting to nullptr
1018 m_selected_link_list = nullptr;
1021 inline void TransactLogConvenientEncoder::select_desc(const Descriptor& desc)
1023 typedef _impl::DescriptorFriend df;
1024 if (&df::get_spec(desc) != m_selected_spec)
1025 do_select_desc(desc); // Throws
1026 // no race with on_link_list_destroyed since both are setting to nullptr
1027 m_selected_link_list = nullptr;
1030 inline void TransactLogConvenientEncoder::select_link_list(const LinkView& list)
1032 // A race between this and a call to on_link_list_destroyed() must
1033 // end up with m_selected_link_list pointing to the list argument given
1034 // here. We assume that the list given to on_link_list_destroyed() can
1035 // *never* be the same as the list argument given here. We resolve the
1036 // race by a) always updating m_selected_link_list in do_select_link_list()
1037 // and b) only atomically and conditionally updating it in
1038 // on_link_list_destroyed().
1039 if (&list != m_selected_link_list) {
1040 do_select_link_list(list); // Throws
1042 m_selected_spec = nullptr;
1046 inline bool TransactLogEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, StringData name)
1048 append_simple_instr(instr_InsertGroupLevelTable, table_ndx, prior_num_tables, name); // Throws
1052 inline void TransactLogConvenientEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables,
1056 m_encoder.insert_group_level_table(table_ndx, prior_num_tables, name); // Throws
1059 inline bool TransactLogEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables)
1061 append_simple_instr(instr_EraseGroupLevelTable, table_ndx, prior_num_tables); // Throws
1065 inline void TransactLogConvenientEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables)
1068 m_encoder.erase_group_level_table(table_ndx, prior_num_tables); // Throws
1071 inline bool TransactLogEncoder::rename_group_level_table(size_t table_ndx, StringData new_name)
1073 append_simple_instr(instr_RenameGroupLevelTable, table_ndx, new_name); // Throws
1077 inline void TransactLogConvenientEncoder::rename_group_level_table(size_t table_ndx, StringData new_name)
1080 m_encoder.rename_group_level_table(table_ndx, new_name); // Throws
1083 inline bool TransactLogEncoder::insert_column(size_t col_ndx, DataType type, StringData name, bool nullable)
1085 Instruction instr = (nullable ? instr_InsertNullableColumn : instr_InsertColumn);
1086 append_simple_instr(instr, col_ndx, type, name); // Throws
1090 inline bool TransactLogEncoder::insert_link_column(size_t col_ndx, DataType type, StringData name,
1091 size_t link_target_table_ndx, size_t backlink_col_ndx)
1093 REALM_ASSERT(_impl::TableFriend::is_link_type(ColumnType(type)));
1094 append_simple_instr(instr_InsertLinkColumn, col_ndx, type, link_target_table_ndx, backlink_col_ndx,
1100 inline void TransactLogConvenientEncoder::insert_column(const Descriptor& desc, size_t col_ndx, DataType type,
1101 StringData name, LinkTargetInfo& link, bool nullable)
1103 select_desc(desc); // Throws
1104 if (link.is_valid()) {
1105 typedef _impl::TableFriend tf;
1106 typedef _impl::DescriptorFriend df;
1107 size_t target_table_ndx = link.m_target_table->get_index_in_group();
1108 const Table& origin_table = df::get_root_table(desc);
1109 REALM_ASSERT(origin_table.is_group_level());
1110 const Spec& target_spec = tf::get_spec(*(link.m_target_table));
1111 size_t origin_table_ndx = origin_table.get_index_in_group();
1112 size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx);
1113 REALM_ASSERT_3(backlink_col_ndx, ==, link.m_backlink_col_ndx);
1114 m_encoder.insert_link_column(col_ndx, type, name, target_table_ndx, backlink_col_ndx); // Throws
1117 m_encoder.insert_column(col_ndx, type, name, nullable); // Throws
1121 inline bool TransactLogEncoder::erase_column(size_t col_ndx)
1123 append_simple_instr(instr_EraseColumn, col_ndx); // Throws
1127 inline bool TransactLogEncoder::erase_link_column(size_t col_ndx, size_t link_target_table_ndx,
1128 size_t backlink_col_ndx)
1130 append_simple_instr(instr_EraseLinkColumn, col_ndx, link_target_table_ndx, backlink_col_ndx); // Throws
1134 inline void TransactLogConvenientEncoder::erase_column(const Descriptor& desc, size_t col_ndx)
1136 select_desc(desc); // Throws
1138 DataType type = desc.get_column_type(col_ndx);
1139 typedef _impl::TableFriend tf;
1140 if (!tf::is_link_type(ColumnType(type))) {
1141 m_encoder.erase_column(col_ndx); // Throws
1143 else { // it's a link column:
1144 REALM_ASSERT(desc.is_root());
1145 typedef _impl::DescriptorFriend df;
1146 const Table& origin_table = df::get_root_table(desc);
1147 REALM_ASSERT(origin_table.is_group_level());
1148 const Table& target_table = *tf::get_link_target_table_accessor(origin_table, col_ndx);
1149 size_t target_table_ndx = target_table.get_index_in_group();
1150 const Spec& target_spec = tf::get_spec(target_table);
1151 size_t origin_table_ndx = origin_table.get_index_in_group();
1152 size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx);
1153 m_encoder.erase_link_column(col_ndx, target_table_ndx, backlink_col_ndx); // Throws
1157 inline bool TransactLogEncoder::rename_column(size_t col_ndx, StringData new_name)
1159 append_simple_instr(instr_RenameColumn, col_ndx, new_name); // Throws
1163 inline void TransactLogConvenientEncoder::rename_column(const Descriptor& desc, size_t col_ndx, StringData name)
1165 select_desc(desc); // Throws
1166 m_encoder.rename_column(col_ndx, name); // Throws
1170 inline bool TransactLogEncoder::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant,
1171 size_t prior_num_rows)
1173 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant);
1174 if (REALM_UNLIKELY(variant == instr_SetUnique))
1175 append_simple_instr(variant, type_Int, col_ndx, ndx, prior_num_rows, value); // Throws
1177 append_simple_instr(variant, type_Int, col_ndx, ndx, value); // Throws
1181 inline void TransactLogConvenientEncoder::set_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value,
1182 Instruction variant)
1184 select_table(t); // Throws
1185 size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0);
1186 m_encoder.set_int(col_ndx, ndx, value, variant, prior_num_rows); // Throws
1190 inline bool TransactLogEncoder::add_int(size_t col_ndx, size_t ndx, int_fast64_t value)
1192 append_simple_instr(instr_AddInteger, col_ndx, ndx, value); // Throws
1196 inline void TransactLogConvenientEncoder::add_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value)
1198 select_table(t); // Throws
1199 m_encoder.add_int(col_ndx, ndx, value);
1202 inline bool TransactLogEncoder::set_bool(size_t col_ndx, size_t ndx, bool value, Instruction variant)
1204 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1205 append_simple_instr(variant, type_Bool, col_ndx, ndx, value); // Throws
1209 inline void TransactLogConvenientEncoder::set_bool(const Table* t, size_t col_ndx, size_t ndx, bool value,
1210 Instruction variant)
1212 select_table(t); // Throws
1213 m_encoder.set_bool(col_ndx, ndx, value, variant); // Throws
1216 inline bool TransactLogEncoder::set_float(size_t col_ndx, size_t ndx, float value, Instruction variant)
1218 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1219 append_simple_instr(variant, type_Float, col_ndx, ndx, value); // Throws
1223 inline void TransactLogConvenientEncoder::set_float(const Table* t, size_t col_ndx, size_t ndx, float value,
1224 Instruction variant)
1226 select_table(t); // Throws
1227 m_encoder.set_float(col_ndx, ndx, value, variant); // Throws
1230 inline bool TransactLogEncoder::set_double(size_t col_ndx, size_t ndx, double value, Instruction variant)
1232 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1233 append_simple_instr(instr_Set, type_Double, col_ndx, ndx, value); // Throws
1237 inline void TransactLogConvenientEncoder::set_double(const Table* t, size_t col_ndx, size_t ndx, double value,
1238 Instruction variant)
1240 select_table(t); // Throws
1241 m_encoder.set_double(col_ndx, ndx, value, variant); // Throws
1244 inline bool TransactLogEncoder::set_string(size_t col_ndx, size_t ndx, StringData value, Instruction variant,
1245 size_t prior_num_rows)
1247 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant);
1248 if (value.is_null()) {
1249 set_null(col_ndx, ndx, variant, prior_num_rows); // Throws
1252 if (REALM_UNLIKELY(variant == instr_SetUnique))
1253 append_simple_instr(variant, type_String, col_ndx, ndx, prior_num_rows, value); // Throws
1255 append_simple_instr(variant, type_String, col_ndx, ndx, value); // Throws
1260 inline void TransactLogConvenientEncoder::set_string(const Table* t, size_t col_ndx, size_t ndx, StringData value,
1261 Instruction variant)
1263 select_table(t); // Throws
1264 size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0);
1265 m_encoder.set_string(col_ndx, ndx, value, variant, prior_num_rows); // Throws
1268 inline bool TransactLogEncoder::set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant)
1270 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1271 if (value.is_null()) {
1272 set_null(col_ndx, row_ndx, variant); // Throws
1275 StringData value_2(value.data(), value.size());
1276 append_simple_instr(variant, type_Binary, col_ndx, row_ndx, value_2); // Throws
1281 inline void TransactLogConvenientEncoder::set_binary(const Table* t, size_t col_ndx, size_t ndx, BinaryData value,
1282 Instruction variant)
1284 select_table(t); // Throws
1285 m_encoder.set_binary(col_ndx, ndx, value, variant); // Throws
1288 inline bool TransactLogEncoder::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, Instruction variant)
1290 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1291 append_simple_instr(variant, type_OldDateTime, col_ndx, ndx, value.get_olddatetime()); // Throws
1295 inline void TransactLogConvenientEncoder::set_olddatetime(const Table* t, size_t col_ndx, size_t ndx,
1296 OldDateTime value, Instruction variant)
1298 select_table(t); // Throws
1299 m_encoder.set_olddatetime(col_ndx, ndx, value, variant); // Throws
1302 inline bool TransactLogEncoder::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, Instruction variant)
1304 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1305 append_simple_instr(variant, type_Timestamp, col_ndx, ndx, value.get_seconds(),
1306 value.get_nanoseconds()); // Throws
1310 inline void TransactLogConvenientEncoder::set_timestamp(const Table* t, size_t col_ndx, size_t ndx, Timestamp value,
1311 Instruction variant)
1313 select_table(t); // Throws
1314 m_encoder.set_timestamp(col_ndx, ndx, value, variant); // Throws
1317 inline bool TransactLogEncoder::set_table(size_t col_ndx, size_t ndx, Instruction variant)
1319 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1320 append_simple_instr(variant, type_Table, col_ndx, ndx); // Throws
1324 inline void TransactLogConvenientEncoder::set_table(const Table* t, size_t col_ndx, size_t ndx, Instruction variant)
1326 select_table(t); // Throws
1327 m_encoder.set_table(col_ndx, ndx, variant); // Throws
1330 inline bool TransactLogEncoder::set_mixed(size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant)
1332 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1333 append_mixed_instr(variant, value, type_Mixed, col_ndx, ndx); // Throws
1337 inline void TransactLogConvenientEncoder::set_mixed(const Table* t, size_t col_ndx, size_t ndx, const Mixed& value,
1338 Instruction variant)
1340 select_table(t); // Throws
1341 m_encoder.set_mixed(col_ndx, ndx, value, variant); // Throws
1344 inline bool TransactLogEncoder::set_link(size_t col_ndx, size_t ndx, size_t value, size_t target_group_level_ndx,
1345 Instruction variant)
1347 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant);
1348 // Map `realm::npos` to zero, and `n` to `n+1`, where `n` is a target row
1350 size_t value_2 = size_t(1) + value;
1351 append_simple_instr(variant, type_Link, col_ndx, ndx, value_2, target_group_level_ndx); // Throws
1355 inline void TransactLogConvenientEncoder::set_link(const Table* t, size_t col_ndx, size_t ndx, size_t value,
1356 Instruction variant)
1358 select_table(t); // Throws
1359 size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx);
1360 m_encoder.set_link(col_ndx, ndx, value, target_group_level_ndx, variant); // Throws
1363 inline bool TransactLogEncoder::set_null(size_t col_ndx, size_t ndx, Instruction variant, size_t prior_num_rows)
1365 REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant);
1366 if (REALM_UNLIKELY(variant == instr_SetUnique)) {
1367 append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx, prior_num_rows); // Throws
1370 append_simple_instr(variant, set_null_sentinel(), col_ndx, ndx); // Throws
1375 inline void TransactLogConvenientEncoder::set_null(const Table* t, size_t col_ndx, size_t row_ndx,
1376 Instruction variant)
1378 select_table(t); // Throws
1379 size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0);
1380 m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows); // Throws
1383 inline bool TransactLogEncoder::nullify_link(size_t col_ndx, size_t ndx, size_t target_group_level_ndx)
1385 append_simple_instr(instr_NullifyLink, col_ndx, ndx, target_group_level_ndx); // Throws
1389 inline void TransactLogConvenientEncoder::nullify_link(const Table* t, size_t col_ndx, size_t ndx)
1391 select_table(t); // Throws
1392 size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx);
1393 m_encoder.nullify_link(col_ndx, ndx, target_group_level_ndx); // Throws
1396 inline bool TransactLogEncoder::insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData value)
1398 append_simple_instr(instr_InsertSubstring, col_ndx, row_ndx, pos, value); // Throws
1402 inline void TransactLogConvenientEncoder::insert_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos,
1405 if (value.size() > 0) {
1406 select_table(t); // Throws
1407 m_encoder.insert_substring(col_ndx, row_ndx, pos, value); // Throws
1411 inline bool TransactLogEncoder::erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size)
1413 append_simple_instr(instr_EraseFromString, col_ndx, row_ndx, pos, size); // Throws
1417 inline void TransactLogConvenientEncoder::erase_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos,
1421 select_table(t); // Throws
1422 m_encoder.erase_substring(col_ndx, row_ndx, pos, size); // Throws
1426 inline bool TransactLogEncoder::insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows,
1429 append_simple_instr(instr_InsertEmptyRows, row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws
1433 inline void TransactLogConvenientEncoder::insert_empty_rows(const Table* t, size_t row_ndx, size_t num_rows_to_insert,
1434 size_t prior_num_rows)
1436 select_table(t); // Throws
1437 bool unordered = false;
1438 m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws
1441 inline bool TransactLogEncoder::add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx,
1444 append_simple_instr(instr_AddRowWithKey, row_ndx, prior_num_rows, key_col_ndx, key); // Throws
1448 inline void TransactLogConvenientEncoder::add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows,
1449 size_t key_col_ndx, int64_t key)
1451 select_table(t); // Throws
1452 m_encoder.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key); // Throws
1455 inline bool TransactLogEncoder::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
1458 append_simple_instr(instr_EraseRows, row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws
1463 inline void TransactLogConvenientEncoder::erase_rows(const Table* t, size_t row_ndx, size_t num_rows_to_erase,
1464 size_t prior_num_rows, bool is_move_last_over)
1466 select_table(t); // Throws
1467 bool unordered = is_move_last_over;
1468 m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws
1471 inline bool TransactLogEncoder::swap_rows(size_t row_ndx_1, size_t row_ndx_2)
1473 append_simple_instr(instr_SwapRows, row_ndx_1, row_ndx_2); // Throws
1477 inline void TransactLogConvenientEncoder::swap_rows(const Table* t, size_t row_ndx_1, size_t row_ndx_2)
1479 REALM_ASSERT(row_ndx_1 < row_ndx_2);
1480 select_table(t); // Throws
1481 m_encoder.swap_rows(row_ndx_1, row_ndx_2);
1484 inline bool TransactLogEncoder::move_row(size_t from_ndx, size_t to_ndx)
1486 append_simple_instr(instr_MoveRow, from_ndx, to_ndx); // Throws
1490 inline void TransactLogConvenientEncoder::move_row(const Table* t, size_t from_ndx, size_t to_ndx)
1492 REALM_ASSERT(from_ndx != to_ndx);
1493 select_table(t); // Throws
1494 m_encoder.move_row(from_ndx, to_ndx);
1497 inline bool TransactLogEncoder::merge_rows(size_t row_ndx, size_t new_row_ndx)
1499 append_simple_instr(instr_MergeRows, row_ndx, new_row_ndx); // Throws
1503 inline void TransactLogConvenientEncoder::merge_rows(const Table* t, size_t row_ndx, size_t new_row_ndx)
1505 select_table(t); // Throws
1506 m_encoder.merge_rows(row_ndx, new_row_ndx);
1509 inline bool TransactLogEncoder::add_search_index(size_t col_ndx)
1511 append_simple_instr(instr_AddSearchIndex, col_ndx); // Throws
1515 inline void TransactLogConvenientEncoder::add_search_index(const Descriptor& desc, size_t col_ndx)
1517 select_desc(desc); // Throws
1518 m_encoder.add_search_index(col_ndx); // Throws
1522 inline bool TransactLogEncoder::remove_search_index(size_t col_ndx)
1524 append_simple_instr(instr_RemoveSearchIndex, col_ndx); // Throws
1528 inline void TransactLogConvenientEncoder::remove_search_index(const Descriptor& desc, size_t col_ndx)
1530 select_desc(desc); // Throws
1531 m_encoder.remove_search_index(col_ndx); // Throws
1534 inline bool TransactLogEncoder::set_link_type(size_t col_ndx, LinkType link_type)
1536 append_simple_instr(instr_SetLinkType, col_ndx, int(link_type)); // Throws
1540 inline void TransactLogConvenientEncoder::set_link_type(const Table* t, size_t col_ndx, LinkType link_type)
1542 select_table(t); // Throws
1543 m_encoder.set_link_type(col_ndx, link_type); // Throws
1547 inline bool TransactLogEncoder::clear_table(size_t old_size)
1549 append_simple_instr(instr_ClearTable, old_size); // Throws
1553 inline void TransactLogConvenientEncoder::clear_table(const Table* t, size_t prior_num_rows)
1555 select_table(t); // Throws
1556 m_encoder.clear_table(prior_num_rows); // Throws
1559 inline bool TransactLogEncoder::optimize_table()
1561 append_simple_instr(instr_OptimizeTable); // Throws
1565 inline void TransactLogConvenientEncoder::optimize_table(const Table* t)
1567 select_table(t); // Throws
1568 m_encoder.optimize_table(); // Throws
1571 inline bool TransactLogEncoder::link_list_set(size_t link_ndx, size_t value, size_t prior_size)
1573 append_simple_instr(instr_LinkListSet, link_ndx, value, prior_size); // Throws
1577 inline void TransactLogConvenientEncoder::link_list_set(const LinkView& list, size_t link_ndx, size_t value)
1579 select_link_list(list); // Throws
1580 m_encoder.link_list_set(link_ndx, value, list.size()); // Throws
1583 inline bool TransactLogEncoder::link_list_nullify(size_t link_ndx, size_t prior_size)
1585 append_simple_instr(instr_LinkListNullify, link_ndx, prior_size); // Throws
1589 inline void TransactLogConvenientEncoder::link_list_nullify(const LinkView& list, size_t link_ndx)
1591 select_link_list(list); // Throws
1592 size_t prior_size = list.size(); // Instruction is emitted before the fact.
1593 m_encoder.link_list_nullify(link_ndx, prior_size); // Throws
1596 inline bool TransactLogEncoder::link_list_set_all(const IntegerColumn& values)
1598 size_t num_values = values.size();
1599 append_simple_instr(
1600 instr_LinkListSetAll, num_values,
1601 std::make_tuple(IntegerColumnIterator(&values, 0), IntegerColumnIterator(&values, num_values))); // Throws
1605 inline void TransactLogConvenientEncoder::set_link_list(const LinkView& list, const IntegerColumn& values)
1607 select_link_list(list); // Throws
1608 m_encoder.link_list_set_all(values); // Throws
1611 inline bool TransactLogEncoder::link_list_insert(size_t link_ndx, size_t value, size_t prior_size)
1613 append_simple_instr(instr_LinkListInsert, link_ndx, value, prior_size); // Throws
1617 inline void TransactLogConvenientEncoder::link_list_insert(const LinkView& list, size_t link_ndx, size_t value)
1619 select_link_list(list); // Throws
1620 size_t prior_size = list.size() - 1; // The instruction is emitted after the fact.
1621 m_encoder.link_list_insert(link_ndx, value, prior_size); // Throws
1624 inline bool TransactLogEncoder::link_list_move(size_t from_link_ndx, size_t to_link_ndx)
1626 REALM_ASSERT(from_link_ndx != to_link_ndx);
1627 append_simple_instr(instr_LinkListMove, from_link_ndx, to_link_ndx); // Throws
1631 inline void TransactLogConvenientEncoder::link_list_move(const LinkView& list, size_t from_link_ndx,
1634 select_link_list(list); // Throws
1635 m_encoder.link_list_move(from_link_ndx, to_link_ndx); // Throws
1638 inline bool TransactLogEncoder::link_list_swap(size_t link1_ndx, size_t link2_ndx)
1640 append_simple_instr(instr_LinkListSwap, link1_ndx, link2_ndx); // Throws
1644 inline void TransactLogConvenientEncoder::link_list_swap(const LinkView& list, size_t link1_ndx, size_t link2_ndx)
1646 select_link_list(list); // Throws
1647 m_encoder.link_list_swap(link1_ndx, link2_ndx); // Throws
1650 inline bool TransactLogEncoder::link_list_erase(size_t link_ndx, size_t prior_size)
1652 append_simple_instr(instr_LinkListErase, link_ndx, prior_size); // Throws
1656 inline void TransactLogConvenientEncoder::link_list_erase(const LinkView& list, size_t link_ndx)
1658 select_link_list(list); // Throws
1659 size_t prior_size = list.size(); // The instruction is emitted before the fact.
1660 m_encoder.link_list_erase(link_ndx, prior_size); // Throws
1663 inline bool TransactLogEncoder::link_list_clear(size_t old_list_size)
1665 append_simple_instr(instr_LinkListClear, old_list_size); // Throws
1669 inline void TransactLogConvenientEncoder::on_table_destroyed(const Table* t) noexcept
1671 if (m_selected_table == t)
1672 m_selected_table = nullptr;
1675 inline void TransactLogConvenientEncoder::on_spec_destroyed(const Spec* s) noexcept
1677 if (m_selected_spec == s)
1678 m_selected_spec = nullptr;
1682 inline void TransactLogConvenientEncoder::on_link_list_destroyed(const LinkView& list) noexcept
1684 const LinkView* lw_ptr = &list;
1685 // atomically clear m_selected_link_list iff it already points to 'list':
1686 // (lw_ptr will be modified if the swap fails, but we ignore that)
1687 m_selected_link_list.compare_exchange_strong(lw_ptr, nullptr, std::memory_order_relaxed,
1688 std::memory_order_relaxed);
1692 inline TransactLogParser::TransactLogParser()
1693 : m_input_buffer(1024) // Throws
1698 inline TransactLogParser::~TransactLogParser() noexcept
1703 template <class InstructionHandler>
1704 void TransactLogParser::parse(NoCopyInputStream& in, InstructionHandler& handler)
1707 m_input_begin = m_input_end = nullptr;
1710 parse_one(handler); // Throws
1713 template <class InstructionHandler>
1714 void TransactLogParser::parse(InputStream& in, InstructionHandler& handler)
1716 NoCopyInputStreamAdaptor in_2(in, m_input_buffer.data(), m_input_buffer.size());
1717 parse(in_2, handler); // Throws
1720 inline bool TransactLogParser::has_next() noexcept
1722 return m_input_begin != m_input_end || next_input_buffer();
1725 template <class InstructionHandler>
1726 void TransactLogParser::parse_one(InstructionHandler& handler)
1729 if (!read_char(instr_ch))
1730 parser_error(); // Throws
1731 // std::cerr << "parsing " << util::promote(instr) << " @ " << std::hex << long(m_input_begin) << std::dec <<
1733 Instruction instr = Instruction(instr_ch);
1735 case instr_SetDefault:
1736 case instr_SetUnique:
1738 int type = read_int<int>(); // Throws
1739 size_t col_ndx = read_int<size_t>(); // Throws
1740 size_t row_ndx = read_int<size_t>(); // Throws
1741 size_t prior_num_rows = 0;
1742 if (REALM_UNLIKELY(instr == instr_SetUnique))
1743 prior_num_rows = read_int<size_t>(); // Throws
1745 if (type == TransactLogEncoder::set_null_sentinel()) {
1746 // Special case for set_null
1747 if (!handler.set_null(col_ndx, row_ndx, instr, prior_num_rows)) // Throws
1752 switch (DataType(type)) {
1754 int_fast64_t value = read_int<int64_t>(); // Throws
1755 if (!handler.set_int(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws
1760 bool value = read_bool(); // Throws
1761 if (!handler.set_bool(col_ndx, row_ndx, value, instr)) // Throws
1766 float value = read_float(); // Throws
1767 if (!handler.set_float(col_ndx, row_ndx, value, instr)) // Throws
1772 double value = read_double(); // Throws
1773 if (!handler.set_double(col_ndx, row_ndx, value, instr)) // Throws
1778 StringData value = read_string(m_string_buffer); // Throws
1779 if (!handler.set_string(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws
1784 BinaryData value = read_binary(m_string_buffer); // Throws
1785 if (!handler.set_binary(col_ndx, row_ndx, value, instr)) // Throws
1789 case type_OldDateTime: {
1790 int_fast64_t value = read_int<int_fast64_t>(); // Throws
1791 if (!handler.set_olddatetime(col_ndx, row_ndx, value, instr)) // Throws
1795 case type_Timestamp: {
1796 int64_t seconds = read_int<int64_t>(); // Throws
1797 int32_t nanoseconds = read_int<int32_t>(); // Throws
1798 Timestamp value = Timestamp(seconds, nanoseconds);
1799 if (!handler.set_timestamp(col_ndx, row_ndx, value, instr)) // Throws
1804 if (!handler.set_table(col_ndx, row_ndx, instr)) // Throws
1810 read_mixed(&value); // Throws
1811 if (!handler.set_mixed(col_ndx, row_ndx, value, instr)) // Throws
1816 size_t value = read_int<size_t>(); // Throws
1817 // Map zero to realm::npos, and `n+1` to `n`, where `n` is a target row index.
1818 size_t target_row_ndx = size_t(value - 1);
1819 size_t target_group_level_ndx = read_int<size_t>(); // Throws
1820 if (!handler.set_link(col_ndx, row_ndx, target_row_ndx, target_group_level_ndx, instr)) // Throws
1824 case type_LinkList: {
1825 // Unsupported column type for Set.
1833 case instr_AddInteger: {
1834 size_t col_ndx = read_int<size_t>(); // Throws
1835 size_t row_ndx = read_int<size_t>(); // Throws
1836 int_fast64_t value = read_int<int64_t>(); // Throws
1837 if (!handler.add_int(col_ndx, row_ndx, value)) // Throws
1841 case instr_NullifyLink: {
1842 size_t col_ndx = read_int<size_t>(); // Throws
1843 size_t row_ndx = read_int<size_t>(); // Throws
1844 size_t target_group_level_ndx = read_int<size_t>(); // Throws
1845 if (!handler.nullify_link(col_ndx, row_ndx, target_group_level_ndx)) // Throws
1849 case instr_InsertSubstring: {
1850 size_t col_ndx = read_int<size_t>(); // Throws
1851 size_t row_ndx = read_int<size_t>(); // Throws
1852 size_t pos = read_int<size_t>(); // Throws
1853 StringData value = read_string(m_string_buffer); // Throws
1854 if (!handler.insert_substring(col_ndx, row_ndx, pos, value)) // Throws
1858 case instr_EraseFromString: {
1859 size_t col_ndx = read_int<size_t>(); // Throws
1860 size_t row_ndx = read_int<size_t>(); // Throws
1861 size_t pos = read_int<size_t>(); // Throws
1862 size_t size = read_int<size_t>(); // Throws
1863 if (!handler.erase_substring(col_ndx, row_ndx, pos, size)) // Throws
1867 case instr_InsertEmptyRows: {
1868 size_t row_ndx = read_int<size_t>(); // Throws
1869 size_t num_rows_to_insert = read_int<size_t>(); // Throws
1870 size_t prior_num_rows = read_int<size_t>(); // Throws
1871 bool unordered = read_bool(); // Throws
1872 if (!handler.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered)) // Throws
1876 case instr_AddRowWithKey: {
1877 size_t row_ndx = read_int<size_t>(); // Throws
1878 size_t prior_num_rows = read_int<size_t>(); // Throws
1879 size_t key_col_ndx = read_int<size_t>(); // Throws
1880 int64_t key = read_int<int64_t>(); // Throws
1881 if (!handler.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key)) // Throws
1885 case instr_EraseRows: {
1886 size_t row_ndx = read_int<size_t>(); // Throws
1887 size_t num_rows_to_erase = read_int<size_t>(); // Throws
1888 size_t prior_num_rows = read_int<size_t>(); // Throws
1889 bool unordered = read_bool(); // Throws
1890 if (!handler.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered)) // Throws
1894 case instr_SwapRows: {
1895 size_t row_ndx_1 = read_int<size_t>(); // Throws
1896 size_t row_ndx_2 = read_int<size_t>(); // Throws
1897 if (!handler.swap_rows(row_ndx_1, row_ndx_2)) // Throws
1901 case instr_MoveRow: {
1902 size_t from_ndx = read_int<size_t>(); // Throws
1903 size_t to_ndx = read_int<size_t>(); // Throws
1904 if (!handler.move_row(from_ndx, to_ndx)) // Throws
1908 case instr_MergeRows: {
1909 size_t row_ndx = read_int<size_t>(); // Throws
1910 size_t new_row_ndx = read_int<size_t>(); // Throws
1911 if (!handler.merge_rows(row_ndx, new_row_ndx)) // Throws
1915 case instr_SelectTable: {
1916 int levels = read_int<int>(); // Throws
1917 if (levels < 0 || levels > m_max_levels)
1919 m_path.reserve(0, 2 * levels); // Throws
1920 size_t* path = m_path.data();
1921 size_t group_level_ndx = read_int<size_t>(); // Throws
1922 for (int i = 0; i != levels; ++i) {
1923 size_t col_ndx = read_int<size_t>(); // Throws
1924 size_t row_ndx = read_int<size_t>(); // Throws
1925 path[2 * i + 0] = col_ndx;
1926 path[2 * i + 1] = row_ndx;
1928 if (!handler.select_table(group_level_ndx, levels, path)) // Throws
1932 case instr_ClearTable: {
1933 size_t old_size = read_int<size_t>(); // Throws
1934 if (!handler.clear_table(old_size)) // Throws
1938 case instr_LinkListSet: {
1939 size_t link_ndx = read_int<size_t>(); // Throws
1940 size_t value = read_int<size_t>(); // Throws
1941 size_t prior_size = read_int<size_t>(); // Throws
1942 if (!handler.link_list_set(link_ndx, value, prior_size)) // Throws
1946 case instr_LinkListSetAll: {
1947 // todo, log that it's a SetAll we're doing
1948 size_t size = read_int<size_t>(); // Throws
1949 for (size_t i = 0; i < size; i++) {
1950 size_t link = read_int<size_t>(); // Throws
1951 if (!handler.link_list_set(i, link, size)) // Throws
1956 case instr_LinkListInsert: {
1957 size_t link_ndx = read_int<size_t>(); // Throws
1958 size_t value = read_int<size_t>(); // Throws
1959 size_t prior_size = read_int<size_t>(); // Throws
1960 if (!handler.link_list_insert(link_ndx, value, prior_size)) // Throws
1964 case instr_LinkListMove: {
1965 size_t from_link_ndx = read_int<size_t>(); // Throws
1966 size_t to_link_ndx = read_int<size_t>(); // Throws
1967 if (!handler.link_list_move(from_link_ndx, to_link_ndx)) // Throws
1971 case instr_LinkListSwap: {
1972 size_t link1_ndx = read_int<size_t>(); // Throws
1973 size_t link2_ndx = read_int<size_t>(); // Throws
1974 if (!handler.link_list_swap(link1_ndx, link2_ndx)) // Throws
1978 case instr_LinkListErase: {
1979 size_t link_ndx = read_int<size_t>(); // Throws
1980 size_t prior_size = read_int<size_t>(); // Throws
1981 if (!handler.link_list_erase(link_ndx, prior_size)) // Throws
1985 case instr_LinkListNullify: {
1986 size_t link_ndx = read_int<size_t>(); // Throws
1987 size_t prior_size = read_int<size_t>(); // Throws
1988 if (!handler.link_list_nullify(link_ndx, prior_size)) // Throws
1992 case instr_LinkListClear: {
1993 size_t old_list_size = read_int<size_t>(); // Throws
1994 if (!handler.link_list_clear(old_list_size)) // Throws
1998 case instr_SelectLinkList: {
1999 size_t col_ndx = read_int<size_t>(); // Throws
2000 size_t row_ndx = read_int<size_t>(); // Throws
2001 size_t target_group_level_ndx = read_int<size_t>(); // Throws
2002 if (!handler.select_link_list(col_ndx, row_ndx, target_group_level_ndx)) // Throws
2006 case instr_MoveColumn: {
2007 // FIXME: remove this in the next breaking change.
2008 // This instruction is no longer supported and not used by either
2009 // bindings or sync, so if we see it here, there was a problem parsing.
2013 case instr_AddSearchIndex: {
2014 size_t col_ndx = read_int<size_t>(); // Throws
2015 if (!handler.add_search_index(col_ndx)) // Throws
2019 case instr_RemoveSearchIndex: {
2020 size_t col_ndx = read_int<size_t>(); // Throws
2021 if (!handler.remove_search_index(col_ndx)) // Throws
2025 case instr_SetLinkType: {
2026 size_t col_ndx = read_int<size_t>(); // Throws
2027 int link_type = read_int<int>(); // Throws
2028 if (!is_valid_link_type(link_type))
2030 if (!handler.set_link_type(col_ndx, LinkType(link_type))) // Throws
2034 case instr_InsertColumn:
2035 case instr_InsertNullableColumn: {
2036 size_t col_ndx = read_int<size_t>(); // Throws
2037 int type = read_int<int>(); // Throws
2038 if (!is_valid_data_type(type))
2040 if (REALM_UNLIKELY(type == type_Link || type == type_LinkList))
2042 StringData name = read_string(m_string_buffer); // Throws
2043 bool nullable = (Instruction(instr) == instr_InsertNullableColumn);
2044 if (REALM_UNLIKELY(nullable && (type == type_Mixed))) {
2045 // Nullability not supported for Mixed columns.
2048 if (!handler.insert_column(col_ndx, DataType(type), name, nullable)) // Throws
2052 case instr_InsertLinkColumn: {
2053 size_t col_ndx = read_int<size_t>(); // Throws
2054 int type = read_int<int>(); // Throws
2055 if (!is_valid_data_type(type))
2057 if (REALM_UNLIKELY(type != type_Link && type != type_LinkList))
2059 size_t link_target_table_ndx = read_int<size_t>(); // Throws
2060 size_t backlink_col_ndx = read_int<size_t>(); // Throws
2061 StringData name = read_string(m_string_buffer); // Throws
2062 if (!handler.insert_link_column(col_ndx, DataType(type), name, link_target_table_ndx,
2063 backlink_col_ndx)) // Throws
2067 case instr_EraseColumn: {
2068 size_t col_ndx = read_int<size_t>(); // Throws
2069 if (!handler.erase_column(col_ndx)) // Throws
2073 case instr_EraseLinkColumn: {
2074 size_t col_ndx = read_int<size_t>(); // Throws
2075 size_t link_target_table_ndx = read_int<size_t>(); // Throws
2076 size_t backlink_col_ndx = read_int<size_t>(); // Throws
2077 if (!handler.erase_link_column(col_ndx, link_target_table_ndx, backlink_col_ndx)) // Throws
2081 case instr_RenameColumn: {
2082 size_t col_ndx = read_int<size_t>(); // Throws
2083 StringData name = read_string(m_string_buffer); // Throws
2084 if (!handler.rename_column(col_ndx, name)) // Throws
2088 case instr_SelectDescriptor: {
2089 int levels = read_int<int>(); // Throws
2090 if (levels < 0 || levels > m_max_levels)
2092 m_path.reserve(0, levels); // Throws
2093 size_t* path = m_path.data();
2094 for (int i = 0; i != levels; ++i) {
2095 size_t col_ndx = read_int<size_t>(); // Throws
2098 if (!handler.select_descriptor(levels, path)) // Throws
2102 case instr_InsertGroupLevelTable: {
2103 size_t table_ndx = read_int<size_t>(); // Throws
2104 size_t num_tables = read_int<size_t>(); // Throws
2105 StringData name = read_string(m_string_buffer); // Throws
2106 if (!handler.insert_group_level_table(table_ndx, num_tables, name)) // Throws
2110 case instr_EraseGroupLevelTable: {
2111 size_t table_ndx = read_int<size_t>(); // Throws
2112 size_t prior_num_tables = read_int<size_t>(); // Throws
2113 if (!handler.erase_group_level_table(table_ndx, prior_num_tables)) // Throws
2117 case instr_RenameGroupLevelTable: {
2118 size_t table_ndx = read_int<size_t>(); // Throws
2119 StringData new_name = read_string(m_string_buffer); // Throws
2120 if (!handler.rename_group_level_table(table_ndx, new_name)) // Throws
2124 case instr_MoveGroupLevelTable: {
2125 // This instruction is no longer supported and not used by either
2126 // bindings or sync, so if we see it here, there was a problem parsing.
2127 // FIXME: remove this in the next breaking change.
2131 case instr_OptimizeTable: {
2132 if (!handler.optimize_table()) // Throws
2138 throw BadTransactLog();
2143 T TransactLogParser::read_int()
2147 const int max_bytes = (std::numeric_limits<T>::digits + 1 + 6) / 7;
2148 for (int i = 0; i != max_bytes; ++i) {
2151 goto bad_transact_log;
2152 part = static_cast<unsigned char>(c);
2154 goto bad_transact_log; // Only the first 8 bits may be used in each byte
2155 if ((part & 0x80) == 0) {
2157 if (util::int_shift_left_with_overflow_detect(p, i * 7))
2158 goto bad_transact_log;
2162 if (i == max_bytes - 1)
2163 goto bad_transact_log; // Too many bytes
2164 value |= T(part & 0x7F) << (i * 7);
2167 // The real value is negative. Because 'value' is positive at
2168 // this point, the following negation is guaranteed by C++11
2169 // to never overflow. See C99+TC3 section 6.2.6.2 paragraph 2.
2171 REALM_DIAG_IGNORE_UNSIGNED_MINUS();
2174 if (util::int_subtract_with_overflow_detect(value, 1))
2175 goto bad_transact_log;
2180 throw BadTransactLog();
2184 inline void TransactLogParser::read_bytes(char* data, size_t size)
2187 const size_t avail = m_input_end - m_input_begin;
2190 realm::safe_copy_n(m_input_begin, avail, data);
2191 if (!next_input_buffer())
2192 throw BadTransactLog();
2196 const char* to = m_input_begin + size;
2197 realm::safe_copy_n(m_input_begin, size, data);
2202 inline BinaryData TransactLogParser::read_buffer(util::StringBuffer& buf, size_t size)
2204 const size_t avail = m_input_end - m_input_begin;
2205 if (avail >= size) {
2206 m_input_begin += size;
2207 return BinaryData(m_input_begin - size, size);
2211 buf.resize(size); // Throws
2212 read_bytes(buf.data(), size);
2213 return BinaryData(buf.data(), size);
2217 inline bool TransactLogParser::read_bool()
2219 return read_int<char>();
2223 inline float TransactLogParser::read_float()
2225 static_assert(std::numeric_limits<float>::is_iec559 &&
2226 sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
2227 "Unsupported 'float' representation");
2229 read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
2234 inline double TransactLogParser::read_double()
2236 static_assert(std::numeric_limits<double>::is_iec559 &&
2237 sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
2238 "Unsupported 'double' representation");
2240 read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
2245 inline StringData TransactLogParser::read_string(util::StringBuffer& buf)
2247 size_t size = read_int<size_t>(); // Throws
2249 if (size > Table::max_string_size)
2252 BinaryData buffer = read_buffer(buf, size);
2253 return StringData{buffer.data(), size};
2256 inline Timestamp TransactLogParser::read_timestamp()
2258 int64_t seconds = read_int<int64_t>(); // Throws
2259 int32_t nanoseconds = read_int<int32_t>(); // Throws
2260 return Timestamp(seconds, nanoseconds);
2264 inline BinaryData TransactLogParser::read_binary(util::StringBuffer& buf)
2266 size_t size = read_int<size_t>(); // Throws
2268 return read_buffer(buf, size);
2272 inline void TransactLogParser::read_mixed(Mixed* mixed)
2274 DataType type = DataType(read_int<int>()); // Throws
2277 int_fast64_t value = read_int<int64_t>(); // Throws
2278 mixed->set_int(value);
2282 bool value = read_bool(); // Throws
2283 mixed->set_bool(value);
2287 float value = read_float(); // Throws
2288 mixed->set_float(value);
2292 double value = read_double(); // Throws
2293 mixed->set_double(value);
2296 case type_OldDateTime: {
2297 int_fast64_t value = read_int<int_fast64_t>(); // Throws
2298 mixed->set_olddatetime(value);
2301 case type_Timestamp: {
2302 Timestamp value = read_timestamp(); // Throws
2303 mixed->set_timestamp(value);
2307 StringData value = read_string(m_string_buffer); // Throws
2308 mixed->set_string(value);
2312 BinaryData value = read_binary(m_string_buffer); // Throws
2313 mixed->set_binary(value);
2317 *mixed = Mixed::subtable_tag();
2324 // FIXME: Need to handle new link types here
2327 throw BadTransactLog();
2331 inline bool TransactLogParser::next_input_buffer()
2333 return m_input->next_block(m_input_begin, m_input_end);
2337 inline bool TransactLogParser::read_char(char& c)
2339 if (m_input_begin == m_input_end && !next_input_buffer())
2341 c = *m_input_begin++;
2346 inline bool TransactLogParser::is_valid_data_type(int type)
2348 switch (DataType(type)) {
2355 case type_OldDateTime:
2356 case type_Timestamp:
2367 inline bool TransactLogParser::is_valid_link_type(int type)
2369 switch (LinkType(type)) {
2378 class TransactReverser {
2380 bool select_table(size_t group_level_ndx, size_t levels, const size_t* path)
2383 m_encoder.select_table(group_level_ndx, levels, path);
2384 m_pending_ts_instr = get_inst();
2388 bool select_descriptor(size_t levels, const size_t* path)
2391 m_encoder.select_descriptor(levels, path);
2392 m_pending_ds_instr = get_inst();
2396 bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData)
2399 m_encoder.erase_group_level_table(table_ndx, num_tables + 1);
2400 append_instruction();
2404 bool erase_group_level_table(size_t table_ndx, size_t num_tables)
2407 m_encoder.insert_group_level_table(table_ndx, num_tables - 1, "");
2408 append_instruction();
2412 bool rename_group_level_table(size_t, StringData)
2418 bool optimize_table()
2420 return true; // No-op
2423 bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered)
2425 size_t num_rows_to_erase = num_rows_to_insert;
2426 size_t prior_num_rows_2 = prior_num_rows + num_rows_to_insert;
2427 m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows_2, unordered); // Throws
2428 append_instruction();
2432 bool add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t)
2434 bool unordered = true;
2435 m_encoder.erase_rows(row_ndx, 1, prior_num_rows + 1, unordered); // Throws
2436 append_instruction();
2440 bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered)
2442 size_t num_rows_to_insert = num_rows_to_erase;
2443 // Number of rows in table after removal, but before inverse insertion
2444 size_t prior_num_rows_2 = prior_num_rows - num_rows_to_erase;
2445 m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows_2, unordered); // Throws
2446 append_instruction();
2450 bool swap_rows(size_t row_ndx_1, size_t row_ndx_2)
2452 m_encoder.swap_rows(row_ndx_1, row_ndx_2);
2453 append_instruction();
2457 bool move_row(size_t from_ndx, size_t to_ndx)
2459 m_encoder.move_row(to_ndx, from_ndx);
2460 append_instruction();
2464 bool merge_rows(size_t row_ndx, size_t new_row_ndx)
2466 // There is no instruction we can generate here to change back.
2467 // However, we do need to refresh accessors for any tables
2468 // connected through backlinks, so we generate updates on each
2469 // affected row by merging to itself.
2470 m_encoder.merge_rows(row_ndx, row_ndx);
2471 append_instruction();
2472 m_encoder.merge_rows(new_row_ndx, new_row_ndx);
2473 append_instruction();
2477 bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t value, Instruction variant, size_t prior_num_rows)
2479 m_encoder.set_int(col_ndx, row_ndx, value, variant, prior_num_rows);
2480 append_instruction();
2484 bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t value)
2486 m_encoder.add_int(col_ndx, row_ndx, -value);
2487 append_instruction();
2491 bool set_bool(size_t col_ndx, size_t row_ndx, bool value, Instruction variant)
2493 m_encoder.set_bool(col_ndx, row_ndx, value, variant);
2494 append_instruction();
2498 bool set_float(size_t col_ndx, size_t row_ndx, float value, Instruction variant)
2500 m_encoder.set_float(col_ndx, row_ndx, value, variant);
2501 append_instruction();
2505 bool set_double(size_t col_ndx, size_t row_ndx, double value, Instruction variant)
2507 m_encoder.set_double(col_ndx, row_ndx, value, variant);
2508 append_instruction();
2512 bool set_string(size_t col_ndx, size_t row_ndx, StringData value, Instruction variant, size_t prior_num_rows)
2514 m_encoder.set_string(col_ndx, row_ndx, value, variant, prior_num_rows);
2515 append_instruction();
2519 bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant)
2521 m_encoder.set_binary(col_ndx, row_ndx, value, variant);
2522 append_instruction();
2526 bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime value, Instruction variant)
2528 m_encoder.set_olddatetime(col_ndx, row_ndx, value, variant);
2529 append_instruction();
2533 bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp value, Instruction variant)
2535 m_encoder.set_timestamp(col_ndx, row_ndx, value, variant);
2536 append_instruction();
2540 bool set_table(size_t col_ndx, size_t row_ndx, Instruction variant)
2542 m_encoder.set_table(col_ndx, row_ndx, variant);
2543 append_instruction();
2547 bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed& value, Instruction variant)
2549 m_encoder.set_mixed(col_ndx, row_ndx, value, variant);
2550 append_instruction();
2554 bool set_null(size_t col_ndx, size_t row_ndx, Instruction variant, size_t prior_num_rows)
2556 m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows);
2557 append_instruction();
2561 bool set_link(size_t col_ndx, size_t row_ndx, size_t value, size_t target_group_level_ndx, Instruction variant)
2563 m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx, variant);
2564 append_instruction();
2568 bool insert_substring(size_t, size_t, size_t, StringData)
2570 return true; // No-op
2573 bool erase_substring(size_t, size_t, size_t, size_t)
2575 return true; // No-op
2578 bool clear_table(size_t old_size)
2580 bool unordered = false;
2581 m_encoder.insert_empty_rows(0, old_size, 0, unordered);
2582 append_instruction();
2586 bool add_search_index(size_t)
2588 return true; // No-op
2591 bool remove_search_index(size_t)
2593 return true; // No-op
2596 bool set_link_type(size_t, LinkType)
2598 return true; // No-op
2601 bool insert_link_column(size_t col_idx, DataType, StringData, size_t target_table_idx, size_t backlink_col_ndx)
2603 m_encoder.erase_link_column(col_idx, target_table_idx, backlink_col_ndx);
2604 append_instruction();
2608 bool erase_link_column(size_t col_idx, size_t target_table_idx, size_t backlink_col_idx)
2610 DataType type = type_Link; // The real type of the column doesn't matter here,
2611 // but the encoder asserts that it's actually a link type.
2612 m_encoder.insert_link_column(col_idx, type, "", target_table_idx, backlink_col_idx);
2613 append_instruction();
2617 bool insert_column(size_t col_idx, DataType, StringData, bool)
2619 m_encoder.erase_column(col_idx);
2620 append_instruction();
2624 bool erase_column(size_t col_idx)
2626 m_encoder.insert_column(col_idx, DataType(), "");
2627 append_instruction();
2631 bool rename_column(size_t, StringData)
2633 return true; // No-op
2636 bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx)
2639 m_encoder.select_link_list(col_ndx, row_ndx, link_target_group_level_ndx);
2640 m_pending_lv_instr = get_inst();
2644 bool link_list_set(size_t row, size_t value, size_t prior_size)
2646 m_encoder.link_list_set(row, value, prior_size);
2647 append_instruction();
2651 bool link_list_insert(size_t link_ndx, size_t, size_t prior_size)
2653 m_encoder.link_list_erase(link_ndx, prior_size + 1);
2654 append_instruction();
2658 bool link_list_move(size_t from_link_ndx, size_t to_link_ndx)
2660 m_encoder.link_list_move(from_link_ndx, to_link_ndx);
2661 append_instruction();
2665 bool link_list_swap(size_t link1_ndx, size_t link2_ndx)
2667 m_encoder.link_list_swap(link1_ndx, link2_ndx);
2668 append_instruction();
2672 bool link_list_erase(size_t link_ndx, size_t prior_size)
2674 m_encoder.link_list_insert(link_ndx, 0, prior_size - 1);
2675 append_instruction();
2679 bool link_list_clear(size_t old_list_size)
2681 // Append in reverse order because the reversed log is itself applied
2682 // in reverse, and this way it generates all back-insertions rather than
2683 // all front-insertions
2684 for (size_t i = old_list_size; i > 0; --i) {
2685 m_encoder.link_list_insert(i - 1, 0, old_list_size - i);
2686 append_instruction();
2691 bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx)
2694 // FIXME: Is zero this right value to pass here, or should
2695 // TransactReverser::nullify_link() also have taken a
2696 // `target_group_level_ndx` argument.
2697 m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx);
2698 append_instruction();
2702 bool link_list_nullify(size_t link_ndx, size_t prior_size)
2704 m_encoder.link_list_insert(link_ndx, 0, prior_size - 1);
2705 append_instruction();
2710 _impl::TransactLogBufferStream m_buffer;
2711 _impl::TransactLogEncoder m_encoder{m_buffer};
2716 std::vector<Instr> m_instructions;
2717 size_t current_instr_start = 0;
2718 Instr m_pending_ts_instr{0, 0};
2719 Instr m_pending_ds_instr{0, 0};
2720 Instr m_pending_lv_instr{0, 0};
2725 instr.begin = current_instr_start;
2726 current_instr_start = transact_log_size();
2727 instr.end = current_instr_start;
2731 size_t transact_log_size() const
2733 REALM_ASSERT_3(m_encoder.write_position(), >=, m_buffer.transact_log_data());
2734 return m_encoder.write_position() - m_buffer.transact_log_data();
2737 void append_instruction()
2739 m_instructions.push_back(get_inst());
2742 void append_instruction(Instr instr)
2744 m_instructions.push_back(instr);
2747 void sync_select(Instr& pending_instr)
2749 if (pending_instr.begin != pending_instr.end) {
2750 append_instruction(pending_instr);
2751 pending_instr = {0, 0};
2755 void sync_linkview()
2757 sync_select(m_pending_lv_instr);
2760 void sync_descriptor()
2763 sync_select(m_pending_ds_instr);
2769 sync_select(m_pending_ts_instr);
2772 friend class ReversedNoCopyInputStream;
2776 class ReversedNoCopyInputStream : public NoCopyInputStream {
2778 ReversedNoCopyInputStream(TransactReverser& reverser)
2779 : m_instr_order(reverser.m_instructions)
2781 // push any pending select_table or select_descriptor into the buffer
2782 reverser.sync_table();
2784 m_buffer = reverser.m_buffer.transact_log_data();
2785 m_current = m_instr_order.size();
2788 bool next_block(const char*& begin, const char*& end) override
2790 if (m_current != 0) {
2792 begin = m_buffer + m_instr_order[m_current].begin;
2793 end = m_buffer + m_instr_order[m_current].end;
2794 return (end > begin);
2800 const char* m_buffer;
2801 std::vector<TransactReverser::Instr>& m_instr_order;
2805 } // namespace _impl
2806 } // namespace realm
2808 #endif // REALM_IMPL_TRANSACT_LOG_HPP