Coverage Report

Created: 2026-09-28 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/table/meta_blocks.cc
Line
Count
Source
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
#include "table/meta_blocks.h"
6
7
#include <array>
8
#include <cstdio>
9
#include <map>
10
#include <string>
11
12
#include "block_fetcher.h"
13
#include "db/dbformat.h"
14
#include "db/table_properties_collector.h"
15
#include "file/random_access_file_reader.h"
16
#include "logging/logging.h"
17
#include "rocksdb/options.h"
18
#include "rocksdb/table.h"
19
#include "rocksdb/table_properties.h"
20
#include "table/block_based/block.h"
21
#include "table/block_based/reader_common.h"
22
#include "table/format.h"
23
#include "table/internal_iterator.h"
24
#include "table/persistent_cache_helper.h"
25
#include "table/sst_file_writer_collectors.h"
26
#include "table/table_properties_internal.h"
27
#include "test_util/sync_point.h"
28
#include "util/coding.h"
29
30
namespace ROCKSDB_NAMESPACE {
31
32
const std::string kPropertiesBlockName = "rocksdb.properties";
33
// NB: only used with format_version >= 6
34
const std::string kIndexBlockName = "rocksdb.index";
35
const std::string kCompressionDictBlockName = "rocksdb.compression_dict";
36
const std::string kRangeDelBlockName = "rocksdb.range_del";
37
38
Status GetGlobalSequenceNumber(const TableProperties& table_properties,
39
                               SequenceNumber largest_seqno,
40
123k
                               SequenceNumber* global_seqno) {
41
123k
  const auto& props = table_properties.user_collected_properties;
42
123k
  const auto version_pos = props.find(ExternalSstFilePropertyNames::kVersion);
43
123k
  const auto seqno_pos = props.find(ExternalSstFilePropertyNames::kGlobalSeqno);
44
45
123k
  *global_seqno = kDisableGlobalSequenceNumber;
46
123k
  if (version_pos == props.end()) {
47
123k
    if (seqno_pos != props.end()) {
48
0
      std::array<char, 200> msg_buf{};
49
0
      snprintf(
50
0
          msg_buf.data(), msg_buf.max_size(),
51
0
          "A non-external sst file have global seqno property with value %s",
52
0
          seqno_pos->second.c_str());
53
0
      return Status::Corruption(msg_buf.data());
54
0
    }
55
123k
    return Status::OK();
56
123k
  }
57
58
100
  const uint32_t version = DecodeFixed32(version_pos->second.c_str());
59
100
  if (version != 2) {
60
0
    std::array<char, 200> msg_buf{};
61
0
    if (version != 1) {
62
0
      snprintf(msg_buf.data(), msg_buf.max_size(),
63
0
               "An external sst file has corrupted version %u.", version);
64
0
      return Status::Corruption(msg_buf.data());
65
0
    }
66
0
    if (seqno_pos != props.end()) {
67
0
      snprintf(msg_buf.data(), msg_buf.max_size(),
68
0
               "An external sst file with version %u has global seqno "
69
0
               "property with value %s",
70
0
               version, seqno_pos->second.c_str());
71
0
      return Status::Corruption(msg_buf.data());
72
0
    }
73
0
    return Status::OK();
74
0
  }
75
76
  // Since we have a plan to deprecate global_seqno, we do not return failure
77
  // if seqno_pos == props.end(). We rely on version_pos to detect whether the
78
  // SST is external.
79
100
  SequenceNumber resolved_global_seqno = 0;
80
100
  if (seqno_pos != props.end()) {
81
0
    resolved_global_seqno = DecodeFixed64(seqno_pos->second.c_str());
82
0
  }
83
  // SstTableReader opens a table reader with kMaxSequenceNumber as
84
  // largest_seqno to denote it is unknown.
85
100
  if (largest_seqno < kMaxSequenceNumber) {
86
0
    if (resolved_global_seqno == 0) {
87
0
      resolved_global_seqno = largest_seqno;
88
0
    }
89
0
    if (resolved_global_seqno != largest_seqno) {
90
0
      std::array<char, 200> msg_buf{};
91
0
      snprintf(
92
0
          msg_buf.data(), msg_buf.max_size(),
93
0
          "An external sst file with version %u have global seqno property "
94
0
          "with value %s, while largest seqno in the file is %llu",
95
0
          version, seqno_pos->second.c_str(),
96
0
          static_cast<unsigned long long>(largest_seqno));
97
0
      return Status::Corruption(msg_buf.data());
98
0
    }
99
0
  }
100
100
  *global_seqno = resolved_global_seqno;
101
102
100
  if (resolved_global_seqno > kMaxSequenceNumber) {
103
0
    std::array<char, 200> msg_buf{};
104
0
    snprintf(msg_buf.data(), msg_buf.max_size(),
105
0
             "An external sst file with version %u have global seqno property "
106
0
             "with value %llu, which is greater than kMaxSequenceNumber",
107
0
             version, static_cast<unsigned long long>(resolved_global_seqno));
108
0
    return Status::Corruption(msg_buf.data());
109
0
  }
110
111
100
  return Status::OK();
112
100
}
113
114
MetaIndexBuilder::MetaIndexBuilder()
115
23.2k
    : meta_index_block_(new BlockBuilder(BlockBuilder::ForMetaBlock{},
116
23.2k
                                         1 /* restart interval */)) {}
117
118
49.4k
void MetaIndexBuilder::Add(const std::string& key, const BlockHandle& handle) {
119
49.4k
  std::string handle_encoding;
120
49.4k
  handle.EncodeTo(&handle_encoding);
121
49.4k
  meta_block_handles_.insert({key, handle_encoding});
122
49.4k
}
123
124
23.2k
Slice MetaIndexBuilder::Finish() {
125
49.4k
  for (const auto& metablock : meta_block_handles_) {
126
    // NOTE: meta index keys and block handles are guaranteed < 4GB
127
49.4k
    meta_index_block_->Add(metablock.first, metablock.second);
128
49.4k
  }
129
23.2k
  return meta_index_block_->Finish();
130
23.2k
}
131
132
// Property block will be read sequentially and cached in a heap located
133
// object, so there's no need for restart points. Thus we set the restart
134
// interval to infinity to save space.
135
PropertyBlockBuilder::PropertyBlockBuilder()
136
23.2k
    : properties_block_(new BlockBuilder(
137
23.2k
          BlockBuilder::ForMetaBlock{},
138
23.2k
          std::numeric_limits<int32_t>::max() /* restart interval */)) {}
139
140
void PropertyBlockBuilder::Add(const std::string& name,
141
1.00M
                               const std::string& val) {
142
1.00M
  assert(props_.find(name) == props_.end());
143
1.00M
  props_.insert({name, val});
144
1.00M
}
145
146
678k
void PropertyBlockBuilder::Add(const std::string& name, uint64_t val) {
147
678k
  std::string dst;
148
678k
  PutVarint64(&dst, val);
149
150
678k
  Add(name, dst);
151
678k
}
152
153
void PropertyBlockBuilder::Add(
154
23.2k
    const UserCollectedProperties& user_collected_properties) {
155
93.0k
  for (const auto& prop : user_collected_properties) {
156
93.0k
    Add(prop.first, prop.second);
157
93.0k
  }
158
23.2k
}
159
160
23.2k
void PropertyBlockBuilder::AddTableProperty(const TableProperties& props) {
161
23.2k
  TEST_SYNC_POINT_CALLBACK("PropertyBlockBuilder::AddTableProperty:Start",
162
23.2k
                           const_cast<TableProperties*>(&props));
163
164
23.2k
  Add(TablePropertiesNames::kOriginalFileNumber, props.orig_file_number);
165
23.2k
  Add(TablePropertiesNames::kRawKeySize, props.raw_key_size);
166
23.2k
  Add(TablePropertiesNames::kRawValueSize, props.raw_value_size);
167
23.2k
  Add(TablePropertiesNames::kDataSize, props.data_size);
168
23.2k
  Add(TablePropertiesNames::kUncompressedDataSize,
169
23.2k
      props.uncompressed_data_size);
170
23.2k
  Add(TablePropertiesNames::kIndexSize, props.index_size);
171
23.2k
  if (props.index_partitions != 0) {
172
0
    Add(TablePropertiesNames::kIndexPartitions, props.index_partitions);
173
0
    Add(TablePropertiesNames::kTopLevelIndexSize, props.top_level_index_size);
174
0
  }
175
23.2k
  Add(TablePropertiesNames::kIndexKeyIsUserKey, props.index_key_is_user_key);
176
23.2k
  Add(TablePropertiesNames::kIndexValueIsDeltaEncoded,
177
23.2k
      props.index_value_is_delta_encoded);
178
23.2k
  if (props.udi_is_primary_index != 0) {
179
0
    Add(TablePropertiesNames::kUDIIsPrimaryIndex, props.udi_is_primary_index);
180
0
  }
181
23.2k
  Add(TablePropertiesNames::kNumEntries, props.num_entries);
182
23.2k
  Add(TablePropertiesNames::kNumFilterEntries, props.num_filter_entries);
183
23.2k
  Add(TablePropertiesNames::kDeletedKeys, props.num_deletions);
184
23.2k
  Add(TablePropertiesNames::kMergeOperands, props.num_merge_operands);
185
23.2k
  Add(TablePropertiesNames::kNumRangeDeletions, props.num_range_deletions);
186
23.2k
  Add(TablePropertiesNames::kNumDataBlocks, props.num_data_blocks);
187
23.2k
  if (props.num_data_blocks_compression_rejected > 0) {
188
0
    Add(TablePropertiesNames::kNumDataBlocksCompressionRejected,
189
0
        props.num_data_blocks_compression_rejected);
190
0
  }
191
23.2k
  if (props.num_data_blocks_compression_bypassed > 0) {
192
22.4k
    Add(TablePropertiesNames::kNumDataBlocksCompressionBypassed,
193
22.4k
        props.num_data_blocks_compression_bypassed);
194
22.4k
  }
195
23.2k
  Add(TablePropertiesNames::kNumUniformBlocks, props.num_uniform_blocks);
196
23.2k
  Add(TablePropertiesNames::kFilterSize, props.filter_size);
197
23.2k
  Add(TablePropertiesNames::kFormatVersion, props.format_version);
198
23.2k
  Add(TablePropertiesNames::kFixedKeyLen, props.fixed_key_len);
199
23.2k
  Add(TablePropertiesNames::kColumnFamilyId, props.column_family_id);
200
23.2k
  Add(TablePropertiesNames::kCreationTime, props.creation_time);
201
23.2k
  Add(TablePropertiesNames::kOldestKeyTime, props.oldest_key_time);
202
23.2k
  Add(TablePropertiesNames::kNewestKeyTime, props.newest_key_time);
203
23.2k
  if (props.file_creation_time > 0) {
204
4.05k
    Add(TablePropertiesNames::kFileCreationTime, props.file_creation_time);
205
4.05k
  }
206
23.2k
  if (props.slow_compression_estimated_data_size > 0) {
207
0
    Add(TablePropertiesNames::kSlowCompressionEstimatedDataSize,
208
0
        props.slow_compression_estimated_data_size);
209
0
  }
210
23.2k
  if (props.fast_compression_estimated_data_size > 0) {
211
0
    Add(TablePropertiesNames::kFastCompressionEstimatedDataSize,
212
0
        props.fast_compression_estimated_data_size);
213
0
  }
214
23.2k
  Add(TablePropertiesNames::kTailStartOffset, props.tail_start_offset);
215
23.2k
  if (props.user_defined_timestamps_persisted == 0) {
216
0
    Add(TablePropertiesNames::kUserDefinedTimestampsPersisted,
217
0
        props.user_defined_timestamps_persisted);
218
0
  }
219
23.2k
  if (!props.db_id.empty()) {
220
23.2k
    Add(TablePropertiesNames::kDbId, props.db_id);
221
23.2k
  }
222
23.2k
  if (!props.db_session_id.empty()) {
223
23.2k
    Add(TablePropertiesNames::kDbSessionId, props.db_session_id);
224
23.2k
  }
225
23.2k
  if (!props.db_host_id.empty()) {
226
23.2k
    Add(TablePropertiesNames::kDbHostId, props.db_host_id);
227
23.2k
  }
228
229
23.2k
  if (!props.filter_policy_name.empty()) {
230
0
    Add(TablePropertiesNames::kFilterPolicy, props.filter_policy_name);
231
0
  }
232
23.2k
  if (!props.comparator_name.empty()) {
233
23.2k
    Add(TablePropertiesNames::kComparator, props.comparator_name);
234
23.2k
  }
235
236
23.2k
  if (!props.merge_operator_name.empty()) {
237
23.2k
    Add(TablePropertiesNames::kMergeOperator, props.merge_operator_name);
238
23.2k
  }
239
23.2k
  if (!props.prefix_extractor_name.empty()) {
240
23.2k
    Add(TablePropertiesNames::kPrefixExtractorName,
241
23.2k
        props.prefix_extractor_name);
242
23.2k
  }
243
23.2k
  if (!props.property_collectors_names.empty()) {
244
23.2k
    Add(TablePropertiesNames::kPropertyCollectors,
245
23.2k
        props.property_collectors_names);
246
23.2k
  }
247
23.2k
  if (!props.column_family_name.empty()) {
248
23.2k
    Add(TablePropertiesNames::kColumnFamilyName, props.column_family_name);
249
23.2k
  }
250
251
23.2k
  if (!props.compression_name.empty()) {
252
23.2k
    Add(TablePropertiesNames::kCompression, props.compression_name);
253
23.2k
  }
254
23.2k
  if (!props.compression_options.empty()) {
255
23.2k
    Add(TablePropertiesNames::kCompressionOptions, props.compression_options);
256
23.2k
  }
257
23.2k
  if (!props.seqno_to_time_mapping.empty()) {
258
0
    Add(TablePropertiesNames::kSequenceNumberTimeMapping,
259
0
        props.seqno_to_time_mapping);
260
0
  }
261
23.2k
  if (props.key_largest_seqno != UINT64_MAX) {
262
23.2k
    Add(TablePropertiesNames::kKeyLargestSeqno, props.key_largest_seqno);
263
23.2k
  }
264
23.2k
  if (props.key_smallest_seqno != UINT64_MAX) {
265
23.2k
    Add(TablePropertiesNames::kKeySmallestSeqno, props.key_smallest_seqno);
266
23.2k
  }
267
23.2k
  if (props.data_block_restart_interval > 0) {
268
23.2k
    Add(TablePropertiesNames::kDataBlockRestartInterval,
269
23.2k
        props.data_block_restart_interval);
270
23.2k
  }
271
23.2k
  if (props.index_block_restart_interval > 0) {
272
23.2k
    Add(TablePropertiesNames::kIndexBlockRestartInterval,
273
23.2k
        props.index_block_restart_interval);
274
23.2k
  }
275
23.2k
  if (props.separate_key_value_in_data_block > 0) {
276
0
    Add(TablePropertiesNames::kSeparateKeyValueInDataBlock,
277
0
        props.separate_key_value_in_data_block);
278
0
  }
279
23.2k
  if (props.lsm_info_at_creation != 0) {
280
23.2k
    Add(TablePropertiesNames::kLsmInfoAtCreation, props.lsm_info_at_creation);
281
23.2k
  }
282
23.2k
}
283
284
23.2k
Slice PropertyBlockBuilder::Finish() {
285
1.00M
  for (const auto& prop : props_) {
286
1.00M
    assert(last_prop_added_to_block_.empty() ||
287
1.00M
           comparator_->Compare(prop.first, last_prop_added_to_block_) > 0);
288
    // Use first 4GB of key and value strings to avoid overflow, e.g. from
289
    // user property collector (see BlockBuilder::Add API comments)
290
1.00M
    Slice key(prop.first.data(), static_cast<uint32_t>(prop.first.size()));
291
1.00M
    Slice value(prop.second.data(), static_cast<uint32_t>(prop.second.size()));
292
1.00M
    properties_block_->Add(key, value);
293
#ifndef NDEBUG
294
    last_prop_added_to_block_ = prop.first;
295
#endif /* !NDEBUG */
296
1.00M
  }
297
298
23.2k
  return properties_block_->Finish();
299
23.2k
}
300
301
void LogPropertiesCollectionError(Logger* info_log, const std::string& method,
302
0
                                  const std::string& name) {
303
0
  assert(method == "Add" || method == "Finish");
304
305
0
  std::string msg =
306
0
      "Encountered error when calling TablePropertiesCollector::" + method +
307
0
      "() with collector name: " + name;
308
0
  ROCKS_LOG_ERROR(info_log, "%s", msg.c_str());
309
0
}
310
311
bool NotifyCollectTableCollectorsOnAdd(
312
    const Slice& key, const Slice& value, uint64_t file_size,
313
    const std::vector<std::unique_ptr<InternalTblPropColl>>& collectors,
314
154k
    Logger* info_log) {
315
154k
  bool all_succeeded = true;
316
154k
  for (auto& collector : collectors) {
317
154k
    Status s = collector->InternalAdd(key, value, file_size);
318
154k
    all_succeeded = all_succeeded && s.ok();
319
154k
    if (!s.ok()) {
320
0
      LogPropertiesCollectionError(info_log, "Add" /* method */,
321
0
                                   collector->Name());
322
0
    }
323
154k
  }
324
154k
  return all_succeeded;
325
154k
}
326
327
void NotifyCollectTableCollectorsOnBlockAdd(
328
    const std::vector<std::unique_ptr<InternalTblPropColl>>& collectors,
329
    const uint64_t block_uncomp_bytes,
330
    const uint64_t block_compressed_bytes_fast,
331
29.5k
    const uint64_t block_compressed_bytes_slow) {
332
29.5k
  for (auto& collector : collectors) {
333
29.5k
    collector->BlockAdd(block_uncomp_bytes, block_compressed_bytes_fast,
334
29.5k
                        block_compressed_bytes_slow);
335
29.5k
  }
336
29.5k
}
337
338
bool NotifyCollectTableCollectorsOnFinish(
339
    const std::vector<std::unique_ptr<InternalTblPropColl>>& collectors,
340
    Logger* info_log, PropertyBlockBuilder* builder,
341
    UserCollectedProperties& user_collected_properties,
342
23.2k
    UserCollectedProperties& readable_properties) {
343
23.2k
  bool all_succeeded = true;
344
23.2k
  for (auto& collector : collectors) {
345
23.2k
    UserCollectedProperties user_properties;
346
23.2k
    Status s = collector->Finish(&user_properties);
347
23.2k
    if (s.ok()) {
348
23.2k
      for (const auto& prop : collector->GetReadableProperties()) {
349
0
        readable_properties.insert(prop);
350
0
      }
351
#ifndef NDEBUG
352
      // Check different user properties collectors are not adding properties of
353
      // the same name.
354
      for (const auto& pair : user_properties) {
355
        assert(user_collected_properties.find(pair.first) ==
356
               user_collected_properties.end());
357
      }
358
#endif /* !NDEBUG */
359
23.2k
      user_collected_properties.merge(user_properties);
360
23.2k
    } else {
361
0
      LogPropertiesCollectionError(info_log, "Finish" /* method */,
362
0
                                   collector->Name());
363
0
      if (all_succeeded) {
364
0
        all_succeeded = false;
365
0
      }
366
0
    }
367
23.2k
  }
368
23.2k
  builder->Add(user_collected_properties);
369
23.2k
  return all_succeeded;
370
23.2k
}
371
372
Status ParsePropertiesBlock(
373
    const ImmutableOptions& ioptions, uint64_t offset, Block& properties_block,
374
122k
    std::unique_ptr<TableProperties>& new_table_properties) {
375
122k
  std::unique_ptr<MetaBlockIter> iter(properties_block.NewMetaIterator());
376
377
  //  All pre-defined properties of type uint64_t
378
122k
  std::unordered_map<std::string, uint64_t*> predefined_uint64_properties = {
379
122k
      {TablePropertiesNames::kOriginalFileNumber,
380
122k
       &new_table_properties->orig_file_number},
381
122k
      {TablePropertiesNames::kDataSize, &new_table_properties->data_size},
382
122k
      {TablePropertiesNames::kUncompressedDataSize,
383
122k
       &new_table_properties->uncompressed_data_size},
384
122k
      {TablePropertiesNames::kLsmInfoAtCreation,
385
122k
       &new_table_properties->lsm_info_at_creation},
386
122k
      {TablePropertiesNames::kIndexSize, &new_table_properties->index_size},
387
122k
      {TablePropertiesNames::kIndexPartitions,
388
122k
       &new_table_properties->index_partitions},
389
122k
      {TablePropertiesNames::kTopLevelIndexSize,
390
122k
       &new_table_properties->top_level_index_size},
391
122k
      {TablePropertiesNames::kIndexKeyIsUserKey,
392
122k
       &new_table_properties->index_key_is_user_key},
393
122k
      {TablePropertiesNames::kIndexValueIsDeltaEncoded,
394
122k
       &new_table_properties->index_value_is_delta_encoded},
395
122k
      {TablePropertiesNames::kUDIIsPrimaryIndex,
396
122k
       &new_table_properties->udi_is_primary_index},
397
122k
      {TablePropertiesNames::kFilterSize, &new_table_properties->filter_size},
398
122k
      {TablePropertiesNames::kRawKeySize, &new_table_properties->raw_key_size},
399
122k
      {TablePropertiesNames::kRawValueSize,
400
122k
       &new_table_properties->raw_value_size},
401
122k
      {TablePropertiesNames::kNumDataBlocks,
402
122k
       &new_table_properties->num_data_blocks},
403
122k
      {TablePropertiesNames::kNumDataBlocksCompressionRejected,
404
122k
       &new_table_properties->num_data_blocks_compression_rejected},
405
122k
      {TablePropertiesNames::kNumDataBlocksCompressionBypassed,
406
122k
       &new_table_properties->num_data_blocks_compression_bypassed},
407
122k
      {TablePropertiesNames::kNumUniformBlocks,
408
122k
       &new_table_properties->num_uniform_blocks},
409
122k
      {TablePropertiesNames::kNumEntries, &new_table_properties->num_entries},
410
122k
      {TablePropertiesNames::kNumFilterEntries,
411
122k
       &new_table_properties->num_filter_entries},
412
122k
      {TablePropertiesNames::kDeletedKeys,
413
122k
       &new_table_properties->num_deletions},
414
122k
      {TablePropertiesNames::kMergeOperands,
415
122k
       &new_table_properties->num_merge_operands},
416
122k
      {TablePropertiesNames::kNumRangeDeletions,
417
122k
       &new_table_properties->num_range_deletions},
418
122k
      {TablePropertiesNames::kFormatVersion,
419
122k
       &new_table_properties->format_version},
420
122k
      {TablePropertiesNames::kFixedKeyLen,
421
122k
       &new_table_properties->fixed_key_len},
422
122k
      {TablePropertiesNames::kColumnFamilyId,
423
122k
       &new_table_properties->column_family_id},
424
122k
      {TablePropertiesNames::kCreationTime,
425
122k
       &new_table_properties->creation_time},
426
122k
      {TablePropertiesNames::kOldestKeyTime,
427
122k
       &new_table_properties->oldest_key_time},
428
122k
      {TablePropertiesNames::kNewestKeyTime,
429
122k
       &new_table_properties->newest_key_time},
430
122k
      {TablePropertiesNames::kFileCreationTime,
431
122k
       &new_table_properties->file_creation_time},
432
122k
      {TablePropertiesNames::kSlowCompressionEstimatedDataSize,
433
122k
       &new_table_properties->slow_compression_estimated_data_size},
434
122k
      {TablePropertiesNames::kFastCompressionEstimatedDataSize,
435
122k
       &new_table_properties->fast_compression_estimated_data_size},
436
122k
      {TablePropertiesNames::kTailStartOffset,
437
122k
       &new_table_properties->tail_start_offset},
438
122k
      {TablePropertiesNames::kUserDefinedTimestampsPersisted,
439
122k
       &new_table_properties->user_defined_timestamps_persisted},
440
122k
      {TablePropertiesNames::kKeyLargestSeqno,
441
122k
       &new_table_properties->key_largest_seqno},
442
122k
      {TablePropertiesNames::kKeySmallestSeqno,
443
122k
       &new_table_properties->key_smallest_seqno},
444
122k
      {TablePropertiesNames::kDataBlockRestartInterval,
445
122k
       &new_table_properties->data_block_restart_interval},
446
122k
      {TablePropertiesNames::kIndexBlockRestartInterval,
447
122k
       &new_table_properties->index_block_restart_interval},
448
122k
      {TablePropertiesNames::kSeparateKeyValueInDataBlock,
449
122k
       &new_table_properties->separate_key_value_in_data_block},
450
122k
  };
451
452
122k
  Status s;
453
122k
  std::string last_key;
454
4.58M
  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
455
4.46M
    s = iter->status();
456
4.46M
    if (!s.ok()) {
457
0
      break;
458
0
    }
459
460
4.46M
    auto key = iter->key().ToString();
461
    // properties block should be strictly sorted with no duplicate key.
462
4.46M
    if (!last_key.empty() &&
463
4.31M
        BytewiseComparator()->Compare(key, last_key) <= 0) {
464
0
      s = Status::Corruption("properties unsorted");
465
0
      break;
466
0
    }
467
4.46M
    last_key = key;
468
469
4.46M
    auto raw_val = iter->value();
470
4.46M
    auto pos = predefined_uint64_properties.find(key);
471
472
4.46M
    if (key == ExternalSstFilePropertyNames::kGlobalSeqno) {
473
0
      new_table_properties->external_sst_file_global_seqno_offset =
474
0
          offset + iter->ValueOffset();
475
0
    }
476
477
4.46M
    if (pos != predefined_uint64_properties.end()) {
478
3.50M
      if (key == TablePropertiesNames::kDeletedKeys ||
479
3.39M
          key == TablePropertiesNames::kMergeOperands) {
480
        // Insert in user-collected properties for API backwards compatibility
481
246k
        new_table_properties->user_collected_properties.insert(
482
246k
            {key, raw_val.ToString()});
483
246k
      }
484
      // handle predefined rocksdb properties
485
3.50M
      uint64_t val;
486
3.50M
      if (!GetVarint64(&raw_val, &val)) {
487
        // skip malformed value
488
0
        auto error_msg =
489
0
            "Detect malformed value in properties meta-block:"
490
0
            "\tkey: " +
491
0
            key + "\tval: " + raw_val.ToString();
492
0
        ROCKS_LOG_ERROR(ioptions.logger, "%s", error_msg.c_str());
493
0
        continue;
494
0
      }
495
3.50M
      *(pos->second) = val;
496
3.50M
    } else if (key == TablePropertiesNames::kDbId) {
497
123k
      new_table_properties->db_id = raw_val.ToString();
498
834k
    } else if (key == TablePropertiesNames::kDbSessionId) {
499
123k
      new_table_properties->db_session_id = raw_val.ToString();
500
710k
    } else if (key == TablePropertiesNames::kDbHostId) {
501
123k
      new_table_properties->db_host_id = raw_val.ToString();
502
587k
    } else if (key == TablePropertiesNames::kFilterPolicy) {
503
0
      new_table_properties->filter_policy_name = raw_val.ToString();
504
587k
    } else if (key == TablePropertiesNames::kColumnFamilyName) {
505
123k
      new_table_properties->column_family_name = raw_val.ToString();
506
464k
    } else if (key == TablePropertiesNames::kComparator) {
507
123k
      new_table_properties->comparator_name = raw_val.ToString();
508
341k
    } else if (key == TablePropertiesNames::kMergeOperator) {
509
123k
      new_table_properties->merge_operator_name = raw_val.ToString();
510
218k
    } else if (key == TablePropertiesNames::kPrefixExtractorName) {
511
123k
      new_table_properties->prefix_extractor_name = raw_val.ToString();
512
123k
    } else if (key == TablePropertiesNames::kPropertyCollectors) {
513
123k
      new_table_properties->property_collectors_names = raw_val.ToString();
514
18.4E
    } else if (key == TablePropertiesNames::kCompression) {
515
123k
      new_table_properties->compression_name = raw_val.ToString();
516
18.4E
    } else if (key == TablePropertiesNames::kCompressionOptions) {
517
123k
      new_table_properties->compression_options = raw_val.ToString();
518
18.4E
    } else if (key == TablePropertiesNames::kSequenceNumberTimeMapping) {
519
0
      new_table_properties->seqno_to_time_mapping = raw_val.ToString();
520
18.4E
    } else {
521
      // handle user-collected properties
522
18.4E
      new_table_properties->user_collected_properties.insert(
523
18.4E
          {key, raw_val.ToString()});
524
18.4E
    }
525
4.46M
  }
526
527
122k
  return s;
528
122k
}
529
530
// FIXME: should be a parameter for reading table properties to use persistent
531
// cache?
532
Status ReadTablePropertiesHelper(
533
    const ReadOptions& ro, const BlockHandle& handle,
534
    RandomAccessFileReader* file, FilePrefetchBuffer* prefetch_buffer,
535
    const Footer& footer, const ImmutableOptions& ioptions,
536
    std::unique_ptr<TableProperties>* table_properties,
537
122k
    MemoryAllocator* memory_allocator) {
538
122k
  assert(table_properties);
539
540
122k
  Status s;
541
122k
  bool retry = false;
542
122k
  while (true) {
543
122k
    BlockContents block_contents;
544
122k
    size_t len = handle.size() + footer.GetBlockTrailerSize();
545
    // If this is an external SST file ingested with write_global_seqno set to
546
    // true, then we expect the checksum mismatch because checksum was written
547
    // by SstFileWriter, but its global seqno in the properties block may have
548
    // been changed during ingestion. For this reason, we initially read
549
    // and process without checksum verification, then later try checksum
550
    // verification so that if it fails, we can copy to a temporary buffer with
551
    // global seqno set to its original value, i.e. 0, and attempt checksum
552
    // verification again.
553
122k
    if (!retry) {
554
121k
      ReadOptions modified_ro = ro;
555
121k
      modified_ro.verify_checksums = false;
556
121k
      BlockFetcher block_fetcher(
557
121k
          file, prefetch_buffer, footer, modified_ro, handle, &block_contents,
558
121k
          ioptions, false /* decompress */, false /*maybe_compressed*/,
559
121k
          BlockType::kProperties, nullptr /*decompressor*/,
560
121k
          PersistentCacheOptions::kEmpty, memory_allocator);
561
121k
      s = block_fetcher.ReadBlockContents();
562
121k
      if (!s.ok()) {
563
0
        return s;
564
0
      }
565
121k
      assert(block_fetcher.GetBlockSizeWithTrailer() == len);
566
121k
      TEST_SYNC_POINT_CALLBACK("ReadTablePropertiesHelper:0",
567
121k
                               &block_contents.data);
568
121k
    } else {
569
155
      assert(s.IsCorruption());
570
      // If retrying, use a stronger file system read to check and correct
571
      // data corruption
572
155
      IOOptions opts;
573
155
      IODebugContext dbg;
574
155
      if (PrepareIOFromReadOptions(ro, ioptions.clock, opts, &dbg) !=
575
155
          IOStatus::OK()) {
576
0
        return s;
577
0
      }
578
155
      opts.verify_and_reconstruct_read = true;
579
155
      std::unique_ptr<char[]> data(new char[len]);
580
155
      Slice result;
581
155
      IOStatus io_s = file->Read(opts, handle.offset(), len, &result,
582
155
                                 data.get(), nullptr, &dbg);
583
155
      RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_COUNT);
584
155
      if (!io_s.ok()) {
585
0
        ROCKS_LOG_INFO(ioptions.info_log,
586
0
                       "Reading properties block failed - %s",
587
0
                       io_s.ToString().c_str());
588
        // Return the original corruption error as that's more serious
589
0
        return s;
590
0
      }
591
155
      if (result.size() < len) {
592
0
        return Status::Corruption("Reading properties block failed - " +
593
0
                                  std::to_string(result.size()) +
594
0
                                  " bytes read");
595
0
      }
596
155
      RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
597
155
      block_contents = BlockContents(std::move(data), handle.size());
598
155
    }
599
600
122k
    uint64_t block_size = block_contents.data.size();
601
122k
    Block properties_block(std::move(block_contents));
602
122k
    std::unique_ptr<TableProperties> new_table_properties{new TableProperties};
603
122k
    s = ParsePropertiesBlock(ioptions, handle.offset(), properties_block,
604
122k
                             new_table_properties);
605
606
    // Modified version of BlockFetcher checksum verification
607
    // (See write_global_seqno comment above)
608
122k
    if (s.ok() && footer.GetBlockTrailerSize() > 0) {
609
122k
      s = VerifyBlockChecksum(footer, properties_block.data(), block_size,
610
122k
                              file->file_name(), handle.offset(),
611
122k
                              BlockType::kProperties);
612
122k
      if (s.IsCorruption()) {
613
0
        if (new_table_properties->external_sst_file_global_seqno_offset != 0) {
614
0
          std::string tmp_buf(properties_block.data(), len);
615
0
          uint64_t global_seqno_offset =
616
0
              new_table_properties->external_sst_file_global_seqno_offset -
617
0
              handle.offset();
618
0
          EncodeFixed64(&tmp_buf[static_cast<size_t>(global_seqno_offset)], 0);
619
0
          s = VerifyBlockChecksum(footer, tmp_buf.data(), block_size,
620
0
                                  file->file_name(), handle.offset(),
621
0
                                  BlockType::kProperties);
622
0
        }
623
0
      }
624
122k
    }
625
626
    // If we detected a corruption and the file system supports verification
627
    // and reconstruction, retry the read
628
122k
    if (s.IsCorruption() && !retry &&
629
0
        CheckFSFeatureSupport(ioptions.fs.get(),
630
0
                              FSSupportedOps::kVerifyAndReconstructRead)) {
631
0
      retry = true;
632
122k
    } else {
633
122k
      if (s.ok()) {
634
122k
        *table_properties = std::move(new_table_properties);
635
122k
      }
636
122k
      break;
637
122k
    }
638
122k
  }
