1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 
10 #include "db/version_edit.h"
11 
12 #include "db/blob/blob_index.h"
13 #include "db/version_set.h"
14 #include "logging/event_logger.h"
15 #include "rocksdb/slice.h"
16 #include "test_util/sync_point.h"
17 #include "util/coding.h"
18 #include "util/string_util.h"
19 
20 namespace ROCKSDB_NAMESPACE {
21 // The unknown file checksum.
22 const std::string kUnknownFileChecksum("");
23 // The unknown sst file checksum function name.
24 const std::string kUnknownFileChecksumFuncName("Unknown");
25 
26 namespace {
27 
28 // Tag numbers for serialized VersionEdit.  These numbers are written to
29 // disk and should not be changed. The number should be forward compatible so
30 // users can down-grade RocksDB safely. A future Tag is ignored by doing '&'
31 // between Tag and kTagSafeIgnoreMask field.
32 enum Tag : uint32_t {
33   kComparator = 1,
34   kLogNumber = 2,
35   kNextFileNumber = 3,
36   kLastSequence = 4,
37   kCompactPointer = 5,
38   kDeletedFile = 6,
39   kNewFile = 7,
40   // 8 was used for large value refs
41   kPrevLogNumber = 9,
42   kMinLogNumberToKeep = 10,
43 
44   // these are new formats divergent from open source leveldb
45   kNewFile2 = 100,
46   kNewFile3 = 102,
47   kNewFile4 = 103,      // 4th (the latest) format version of adding files
48   kColumnFamily = 200,  // specify column family for version edit
49   kColumnFamilyAdd = 201,
50   kColumnFamilyDrop = 202,
51   kMaxColumnFamily = 203,
52 
53   kInAtomicGroup = 300,
54 
55   // Mask for an unidentified tag from the future which can be safely ignored.
56   kTagSafeIgnoreMask = 1 << 13,
57 
58   // Forward compatible (aka ignorable) records
59   kDbId,
60   kBlobFileAddition,
61   kBlobFileGarbage,
62 };
63 
64 enum NewFileCustomTag : uint32_t {
65   kTerminate = 1,  // The end of customized fields
66   kNeedCompaction = 2,
67   // Since Manifest is not entirely forward-compatible, we currently encode
68   // kMinLogNumberToKeep as part of NewFile as a hack. This should be removed
69   // when manifest becomes forward-comptabile.
70   kMinLogNumberToKeepHack = 3,
71   kOldestBlobFileNumber = 4,
72   kOldestAncesterTime = 5,
73   kFileCreationTime = 6,
74   kFileChecksum = 7,
75   kFileChecksumFuncName = 8,
76 
77   // If this bit for the custom tag is set, opening DB should fail if
78   // we don't know this field.
79   kCustomTagNonSafeIgnoreMask = 1 << 6,
80 
81   // Forward incompatible (aka unignorable) fields
82   kPathId,
83 };
84 
85 }  // anonymous namespace
86 
PackFileNumberAndPathId(uint64_t number,uint64_t path_id)87 uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id) {
88   assert(number <= kFileNumberMask);
89   return number | (path_id * (kFileNumberMask + 1));
90 }
91 
UpdateBoundaries(const Slice & key,const Slice & value,SequenceNumber seqno,ValueType value_type)92 void FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
93                                     SequenceNumber seqno,
94                                     ValueType value_type) {
95   if (smallest.size() == 0) {
96     smallest.DecodeFrom(key);
97   }
98   largest.DecodeFrom(key);
99   fd.smallest_seqno = std::min(fd.smallest_seqno, seqno);
100   fd.largest_seqno = std::max(fd.largest_seqno, seqno);
101 
102 #ifndef ROCKSDB_LITE
103   if (value_type == kTypeBlobIndex) {
104     BlobIndex blob_index;
105     const Status s = blob_index.DecodeFrom(value);
106     if (!s.ok()) {
107       return;
108     }
109 
110     if (blob_index.IsInlined()) {
111       return;
112     }
113 
114     if (blob_index.HasTTL()) {
115       return;
116     }
117 
118     // Paranoid check: this should not happen because BlobDB numbers the blob
119     // files starting from 1.
120     if (blob_index.file_number() == kInvalidBlobFileNumber) {
121       return;
122     }
123 
124     if (oldest_blob_file_number == kInvalidBlobFileNumber ||
125         oldest_blob_file_number > blob_index.file_number()) {
126       oldest_blob_file_number = blob_index.file_number();
127     }
128   }
129 #else
130   (void)value;
131   (void)value_type;
132 #endif
133 }
134 
Clear()135 void VersionEdit::Clear() {
136   max_level_ = 0;
137   db_id_.clear();
138   comparator_.clear();
139   log_number_ = 0;
140   prev_log_number_ = 0;
141   next_file_number_ = 0;
142   max_column_family_ = 0;
143   min_log_number_to_keep_ = 0;
144   last_sequence_ = 0;
145   has_db_id_ = false;
146   has_comparator_ = false;
147   has_log_number_ = false;
148   has_prev_log_number_ = false;
149   has_next_file_number_ = false;
150   has_max_column_family_ = false;
151   has_min_log_number_to_keep_ = false;
152   has_last_sequence_ = false;
153   deleted_files_.clear();
154   new_files_.clear();
155   blob_file_additions_.clear();
156   blob_file_garbages_.clear();
157   column_family_ = 0;
158   is_column_family_add_ = false;
159   is_column_family_drop_ = false;
160   column_family_name_.clear();
161   is_in_atomic_group_ = false;
162   remaining_entries_ = 0;
163 }
164 
EncodeTo(std::string * dst) const165 bool VersionEdit::EncodeTo(std::string* dst) const {
166   if (has_db_id_) {
167     PutVarint32(dst, kDbId);
168     PutLengthPrefixedSlice(dst, db_id_);
169   }
170   if (has_comparator_) {
171     PutVarint32(dst, kComparator);
172     PutLengthPrefixedSlice(dst, comparator_);
173   }
174   if (has_log_number_) {
175     PutVarint32Varint64(dst, kLogNumber, log_number_);
176   }
177   if (has_prev_log_number_) {
178     PutVarint32Varint64(dst, kPrevLogNumber, prev_log_number_);
179   }
180   if (has_next_file_number_) {
181     PutVarint32Varint64(dst, kNextFileNumber, next_file_number_);
182   }
183   if (has_max_column_family_) {
184     PutVarint32Varint32(dst, kMaxColumnFamily, max_column_family_);
185   }
186   if (has_last_sequence_) {
187     PutVarint32Varint64(dst, kLastSequence, last_sequence_);
188   }
189   for (const auto& deleted : deleted_files_) {
190     PutVarint32Varint32Varint64(dst, kDeletedFile, deleted.first /* level */,
191                                 deleted.second /* file number */);
192   }
193 
194   bool min_log_num_written = false;
195   for (size_t i = 0; i < new_files_.size(); i++) {
196     const FileMetaData& f = new_files_[i].second;
197     if (!f.smallest.Valid() || !f.largest.Valid()) {
198       return false;
199     }
200     PutVarint32(dst, kNewFile4);
201     PutVarint32Varint64(dst, new_files_[i].first /* level */, f.fd.GetNumber());
202     PutVarint64(dst, f.fd.GetFileSize());
203     PutLengthPrefixedSlice(dst, f.smallest.Encode());
204     PutLengthPrefixedSlice(dst, f.largest.Encode());
205     PutVarint64Varint64(dst, f.fd.smallest_seqno, f.fd.largest_seqno);
206     // Customized fields' format:
207     // +-----------------------------+
208     // | 1st field's tag (varint32)  |
209     // +-----------------------------+
210     // | 1st field's size (varint32) |
211     // +-----------------------------+
212     // |    bytes for 1st field      |
213     // |  (based on size decoded)    |
214     // +-----------------------------+
215     // |                             |
216     // |          ......             |
217     // |                             |
218     // +-----------------------------+
219     // | last field's size (varint32)|
220     // +-----------------------------+
221     // |    bytes for last field     |
222     // |  (based on size decoded)    |
223     // +-----------------------------+
224     // | terminating tag (varint32)  |
225     // +-----------------------------+
226     //
227     // Customized encoding for fields:
228     //   tag kPathId: 1 byte as path_id
229     //   tag kNeedCompaction:
230     //        now only can take one char value 1 indicating need-compaction
231     //
232     PutVarint32(dst, NewFileCustomTag::kOldestAncesterTime);
233     std::string varint_oldest_ancester_time;
234     PutVarint64(&varint_oldest_ancester_time, f.oldest_ancester_time);
235     TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintOldestAncesterTime",
236                              &varint_oldest_ancester_time);
237     PutLengthPrefixedSlice(dst, Slice(varint_oldest_ancester_time));
238 
239     PutVarint32(dst, NewFileCustomTag::kFileCreationTime);
240     std::string varint_file_creation_time;
241     PutVarint64(&varint_file_creation_time, f.file_creation_time);
242     TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintFileCreationTime",
243                              &varint_file_creation_time);
244     PutLengthPrefixedSlice(dst, Slice(varint_file_creation_time));
245 
246     PutVarint32(dst, NewFileCustomTag::kFileChecksum);
247     PutLengthPrefixedSlice(dst, Slice(f.file_checksum));
248 
249     PutVarint32(dst, NewFileCustomTag::kFileChecksumFuncName);
250     PutLengthPrefixedSlice(dst, Slice(f.file_checksum_func_name));
251 
252     if (f.fd.GetPathId() != 0) {
253       PutVarint32(dst, NewFileCustomTag::kPathId);
254       char p = static_cast<char>(f.fd.GetPathId());
255       PutLengthPrefixedSlice(dst, Slice(&p, 1));
256     }
257     if (f.marked_for_compaction) {
258       PutVarint32(dst, NewFileCustomTag::kNeedCompaction);
259       char p = static_cast<char>(1);
260       PutLengthPrefixedSlice(dst, Slice(&p, 1));
261     }
262     if (has_min_log_number_to_keep_ && !min_log_num_written) {
263       PutVarint32(dst, NewFileCustomTag::kMinLogNumberToKeepHack);
264       std::string varint_log_number;
265       PutFixed64(&varint_log_number, min_log_number_to_keep_);
266       PutLengthPrefixedSlice(dst, Slice(varint_log_number));
267       min_log_num_written = true;
268     }
269     if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
270       PutVarint32(dst, NewFileCustomTag::kOldestBlobFileNumber);
271       std::string oldest_blob_file_number;
272       PutVarint64(&oldest_blob_file_number, f.oldest_blob_file_number);
273       PutLengthPrefixedSlice(dst, Slice(oldest_blob_file_number));
274     }
275     TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
276                              dst);
277 
278     PutVarint32(dst, NewFileCustomTag::kTerminate);
279   }
280 
281   for (const auto& blob_file_addition : blob_file_additions_) {
282     PutVarint32(dst, kBlobFileAddition);
283     blob_file_addition.EncodeTo(dst);
284   }
285 
286   for (const auto& blob_file_garbage : blob_file_garbages_) {
287     PutVarint32(dst, kBlobFileGarbage);
288     blob_file_garbage.EncodeTo(dst);
289   }
290 
291   // 0 is default and does not need to be explicitly written
292   if (column_family_ != 0) {
293     PutVarint32Varint32(dst, kColumnFamily, column_family_);
294   }
295 
296   if (is_column_family_add_) {
297     PutVarint32(dst, kColumnFamilyAdd);
298     PutLengthPrefixedSlice(dst, Slice(column_family_name_));
299   }
300 
301   if (is_column_family_drop_) {
302     PutVarint32(dst, kColumnFamilyDrop);
303   }
304 
305   if (is_in_atomic_group_) {
306     PutVarint32(dst, kInAtomicGroup);
307     PutVarint32(dst, remaining_entries_);
308   }
309   return true;
310 }
311 
GetInternalKey(Slice * input,InternalKey * dst)312 static bool GetInternalKey(Slice* input, InternalKey* dst) {
313   Slice str;
314   if (GetLengthPrefixedSlice(input, &str)) {
315     dst->DecodeFrom(str);
316     return dst->Valid();
317   } else {
318     return false;
319   }
320 }
321 
GetLevel(Slice * input,int * level,const char **)322 bool VersionEdit::GetLevel(Slice* input, int* level, const char** /*msg*/) {
323   uint32_t v = 0;
324   if (GetVarint32(input, &v)) {
325     *level = v;
326     if (max_level_ < *level) {
327       max_level_ = *level;
328     }
329     return true;
330   } else {
331     return false;
332   }
333 }
334 
DecodeNewFile4From(Slice * input)335 const char* VersionEdit::DecodeNewFile4From(Slice* input) {
336   const char* msg = nullptr;
337   int level = 0;
338   FileMetaData f;
339   uint64_t number = 0;
340   uint32_t path_id = 0;
341   uint64_t file_size = 0;
342   SequenceNumber smallest_seqno = 0;
343   SequenceNumber largest_seqno = kMaxSequenceNumber;
344   if (GetLevel(input, &level, &msg) && GetVarint64(input, &number) &&
345       GetVarint64(input, &file_size) && GetInternalKey(input, &f.smallest) &&
346       GetInternalKey(input, &f.largest) &&
347       GetVarint64(input, &smallest_seqno) &&
348       GetVarint64(input, &largest_seqno)) {
349     // See comments in VersionEdit::EncodeTo() for format of customized fields
350     while (true) {
351       uint32_t custom_tag = 0;
352       Slice field;
353       if (!GetVarint32(input, &custom_tag)) {
354         return "new-file4 custom field";
355       }
356       if (custom_tag == kTerminate) {
357         break;
358       }
359       if (!GetLengthPrefixedSlice(input, &field)) {
360         return "new-file4 custom field length prefixed slice error";
361       }
362       switch (custom_tag) {
363         case kPathId:
364           if (field.size() != 1) {
365             return "path_id field wrong size";
366           }
367           path_id = field[0];
368           if (path_id > 3) {
369             return "path_id wrong vaue";
370           }
371           break;
372         case kOldestAncesterTime:
373           if (!GetVarint64(&field, &f.oldest_ancester_time)) {
374             return "invalid oldest ancester time";
375           }
376           break;
377         case kFileCreationTime:
378           if (!GetVarint64(&field, &f.file_creation_time)) {
379             return "invalid file creation time";
380           }
381           break;
382         case kFileChecksum:
383           f.file_checksum = field.ToString();
384           break;
385         case kFileChecksumFuncName:
386           f.file_checksum_func_name = field.ToString();
387           break;
388         case kNeedCompaction:
389           if (field.size() != 1) {
390             return "need_compaction field wrong size";
391           }
392           f.marked_for_compaction = (field[0] == 1);
393           break;
394         case kMinLogNumberToKeepHack:
395           // This is a hack to encode kMinLogNumberToKeep in a
396           // forward-compatible fashion.
397           if (!GetFixed64(&field, &min_log_number_to_keep_)) {
398             return "deleted log number malformatted";
399           }
400           has_min_log_number_to_keep_ = true;
401           break;
402         case kOldestBlobFileNumber:
403           if (!GetVarint64(&field, &f.oldest_blob_file_number)) {
404             return "invalid oldest blob file number";
405           }
406           break;
407         default:
408           if ((custom_tag & kCustomTagNonSafeIgnoreMask) != 0) {
409             // Should not proceed if cannot understand it
410             return "new-file4 custom field not supported";
411           }
412           break;
413       }
414     }
415   } else {
416     return "new-file4 entry";
417   }
418   f.fd =
419       FileDescriptor(number, path_id, file_size, smallest_seqno, largest_seqno);
420   new_files_.push_back(std::make_pair(level, f));
421   return nullptr;
422 }
423 
DecodeFrom(const Slice & src)424 Status VersionEdit::DecodeFrom(const Slice& src) {
425   Clear();
426   Slice input = src;
427   const char* msg = nullptr;
428   uint32_t tag = 0;
429 
430   // Temporary storage for parsing
431   int level = 0;
432   FileMetaData f;
433   Slice str;
434   InternalKey key;
435   while (msg == nullptr && GetVarint32(&input, &tag)) {
436     switch (tag) {
437       case kDbId:
438         if (GetLengthPrefixedSlice(&input, &str)) {
439           db_id_ = str.ToString();
440           has_db_id_ = true;
441         } else {
442           msg = "db id";
443         }
444         break;
445       case kComparator:
446         if (GetLengthPrefixedSlice(&input, &str)) {
447           comparator_ = str.ToString();
448           has_comparator_ = true;
449         } else {
450           msg = "comparator name";
451         }
452         break;
453 
454       case kLogNumber:
455         if (GetVarint64(&input, &log_number_)) {
456           has_log_number_ = true;
457         } else {
458           msg = "log number";
459         }
460         break;
461 
462       case kPrevLogNumber:
463         if (GetVarint64(&input, &prev_log_number_)) {
464           has_prev_log_number_ = true;
465         } else {
466           msg = "previous log number";
467         }
468         break;
469 
470       case kNextFileNumber:
471         if (GetVarint64(&input, &next_file_number_)) {
472           has_next_file_number_ = true;
473         } else {
474           msg = "next file number";
475         }
476         break;
477 
478       case kMaxColumnFamily:
479         if (GetVarint32(&input, &max_column_family_)) {
480           has_max_column_family_ = true;
481         } else {
482           msg = "max column family";
483         }
484         break;
485 
486       case kMinLogNumberToKeep:
487         if (GetVarint64(&input, &min_log_number_to_keep_)) {
488           has_min_log_number_to_keep_ = true;
489         } else {
490           msg = "min log number to kee";
491         }
492         break;
493 
494       case kLastSequence:
495         if (GetVarint64(&input, &last_sequence_)) {
496           has_last_sequence_ = true;
497         } else {
498           msg = "last sequence number";
499         }
500         break;
501 
502       case kCompactPointer:
503         if (GetLevel(&input, &level, &msg) &&
504             GetInternalKey(&input, &key)) {
505           // we don't use compact pointers anymore,
506           // but we should not fail if they are still
507           // in manifest
508         } else {
509           if (!msg) {
510             msg = "compaction pointer";
511           }
512         }
513         break;
514 
515       case kDeletedFile: {
516         uint64_t number = 0;
517         if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number)) {
518           deleted_files_.insert(std::make_pair(level, number));
519         } else {
520           if (!msg) {
521             msg = "deleted file";
522           }
523         }
524         break;
525       }
526 
527       case kNewFile: {
528         uint64_t number = 0;
529         uint64_t file_size = 0;
530         if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
531             GetVarint64(&input, &file_size) &&
532             GetInternalKey(&input, &f.smallest) &&
533             GetInternalKey(&input, &f.largest)) {
534           f.fd = FileDescriptor(number, 0, file_size);
535           new_files_.push_back(std::make_pair(level, f));
536         } else {
537           if (!msg) {
538             msg = "new-file entry";
539           }
540         }
541         break;
542       }
543       case kNewFile2: {
544         uint64_t number = 0;
545         uint64_t file_size = 0;
546         SequenceNumber smallest_seqno = 0;
547         SequenceNumber largest_seqno = kMaxSequenceNumber;
548         if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
549             GetVarint64(&input, &file_size) &&
550             GetInternalKey(&input, &f.smallest) &&
551             GetInternalKey(&input, &f.largest) &&
552             GetVarint64(&input, &smallest_seqno) &&
553             GetVarint64(&input, &largest_seqno)) {
554           f.fd = FileDescriptor(number, 0, file_size, smallest_seqno,
555                                 largest_seqno);
556           new_files_.push_back(std::make_pair(level, f));
557         } else {
558           if (!msg) {
559             msg = "new-file2 entry";
560           }
561         }
562         break;
563       }
564 
565       case kNewFile3: {
566         uint64_t number = 0;
567         uint32_t path_id = 0;
568         uint64_t file_size = 0;
569         SequenceNumber smallest_seqno = 0;
570         SequenceNumber largest_seqno = kMaxSequenceNumber;
571         if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&
572             GetVarint32(&input, &path_id) && GetVarint64(&input, &file_size) &&
573             GetInternalKey(&input, &f.smallest) &&
574             GetInternalKey(&input, &f.largest) &&
575             GetVarint64(&input, &smallest_seqno) &&
576             GetVarint64(&input, &largest_seqno)) {
577           f.fd = FileDescriptor(number, path_id, file_size, smallest_seqno,
578                                 largest_seqno);
579           new_files_.push_back(std::make_pair(level, f));
580         } else {
581           if (!msg) {
582             msg = "new-file3 entry";
583           }
584         }
585         break;
586       }
587 
588       case kNewFile4: {
589         msg = DecodeNewFile4From(&input);
590         break;
591       }
592 
593       case kBlobFileAddition: {
594         BlobFileAddition blob_file_addition;
595         const Status s = blob_file_addition.DecodeFrom(&input);
596         if (!s.ok()) {
597           return s;
598         }
599 
600         blob_file_additions_.emplace_back(blob_file_addition);
601         break;
602       }
603 
604       case kBlobFileGarbage: {
605         BlobFileGarbage blob_file_garbage;
606         const Status s = blob_file_garbage.DecodeFrom(&input);
607         if (!s.ok()) {
608           return s;
609         }
610 
611         blob_file_garbages_.emplace_back(blob_file_garbage);
612         break;
613       }
614 
615       case kColumnFamily:
616         if (!GetVarint32(&input, &column_family_)) {
617           if (!msg) {
618             msg = "set column family id";
619           }
620         }
621         break;
622 
623       case kColumnFamilyAdd:
624         if (GetLengthPrefixedSlice(&input, &str)) {
625           is_column_family_add_ = true;
626           column_family_name_ = str.ToString();
627         } else {
628           if (!msg) {
629             msg = "column family add";
630           }
631         }
632         break;
633 
634       case kColumnFamilyDrop:
635         is_column_family_drop_ = true;
636         break;
637 
638       case kInAtomicGroup:
639         is_in_atomic_group_ = true;
640         if (!GetVarint32(&input, &remaining_entries_)) {
641           if (!msg) {
642             msg = "remaining entries";
643           }
644         }
645         break;
646 
647       default:
648         if (tag & kTagSafeIgnoreMask) {
649           // Tag from future which can be safely ignored.
650           // The next field must be the length of the entry.
651           uint32_t field_len;
652           if (!GetVarint32(&input, &field_len) ||
653               static_cast<size_t>(field_len) > input.size()) {
654             if (!msg) {
655               msg = "safely ignoreable tag length error";
656             }
657           } else {
658             input.remove_prefix(static_cast<size_t>(field_len));
659           }
660         } else {
661           msg = "unknown tag";
662         }
663         break;
664     }
665   }
666 
667   if (msg == nullptr && !input.empty()) {
668     msg = "invalid tag";
669   }
670 
671   Status result;
672   if (msg != nullptr) {
673     result = Status::Corruption("VersionEdit", msg);
674   }
675   return result;
676 }
677 
DebugString(bool hex_key) const678 std::string VersionEdit::DebugString(bool hex_key) const {
679   std::string r;
680   r.append("VersionEdit {");
681   if (has_db_id_) {
682     r.append("\n  DB ID: ");
683     r.append(db_id_);
684   }
685   if (has_comparator_) {
686     r.append("\n  Comparator: ");
687     r.append(comparator_);
688   }
689   if (has_log_number_) {
690     r.append("\n  LogNumber: ");
691     AppendNumberTo(&r, log_number_);
692   }
693   if (has_prev_log_number_) {
694     r.append("\n  PrevLogNumber: ");
695     AppendNumberTo(&r, prev_log_number_);
696   }
697   if (has_next_file_number_) {
698     r.append("\n  NextFileNumber: ");
699     AppendNumberTo(&r, next_file_number_);
700   }
701   if (has_max_column_family_) {
702     r.append("\n  MaxColumnFamily: ");
703     AppendNumberTo(&r, max_column_family_);
704   }
705   if (has_min_log_number_to_keep_) {
706     r.append("\n  MinLogNumberToKeep: ");
707     AppendNumberTo(&r, min_log_number_to_keep_);
708   }
709   if (has_last_sequence_) {
710     r.append("\n  LastSeq: ");
711     AppendNumberTo(&r, last_sequence_);
712   }
713   for (const auto& deleted_file : deleted_files_) {
714     r.append("\n  DeleteFile: ");
715     AppendNumberTo(&r, deleted_file.first);
716     r.append(" ");
717     AppendNumberTo(&r, deleted_file.second);
718   }
719   for (size_t i = 0; i < new_files_.size(); i++) {
720     const FileMetaData& f = new_files_[i].second;
721     r.append("\n  AddFile: ");
722     AppendNumberTo(&r, new_files_[i].first);
723     r.append(" ");
724     AppendNumberTo(&r, f.fd.GetNumber());
725     r.append(" ");
726     AppendNumberTo(&r, f.fd.GetFileSize());
727     r.append(" ");
728     r.append(f.smallest.DebugString(hex_key));
729     r.append(" .. ");
730     r.append(f.largest.DebugString(hex_key));
731     if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
732       r.append(" blob_file:");
733       AppendNumberTo(&r, f.oldest_blob_file_number);
734     }
735     r.append(" oldest_ancester_time:");
736     AppendNumberTo(&r, f.oldest_ancester_time);
737     r.append(" file_creation_time:");
738     AppendNumberTo(&r, f.file_creation_time);
739     r.append(" file_checksum:");
740     r.append(f.file_checksum);
741     r.append(" file_checksum_func_name: ");
742     r.append(f.file_checksum_func_name);
743   }
744 
745   for (const auto& blob_file_addition : blob_file_additions_) {
746     r.append("\n  BlobFileAddition: ");
747     r.append(blob_file_addition.DebugString());
748   }
749 
750   for (const auto& blob_file_garbage : blob_file_garbages_) {
751     r.append("\n  BlobFileGarbage: ");
752     r.append(blob_file_garbage.DebugString());
753   }
754 
755   r.append("\n  ColumnFamily: ");
756   AppendNumberTo(&r, column_family_);
757   if (is_column_family_add_) {
758     r.append("\n  ColumnFamilyAdd: ");
759     r.append(column_family_name_);
760   }
761   if (is_column_family_drop_) {
762     r.append("\n  ColumnFamilyDrop");
763   }
764   if (is_in_atomic_group_) {
765     r.append("\n  AtomicGroup: ");
766     AppendNumberTo(&r, remaining_entries_);
767     r.append(" entries remains");
768   }
769   r.append("\n}\n");
770   return r;
771 }
772 
DebugJSON(int edit_num,bool hex_key) const773 std::string VersionEdit::DebugJSON(int edit_num, bool hex_key) const {
774   JSONWriter jw;
775   jw << "EditNumber" << edit_num;
776 
777   if (has_db_id_) {
778     jw << "DB ID" << db_id_;
779   }
780   if (has_comparator_) {
781     jw << "Comparator" << comparator_;
782   }
783   if (has_log_number_) {
784     jw << "LogNumber" << log_number_;
785   }
786   if (has_prev_log_number_) {
787     jw << "PrevLogNumber" << prev_log_number_;
788   }
789   if (has_next_file_number_) {
790     jw << "NextFileNumber" << next_file_number_;
791   }
792   if (has_max_column_family_) {
793     jw << "MaxColumnFamily" << max_column_family_;
794   }
795   if (has_min_log_number_to_keep_) {
796     jw << "MinLogNumberToKeep" << min_log_number_to_keep_;
797   }
798   if (has_last_sequence_) {
799     jw << "LastSeq" << last_sequence_;
800   }
801 
802   if (!deleted_files_.empty()) {
803     jw << "DeletedFiles";
804     jw.StartArray();
805 
806     for (const auto& deleted_file : deleted_files_) {
807       jw.StartArrayedObject();
808       jw << "Level" << deleted_file.first;
809       jw << "FileNumber" << deleted_file.second;
810       jw.EndArrayedObject();
811     }
812 
813     jw.EndArray();
814   }
815 
816   if (!new_files_.empty()) {
817     jw << "AddedFiles";
818     jw.StartArray();
819 
820     for (size_t i = 0; i < new_files_.size(); i++) {
821       jw.StartArrayedObject();
822       jw << "Level" << new_files_[i].first;
823       const FileMetaData& f = new_files_[i].second;
824       jw << "FileNumber" << f.fd.GetNumber();
825       jw << "FileSize" << f.fd.GetFileSize();
826       jw << "SmallestIKey" << f.smallest.DebugString(hex_key);
827       jw << "LargestIKey" << f.largest.DebugString(hex_key);
828       if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
829         jw << "OldestBlobFile" << f.oldest_blob_file_number;
830       }
831       jw.EndArrayedObject();
832     }
833 
834     jw.EndArray();
835   }
836 
837   if (!blob_file_additions_.empty()) {
838     jw << "BlobFileAdditions";
839 
840     jw.StartArray();
841 
842     for (const auto& blob_file_addition : blob_file_additions_) {
843       jw.StartArrayedObject();
844       jw << blob_file_addition;
845       jw.EndArrayedObject();
846     }
847 
848     jw.EndArray();
849   }
850 
851   if (!blob_file_garbages_.empty()) {
852     jw << "BlobFileGarbages";
853 
854     jw.StartArray();
855 
856     for (const auto& blob_file_garbage : blob_file_garbages_) {
857       jw.StartArrayedObject();
858       jw << blob_file_garbage;
859       jw.EndArrayedObject();
860     }
861 
862     jw.EndArray();
863   }
864 
865   jw << "ColumnFamily" << column_family_;
866 
867   if (is_column_family_add_) {
868     jw << "ColumnFamilyAdd" << column_family_name_;
869   }
870   if (is_column_family_drop_) {
871     jw << "ColumnFamilyDrop" << column_family_name_;
872   }
873   if (is_in_atomic_group_) {
874     jw << "AtomicGroup" << remaining_entries_;
875   }
876 
877   jw.EndObject();
878 
879   return jw.Get();
880 }
881 
882 }  // namespace ROCKSDB_NAMESPACE
883