sst_file_dumper.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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. #include "table/sst_file_dumper.h"
  7. #include <chrono>
  8. #include <cinttypes>
  9. #include <iostream>
  10. #include <map>
  11. #include <memory>
  12. #include <sstream>
  13. #include <vector>
  14. #include "db/blob/blob_index.h"
  15. #include "db/memtable.h"
  16. #include "db/wide/wide_column_serialization.h"
  17. #include "db/wide/wide_columns_helper.h"
  18. #include "db/write_batch_internal.h"
  19. #include "options/cf_options.h"
  20. #include "port/port.h"
  21. #include "rocksdb/db.h"
  22. #include "rocksdb/env.h"
  23. #include "rocksdb/iterator.h"
  24. #include "rocksdb/slice_transform.h"
  25. #include "rocksdb/status.h"
  26. #include "rocksdb/table_properties.h"
  27. #include "rocksdb/utilities/ldb_cmd.h"
  28. #include "table/block_based/block.h"
  29. #include "table/block_based/block_based_table_builder.h"
  30. #include "table/block_based/block_based_table_factory.h"
  31. #include "table/block_based/block_builder.h"
  32. #include "table/format.h"
  33. #include "table/meta_blocks.h"
  34. #include "table/plain/plain_table_factory.h"
  35. #include "table/table_reader.h"
  36. #include "util/compression.h"
  37. #include "util/random.h"
  38. #include "util/udt_util.h"
  39. namespace ROCKSDB_NAMESPACE {
  40. SstFileDumper::SstFileDumper(const Options& options,
  41. const std::string& file_path,
  42. Temperature file_temp, size_t readahead_size,
  43. bool verify_checksum, bool output_hex,
  44. bool decode_blob_index, const EnvOptions& soptions,
  45. bool silent)
  46. : file_name_(file_path),
  47. read_num_(0),
  48. file_temp_(file_temp),
  49. output_hex_(output_hex),
  50. decode_blob_index_(decode_blob_index),
  51. soptions_(soptions),
  52. silent_(silent),
  53. options_(options),
  54. ioptions_(options_),
  55. moptions_(ColumnFamilyOptions(options_)),
  56. // TODO: plumb Env::IOActivity, Env::IOPriority
  57. read_options_(verify_checksum, false),
  58. internal_comparator_(BytewiseComparator()) {
  59. read_options_.readahead_size = readahead_size;
  60. if (!silent_) {
  61. fprintf(stdout, "Process %s\n", file_path.c_str());
  62. }
  63. init_result_ = GetTableReader(file_name_);
  64. }
  65. const char* testFileName = "test_file_name";
  66. Status SstFileDumper::GetTableReader(const std::string& file_path) {
  67. // Warning about 'magic_number' being uninitialized shows up only in UBsan
  68. // builds. Though access is guarded by 's.ok()' checks, fix the issue to
  69. // avoid any warnings.
  70. uint64_t magic_number = Footer::kNullTableMagicNumber;
  71. // read table magic number
  72. Footer footer;
  73. const auto& fs = options_.env->GetFileSystem();
  74. std::unique_ptr<FSRandomAccessFile> file;
  75. uint64_t file_size = 0;
  76. FileOptions fopts = soptions_;
  77. fopts.temperature = file_temp_;
  78. Status s = fs->NewRandomAccessFile(file_path, fopts, &file, nullptr);
  79. if (s.ok()) {
  80. // check empty file
  81. // if true, skip further processing of this file
  82. s = fs->GetFileSize(file_path, IOOptions(), &file_size, nullptr);
  83. if (s.ok()) {
  84. if (file_size == 0) {
  85. return Status::Aborted(file_path, "Empty file");
  86. }
  87. }
  88. }
  89. file_.reset(new RandomAccessFileReader(std::move(file), file_path));
  90. FilePrefetchBuffer prefetch_buffer(ReadaheadParams(),
  91. !fopts.use_mmap_reads /* enable */,
  92. false /* track_min_offset */);
  93. if (s.ok()) {
  94. const uint64_t kSstDumpTailPrefetchSize = 512 * 1024;
  95. uint64_t prefetch_size = (file_size > kSstDumpTailPrefetchSize)
  96. ? kSstDumpTailPrefetchSize
  97. : file_size;
  98. uint64_t prefetch_off = file_size - prefetch_size;
  99. IOOptions opts;
  100. s = prefetch_buffer.Prefetch(opts, file_.get(), prefetch_off,
  101. static_cast<size_t>(prefetch_size));
  102. s = ReadFooterFromFile(opts, file_.get(), *fs, &prefetch_buffer, file_size,
  103. &footer);
  104. }
  105. if (s.ok()) {
  106. magic_number = footer.table_magic_number();
  107. }
  108. if (s.ok()) {
  109. if (magic_number == kPlainTableMagicNumber ||
  110. magic_number == kLegacyPlainTableMagicNumber ||
  111. magic_number == kCuckooTableMagicNumber) {
  112. soptions_.use_mmap_reads = true;
  113. fopts.use_mmap_reads = soptions_.use_mmap_reads;
  114. if (magic_number == kCuckooTableMagicNumber) {
  115. fopts = soptions_;
  116. fopts.temperature = file_temp_;
  117. }
  118. fs->NewRandomAccessFile(file_path, fopts, &file, nullptr);
  119. file_.reset(new RandomAccessFileReader(std::move(file), file_path));
  120. }
  121. // For old sst format, ReadTableProperties might fail but file can be read
  122. if (ReadTableProperties(magic_number, file_.get(), file_size,
  123. (magic_number == kBlockBasedTableMagicNumber)
  124. ? &prefetch_buffer
  125. : nullptr)
  126. .ok()) {
  127. s = SetTableOptionsByMagicNumber(magic_number);
  128. if (s.ok()) {
  129. if (table_properties_ && !table_properties_->comparator_name.empty()) {
  130. ConfigOptions config_options;
  131. const Comparator* user_comparator = nullptr;
  132. s = Comparator::CreateFromString(config_options,
  133. table_properties_->comparator_name,
  134. &user_comparator);
  135. if (s.ok()) {
  136. assert(user_comparator);
  137. internal_comparator_ = InternalKeyComparator(user_comparator);
  138. }
  139. }
  140. }
  141. } else {
  142. s = SetOldTableOptions();
  143. }
  144. options_.comparator = internal_comparator_.user_comparator();
  145. {
  146. Status status = ReadMetaIndexBlockInFile(
  147. file_.get(), file_size, magic_number, ImmutableOptions(options_),
  148. ReadOptions(), &meta_index_contents_);
  149. // Ignore any errors since this is required for a specific CLI option
  150. status.PermitUncheckedError();
  151. }
  152. }
  153. if (s.ok()) {
  154. s = NewTableReader(ioptions_, soptions_, internal_comparator_, file_size,
  155. &table_reader_);
  156. }
  157. return s;
  158. }
  159. Status SstFileDumper::NewTableReader(
  160. const ImmutableOptions& /*ioptions*/, const EnvOptions& /*soptions*/,
  161. const InternalKeyComparator& /*internal_comparator*/, uint64_t file_size,
  162. std::unique_ptr<TableReader>* /*table_reader*/) {
  163. auto t_opt = TableReaderOptions(
  164. ioptions_, moptions_.prefix_extractor,
  165. moptions_.compression_manager.get(), soptions_, internal_comparator_,
  166. 0 /* block_protection_bytes_per_key */, false /* skip_filters */,
  167. false /* immortal */, true /* force_direct_prefetch */, -1 /* level */,
  168. nullptr /* block_cache_tracer */, 0 /* max_file_size_for_l0_meta_pin */,
  169. "" /* cur_db_session_id */, 0 /* cur_file_num */, {} /* unique_id */,
  170. 0 /* largest_seqno */, 0 /* tail_size */,
  171. table_properties_ == nullptr
  172. ? true
  173. : static_cast<bool>(
  174. table_properties_->user_defined_timestamps_persisted));
  175. // Allow open file with global sequence number for backward compatibility.
  176. t_opt.largest_seqno = kMaxSequenceNumber;
  177. // We need to turn off pre-fetching of index and filter nodes for
  178. // BlockBasedTable
  179. if (options_.table_factory->IsInstanceOf(
  180. TableFactory::kBlockBasedTableName())) {
  181. return options_.table_factory->NewTableReader(t_opt, std::move(file_),
  182. file_size, &table_reader_,
  183. /*enable_prefetch=*/false);
  184. }
  185. // For all other factory implementation
  186. return options_.table_factory->NewTableReader(t_opt, std::move(file_),
  187. file_size, &table_reader_);
  188. }
  189. Status SstFileDumper::VerifyChecksum() {
  190. assert(read_options_.verify_checksums);
  191. // We could pass specific readahead setting into read options if needed.
  192. return table_reader_->VerifyChecksum(read_options_,
  193. TableReaderCaller::kSSTDumpTool);
  194. }
  195. Status SstFileDumper::DumpTable(const std::string& out_filename) {
  196. std::unique_ptr<WritableFile> out_file;
  197. Env* env = options_.env;
  198. Status s = env->NewWritableFile(out_filename, &out_file, soptions_);
  199. if (s.ok()) {
  200. s = table_reader_->DumpTable(out_file.get());
  201. }
  202. if (!s.ok()) {
  203. // close the file before return error, ignore the close error if there's any
  204. out_file->Close().PermitUncheckedError();
  205. return s;
  206. }
  207. return out_file->Close();
  208. }
  209. Status SstFileDumper::CalculateCompressedTableSize(
  210. const TableBuilderOptions& tb_options, TableProperties* props,
  211. std::chrono::microseconds* write_time,
  212. std::chrono::microseconds* read_time) {
  213. std::unique_ptr<Env> env(NewMemEnv(options_.env));
  214. std::unique_ptr<WritableFileWriter> dest_writer;
  215. Status s =
  216. WritableFileWriter::Create(env->GetFileSystem(), testFileName,
  217. FileOptions(soptions_), &dest_writer, nullptr);
  218. if (!s.ok()) {
  219. return s;
  220. }
  221. std::chrono::steady_clock::time_point start =
  222. std::chrono::steady_clock::now();
  223. std::unique_ptr<TableBuilder> table_builder{
  224. tb_options.moptions.table_factory->NewTableBuilder(tb_options,
  225. dest_writer.get())};
  226. std::unique_ptr<InternalIterator> iter(table_reader_->NewIterator(
  227. read_options_, moptions_.prefix_extractor.get(), /*arena=*/nullptr,
  228. /*skip_filters=*/false, TableReaderCaller::kSSTDumpTool));
  229. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  230. table_builder->Add(iter->key(), iter->value());
  231. }
  232. s = iter->status();
  233. if (!s.ok()) {
  234. return s;
  235. }
  236. iter.reset();
  237. s = table_builder->Finish();
  238. *write_time = std::chrono::duration_cast<std::chrono::microseconds>(
  239. std::chrono::steady_clock::now() - start);
  240. if (!s.ok()) {
  241. return s;
  242. }
  243. s = dest_writer->Close({});
  244. if (!s.ok()) {
  245. return s;
  246. }
  247. dest_writer.reset();
  248. *props = table_builder->GetTableProperties();
  249. start = std::chrono::steady_clock::now();
  250. TableReaderOptions reader_options(ioptions_, moptions_.prefix_extractor,
  251. moptions_.compression_manager.get(),
  252. soptions_, internal_comparator_,
  253. 0 /* block_protection_bytes_per_key */);
  254. std::unique_ptr<RandomAccessFileReader> file_reader;
  255. s = RandomAccessFileReader::Create(env->GetFileSystem(), testFileName,
  256. soptions_, &file_reader, /*dbg=*/nullptr);
  257. if (!s.ok()) {
  258. return s;
  259. }
  260. std::unique_ptr<TableReader> table_reader;
  261. s = tb_options.moptions.table_factory->NewTableReader(
  262. reader_options, std::move(file_reader), table_builder->FileSize(),
  263. &table_reader);
  264. if (!s.ok()) {
  265. return s;
  266. }
  267. iter.reset(table_reader->NewIterator(
  268. read_options_, moptions_.prefix_extractor.get(), /*arena=*/nullptr,
  269. /*skip_filters=*/false, TableReaderCaller::kSSTDumpTool));
  270. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  271. }
  272. s = iter->status();
  273. if (!s.ok()) {
  274. return s;
  275. }
  276. iter.reset();
  277. table_reader.reset();
  278. file_reader.reset();
  279. *read_time = std::chrono::duration_cast<std::chrono::microseconds>(
  280. std::chrono::steady_clock::now() - start);
  281. return env->DeleteFile(testFileName);
  282. }
  283. Status SstFileDumper::ShowAllCompressionSizes(
  284. const std::vector<CompressionType>& compression_types,
  285. int32_t compress_level_from, int32_t compress_level_to) {
  286. #ifndef NDEBUG
  287. fprintf(stdout,
  288. "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
  289. #endif
  290. BlockBasedTableOptions bbto;
  291. if (options_.table_factory->IsInstanceOf(
  292. TableFactory::kBlockBasedTableName())) {
  293. bbto = *(static_cast_with_check<BlockBasedTableFactory>(
  294. options_.table_factory.get()))
  295. ->GetOptions<BlockBasedTableOptions>();
  296. }
  297. for (CompressionType ctype : compression_types) {
  298. std::string cname;
  299. if (!GetStringFromCompressionType(&cname, ctype).ok()) {
  300. // Can produce names like "Reserved4F" for unrecognized values
  301. cname = CompressionTypeToString(ctype);
  302. }
  303. if (options_.compression_manager
  304. ? options_.compression_manager->SupportsCompressionType(ctype)
  305. : CompressionTypeSupported(ctype)) {
  306. CompressionOptions compress_opt = options_.compression_opts;
  307. fprintf(stdout,
  308. "Compression: %-24s Block Size: %" PRIu64 " Threads: %u\n",
  309. cname.c_str(), bbto.block_size, compress_opt.parallel_threads);
  310. for (int32_t j = compress_level_from; j <= compress_level_to; j++) {
  311. fprintf(stdout, "Cx level: %d", j);
  312. compress_opt.level = j;
  313. Status s = ShowCompressionSize(ctype, compress_opt);
  314. if (!s.ok()) {
  315. return s;
  316. }
  317. }
  318. } else {
  319. fprintf(stdout, "Unsupported compression type: %s.\n", cname.c_str());
  320. }
  321. }
  322. return Status::OK();
  323. }
  324. Status SstFileDumper::ShowCompressionSize(
  325. CompressionType compress_type, const CompressionOptions& compress_opt) {
  326. Options opts = options_; // Use compression_manager etc.
  327. opts.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
  328. opts.statistics->set_stats_level(StatsLevel::kAll);
  329. if (!opts.table_factory->IsInstanceOf(TableFactory::kBlockBasedTableName())) {
  330. // Currently need block-based table for compression
  331. opts.table_factory = std::make_shared<BlockBasedTableFactory>();
  332. }
  333. // Create internal Options types
  334. const ImmutableOptions imoptions(opts);
  335. const ColumnFamilyOptions cfo(opts);
  336. const MutableCFOptions moptions(cfo);
  337. // TODO: plumb Env::IOActivity, Env::IOPriority
  338. const ReadOptions read_options;
  339. const WriteOptions write_options;
  340. ROCKSDB_NAMESPACE::InternalKeyComparator ikc(opts.comparator);
  341. InternalTblPropCollFactories block_based_table_factories;
  342. std::string column_family_name;
  343. int unknown_level = -1;
  344. TableBuilderOptions tb_opts(
  345. imoptions, moptions, read_options, write_options, ikc,
  346. &block_based_table_factories, compress_type, compress_opt,
  347. TablePropertiesCollectorFactory::Context::kUnknownColumnFamily,
  348. column_family_name, unknown_level, kUnknownNewestKeyTime);
  349. TableProperties props;
  350. std::chrono::microseconds write_time;
  351. std::chrono::microseconds read_time;
  352. Status s =
  353. CalculateCompressedTableSize(tb_opts, &props, &write_time, &read_time);
  354. if (!s.ok()) {
  355. return s;
  356. }
  357. uint64_t num_data_blocks = props.num_data_blocks;
  358. fprintf(stdout, " Cx size: %10" PRIu64, props.data_size);
  359. fprintf(stdout, " Uncx size: %10" PRIu64, props.uncompressed_data_size);
  360. fprintf(stdout, " Ratio: %10s",
  361. std::to_string(static_cast<double>(props.uncompressed_data_size) /
  362. static_cast<double>(props.data_size))
  363. .c_str());
  364. fprintf(stdout, " Write usec: %10s ",
  365. std::to_string(write_time.count()).c_str());
  366. fprintf(stdout, " Read usec: %10s ",
  367. std::to_string(read_time.count()).c_str());
  368. const uint64_t compressed_blocks =
  369. opts.statistics->getAndResetTickerCount(NUMBER_BLOCK_COMPRESSED);
  370. const uint64_t not_compressed_blocks =
  371. opts.statistics->getAndResetTickerCount(
  372. NUMBER_BLOCK_COMPRESSION_REJECTED);
  373. // When the option enable_index_compression is true,
  374. // NUMBER_BLOCK_COMPRESSED is incremented for index block(s).
  375. if ((compressed_blocks + not_compressed_blocks) > num_data_blocks) {
  376. num_data_blocks = compressed_blocks + not_compressed_blocks;
  377. }
  378. const uint64_t ratio_not_compressed_blocks =
  379. (num_data_blocks - compressed_blocks) - not_compressed_blocks;
  380. const double compressed_pcnt =
  381. (0 == num_data_blocks) ? 0.0
  382. : ((static_cast<double>(compressed_blocks) /
  383. static_cast<double>(num_data_blocks)) *
  384. 100.0);
  385. const double ratio_not_compressed_pcnt =
  386. (0 == num_data_blocks)
  387. ? 0.0
  388. : ((static_cast<double>(ratio_not_compressed_blocks) /
  389. static_cast<double>(num_data_blocks)) *
  390. 100.0);
  391. const double not_compressed_pcnt =
  392. (0 == num_data_blocks) ? 0.0
  393. : ((static_cast<double>(not_compressed_blocks) /
  394. static_cast<double>(num_data_blocks)) *
  395. 100.0);
  396. fprintf(stdout, " Cx count: %6" PRIu64 " (%5.1f%%)", compressed_blocks,
  397. compressed_pcnt);
  398. fprintf(stdout, " Not cx for ratio: %6" PRIu64 " (%5.1f%%)",
  399. ratio_not_compressed_blocks, ratio_not_compressed_pcnt);
  400. fprintf(stdout, " Not cx otherwise: %6" PRIu64 " (%5.1f%%)\n",
  401. not_compressed_blocks, not_compressed_pcnt);
  402. return Status::OK();
  403. }
  404. // Reads TableProperties prior to opening table reader in order to set up
  405. // options.
  406. Status SstFileDumper::ReadTableProperties(uint64_t table_magic_number,
  407. RandomAccessFileReader* file,
  408. uint64_t file_size,
  409. FilePrefetchBuffer* prefetch_buffer) {
  410. Status s = ROCKSDB_NAMESPACE::ReadTableProperties(
  411. file, file_size, table_magic_number, ioptions_, read_options_,
  412. &table_properties_,
  413. /* memory_allocator= */ nullptr, prefetch_buffer);
  414. if (!s.ok()) {
  415. if (!silent_) {
  416. fprintf(stdout, "Not able to read table properties\n");
  417. }
  418. }
  419. return s;
  420. }
  421. Status SstFileDumper::SetTableOptionsByMagicNumber(
  422. uint64_t table_magic_number) {
  423. assert(table_properties_);
  424. if (table_magic_number == kBlockBasedTableMagicNumber ||
  425. table_magic_number == kLegacyBlockBasedTableMagicNumber) {
  426. // Preserve BlockBasedTableOptions on options_ when possible
  427. if (!options_.table_factory->IsInstanceOf(
  428. TableFactory::kBlockBasedTableName())) {
  429. options_.table_factory = std::make_shared<BlockBasedTableFactory>();
  430. }
  431. BlockBasedTableFactory* bbtf =
  432. static_cast_with_check<BlockBasedTableFactory>(
  433. options_.table_factory.get());
  434. // To force tail prefetching, we fake reporting two useful reads of 512KB
  435. // from the tail.
  436. // It needs at least two data points to warm up the stats.
  437. bbtf->tail_prefetch_stats()->RecordEffectiveSize(512 * 1024);
  438. bbtf->tail_prefetch_stats()->RecordEffectiveSize(512 * 1024);
  439. if (!silent_) {
  440. fprintf(stdout, "Sst file format: block-based\n");
  441. }
  442. auto& props = table_properties_->user_collected_properties;
  443. auto pos = props.find(BlockBasedTablePropertyNames::kIndexType);
  444. if (pos != props.end()) {
  445. auto index_type_on_file = static_cast<BlockBasedTableOptions::IndexType>(
  446. DecodeFixed32(pos->second.c_str()));
  447. if (index_type_on_file ==
  448. BlockBasedTableOptions::IndexType::kHashSearch) {
  449. options_.prefix_extractor.reset(NewNoopTransform());
  450. }
  451. }
  452. } else if (table_magic_number == kPlainTableMagicNumber ||
  453. table_magic_number == kLegacyPlainTableMagicNumber) {
  454. options_.allow_mmap_reads = true;
  455. PlainTableOptions plain_table_options;
  456. plain_table_options.user_key_len = kPlainTableVariableLength;
  457. plain_table_options.bloom_bits_per_key = 0;
  458. plain_table_options.hash_table_ratio = 0;
  459. plain_table_options.index_sparseness = 1;
  460. plain_table_options.huge_page_tlb_size = 0;
  461. plain_table_options.encoding_type = kPlain;
  462. plain_table_options.full_scan_mode = true;
  463. options_.table_factory.reset(NewPlainTableFactory(plain_table_options));
  464. if (!silent_) {
  465. fprintf(stdout, "Sst file format: plain table\n");
  466. }
  467. } else if (table_magic_number == kCuckooTableMagicNumber) {
  468. ioptions_.allow_mmap_reads = true;
  469. options_.table_factory.reset(NewCuckooTableFactory());
  470. if (!silent_) {
  471. fprintf(stdout, "Sst file format: cuckoo table\n");
  472. }
  473. } else {
  474. char error_msg_buffer[80];
  475. snprintf(error_msg_buffer, sizeof(error_msg_buffer) - 1,
  476. "Unsupported table magic number --- %lx",
  477. (long)table_magic_number);
  478. return Status::InvalidArgument(error_msg_buffer);
  479. }
  480. return Status::OK();
  481. }
  482. Status SstFileDumper::SetOldTableOptions() {
  483. assert(table_properties_ == nullptr);
  484. if (!options_.table_factory->IsInstanceOf(
  485. TableFactory::kBlockBasedTableName())) {
  486. options_.table_factory = std::make_shared<BlockBasedTableFactory>();
  487. }
  488. if (!silent_) {
  489. fprintf(stdout, "Sst file format: block-based(old version)\n");
  490. }
  491. return Status::OK();
  492. }
  493. Status SstFileDumper::ReadSequential(bool print_kv, uint64_t read_num_limit,
  494. bool has_from, const std::string& from_key,
  495. bool has_to, const std::string& to_key,
  496. bool use_from_as_prefix) {
  497. if (!table_reader_) {
  498. return init_result_;
  499. }
  500. InternalIterator* iter = table_reader_->NewIterator(
  501. read_options_, moptions_.prefix_extractor.get(),
  502. /*arena=*/nullptr, /*skip_filters=*/false,
  503. TableReaderCaller::kSSTDumpTool);
  504. const Comparator* ucmp = internal_comparator_.user_comparator();
  505. size_t ts_sz = ucmp->timestamp_size();
  506. OptSlice from_opt = has_from ? from_key : OptSlice{};
  507. OptSlice to_opt = has_to ? to_key : OptSlice{};
  508. std::string from_key_buf, to_key_buf;
  509. auto [from, to] = MaybeAddTimestampsToRange(from_opt, to_opt, ts_sz,
  510. &from_key_buf, &to_key_buf);
  511. uint64_t i = 0;
  512. if (from.has_value()) {
  513. InternalKey ikey;
  514. ikey.SetMinPossibleForUserKey(from.value());
  515. iter->Seek(ikey.Encode());
  516. } else {
  517. iter->SeekToFirst();
  518. }
  519. for (; iter->Valid(); iter->Next()) {
  520. Slice key = iter->key();
  521. Slice value = iter->value();
  522. ++i;
  523. if (read_num_limit > 0 && i > read_num_limit) {
  524. break;
  525. }
  526. ParsedInternalKey ikey;
  527. Status pik_status = ParseInternalKey(key, &ikey, true /* log_err_key */);
  528. if (!pik_status.ok()) {
  529. std::cerr << pik_status.getState() << "\n";
  530. continue;
  531. }
  532. // the key returned is not prefixed with out 'from' key
  533. if (use_from_as_prefix && !ikey.user_key.starts_with(from_key)) {
  534. break;
  535. }
  536. // If end marker was specified, we stop before it
  537. if (to.has_value() && ucmp->Compare(ikey.user_key, to.value()) >= 0) {
  538. break;
  539. }
  540. if (print_kv) {
  541. if (!decode_blob_index_ || ikey.type != kTypeBlobIndex) {
  542. if (ikey.type == kTypeWideColumnEntity) {
  543. std::ostringstream oss;
  544. const Status s = WideColumnsHelper::DumpSliceAsWideColumns(
  545. iter->value(), oss, output_hex_);
  546. if (!s.ok()) {
  547. fprintf(stderr, "%s => error deserializing wide columns\n",
  548. ikey.DebugString(true, output_hex_, ucmp).c_str());
  549. continue;
  550. }
  551. fprintf(stdout, "%s => %s\n",
  552. ikey.DebugString(true, output_hex_, ucmp).c_str(),
  553. oss.str().c_str());
  554. } else if (ikey.type == kTypeValuePreferredSeqno) {
  555. auto [unpacked_value, preferred_seqno] =
  556. ParsePackedValueWithSeqno(value);
  557. fprintf(stdout, "%s => %s, %llu\n",
  558. ikey.DebugString(true, output_hex_, ucmp).c_str(),
  559. unpacked_value.ToString(output_hex_).c_str(),
  560. static_cast<unsigned long long>(preferred_seqno));
  561. } else {
  562. fprintf(stdout, "%s => %s\n",
  563. ikey.DebugString(true, output_hex_, ucmp).c_str(),
  564. value.ToString(output_hex_).c_str());
  565. }
  566. } else {
  567. BlobIndex blob_index;
  568. const Status s = blob_index.DecodeFrom(value);
  569. if (!s.ok()) {
  570. fprintf(stderr, "%s => error decoding blob index\n",
  571. ikey.DebugString(true, output_hex_, ucmp).c_str());
  572. continue;
  573. }
  574. fprintf(stdout, "%s => %s\n",
  575. ikey.DebugString(true, output_hex_, ucmp).c_str(),
  576. blob_index.DebugString(output_hex_).c_str());
  577. }
  578. }
  579. }
  580. read_num_ += i;
  581. Status ret = iter->status();
  582. bool verify_num_entries =
  583. (read_num_limit == 0 ||
  584. read_num_limit == std::numeric_limits<uint64_t>::max()) &&
  585. !has_from && !has_to;
  586. if (verify_num_entries && ret.ok()) {
  587. // Compare the number of entries
  588. if (!table_properties_) {
  589. fprintf(stderr, "Table properties not available.");
  590. } else {
  591. // TODO: verify num_range_deletions
  592. if (i != table_properties_->num_entries -
  593. table_properties_->num_range_deletions) {
  594. std::ostringstream oss;
  595. oss << "Table property expects "
  596. << table_properties_->num_entries -
  597. table_properties_->num_range_deletions
  598. << " entries when excluding range deletions,"
  599. << " but scanning the table returned " << std::to_string(i)
  600. << " entries";
  601. ret = Status::Corruption(oss.str());
  602. }
  603. }
  604. }
  605. delete iter;
  606. return ret;
  607. }
  608. // Provides TableProperties to API user
  609. Status SstFileDumper::ReadTableProperties(
  610. std::shared_ptr<const TableProperties>* table_properties) {
  611. if (!table_reader_) {
  612. return init_result_;
  613. }
  614. *table_properties = table_reader_->GetTableProperties();
  615. return init_result_;
  616. }
  617. } // namespace ROCKSDB_NAMESPACE