639
640
122k
  return s;
641
122k
}
642
643
Status ReadTableProperties(RandomAccessFileReader* file, uint64_t file_size,
644
                           uint64_t table_magic_number,
645
                           const ImmutableOptions& ioptions,
646
                           const ReadOptions& read_options,
647
                           std::unique_ptr<TableProperties>* properties,
648
                           MemoryAllocator* memory_allocator,
649
0
                           FilePrefetchBuffer* prefetch_buffer) {
650
0
  BlockHandle block_handle;
651
0
  Footer footer;
652
0
  Status s =
653
0
      FindMetaBlockInFile(file, file_size, table_magic_number, ioptions,
654
0
                          read_options, kPropertiesBlockName, &block_handle,
655
0
                          memory_allocator, prefetch_buffer, &footer);
656
0
  if (!s.ok()) {
657
0
    return s;
658
0
  }
659
660
0
  if (!block_handle.IsNull()) {
661
0
    s = ReadTablePropertiesHelper(read_options, block_handle, file,
662
0
                                  prefetch_buffer, footer, ioptions, properties,
663
0
                                  memory_allocator);
664
0
  } else {
665
0
    s = Status::NotFound();
666
0
  }
667
0
  return s;
668
0
}
669
670
Status FindOptionalMetaBlock(InternalIterator* meta_index_iter,
671
                             const std::string& meta_block_name,
672
483k
                             BlockHandle* block_handle) {
673
483k
  assert(block_handle != nullptr);
674
483k
  meta_index_iter->Seek(meta_block_name);
675
483k
  if (meta_index_iter->status().ok()) {
676
472k
    if (meta_index_iter->Valid() && meta_index_iter->key() == meta_block_name) {
677
242k
      Slice v = meta_index_iter->value();
678
242k
      return block_handle->DecodeFrom(&v);
679
242k
    }
680
472k
  }
681
  // else
682
241k
  *block_handle = BlockHandle::NullBlockHandle();
683
241k
  return meta_index_iter->status();
684
483k
}
685
686
Status FindMetaBlock(InternalIterator* meta_index_iter,
687
                     const std::string& meta_block_name,
688
121k
                     BlockHandle* block_handle) {
689
121k
  Status s =
690
121k
      FindOptionalMetaBlock(meta_index_iter, meta_block_name, block_handle);
691
121k
  if (s.ok() && block_handle->IsNull()) {
692
0
    return Status::Corruption("Cannot find the meta block", meta_block_name);
693
121k
  } else {
694
121k
    return s;
695
121k
  }
696
121k
}
697
698
Status ReadMetaIndexBlockInFile(RandomAccessFileReader* file,
699
                                uint64_t file_size, uint64_t table_magic_number,
700
                                const ImmutableOptions& ioptions,
701
                                const ReadOptions& read_options,
702
                                BlockContents* metaindex_contents,
703
                                MemoryAllocator* memory_allocator,
704
                                FilePrefetchBuffer* prefetch_buffer,
705
0
                                Footer* footer_out) {
706
0
  Footer footer;
707
0
  IOOptions opts;
708
0
  IODebugContext dbg;
709
0
  Status s;
710
0
  s = file->PrepareIOOptions(read_options, opts, &dbg);
711
0
  if (!s.ok()) {
712
0
    return s;
713
0
  }
714
0
  s = ReadFooterFromFile(opts, file, *ioptions.fs, prefetch_buffer, file_size,
715
0
                         &footer, table_magic_number, ioptions.stats);
716
0
  if (!s.ok()) {
717
0
    return s;
718
0
  }
719
0
  if (footer_out) {
720
0
    *footer_out = footer;
721
0
  }
722
723
0
  auto metaindex_handle = footer.metaindex_handle();
724
0
  return BlockFetcher(file, prefetch_buffer, footer, read_options,
725
0
                      metaindex_handle, metaindex_contents, ioptions,
726
0
                      false /* do decompression */, false /*maybe_compressed*/,
727
0
                      BlockType::kMetaIndex, nullptr /*decompressor*/,
728
0
                      PersistentCacheOptions::kEmpty, memory_allocator)
729
0
      .ReadBlockContents();
730
0
}
731
732
Status FindMetaBlockInFile(
733
    RandomAccessFileReader* file, uint64_t file_size,
734
    uint64_t table_magic_number, const ImmutableOptions& ioptions,
735
    const ReadOptions& read_options, const std::string& meta_block_name,
736
    BlockHandle* block_handle, MemoryAllocator* memory_allocator,
737
0
    FilePrefetchBuffer* prefetch_buffer, Footer* footer_out) {
738
0
  BlockContents metaindex_contents;
739
0
  auto s = ReadMetaIndexBlockInFile(
740
0
      file, file_size, table_magic_number, ioptions, read_options,
741
0
      &metaindex_contents, memory_allocator, prefetch_buffer, footer_out);
742
0
  if (!s.ok()) {
743
0
    return s;
744
0
  }
745
  // meta blocks are never compressed. Need to add uncompress logic if we are to
746
  // compress it.
747
0
  Block metaindex_block(std::move(metaindex_contents));
748
749
0
  std::unique_ptr<InternalIterator> meta_iter;
750
0
  meta_iter.reset(metaindex_block.NewMetaIterator());
751
752
0
  return FindMetaBlock(meta_iter.get(), meta_block_name, block_handle);
753
0
}
754
755
Status ReadMetaBlock(RandomAccessFileReader* file,
756
                     FilePrefetchBuffer* prefetch_buffer, uint64_t file_size,
757
                     uint64_t table_magic_number,
758
                     const ImmutableOptions& ioptions,
759
                     const ReadOptions& read_options,
760
                     const std::string& meta_block_name, BlockType block_type,
761
                     BlockContents* contents,
762
0
                     MemoryAllocator* memory_allocator) {
763
  // TableProperties requires special handling because of checksum issues.
764
  // Call ReadTableProperties instead for that case.
765
0
  assert(block_type != BlockType::kProperties);
766
767
0
  BlockHandle block_handle;
768
0
  Footer footer;
769
0
  Status status =
770
0
      FindMetaBlockInFile(file, file_size, table_magic_number, ioptions,
771
0
                          read_options, meta_block_name, &block_handle,
772
0
                          memory_allocator, prefetch_buffer, &footer);
773
0
  if (!status.ok()) {
774
0
    return status;
775
0
  }
776
777
0
  return BlockFetcher(file, prefetch_buffer, footer, read_options, block_handle,
778
0
                      contents, ioptions, false /* decompress */,
779
0
                      false /*maybe_compressed*/, block_type,
780
0
                      nullptr /*decompressor*/, PersistentCacheOptions::kEmpty,
781
0
                      memory_allocator)
782
0
      .ReadBlockContents();
783
0
}
784
785
}  // namespace ROCKSDB_NAMESPACE