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 #include "table/block_based/hash_index_reader.h"
10
11 #include "table/block_fetcher.h"
12 #include "table/meta_blocks.h"
13
14 namespace ROCKSDB_NAMESPACE {
Create(const BlockBasedTable * table,FilePrefetchBuffer * prefetch_buffer,InternalIterator * meta_index_iter,bool use_cache,bool prefetch,bool pin,BlockCacheLookupContext * lookup_context,std::unique_ptr<IndexReader> * index_reader)15 Status HashIndexReader::Create(const BlockBasedTable* table,
16 FilePrefetchBuffer* prefetch_buffer,
17 InternalIterator* meta_index_iter,
18 bool use_cache, bool prefetch, bool pin,
19 BlockCacheLookupContext* lookup_context,
20 std::unique_ptr<IndexReader>* index_reader) {
21 assert(table != nullptr);
22 assert(index_reader != nullptr);
23 assert(!pin || prefetch);
24
25 const BlockBasedTable::Rep* rep = table->get_rep();
26 assert(rep != nullptr);
27
28 CachableEntry<Block> index_block;
29 if (prefetch || !use_cache) {
30 const Status s =
31 ReadIndexBlock(table, prefetch_buffer, ReadOptions(), use_cache,
32 /*get_context=*/nullptr, lookup_context, &index_block);
33 if (!s.ok()) {
34 return s;
35 }
36
37 if (use_cache && !pin) {
38 index_block.Reset();
39 }
40 }
41
42 // Note, failure to create prefix hash index does not need to be a
43 // hard error. We can still fall back to the original binary search index.
44 // So, Create will succeed regardless, from this point on.
45
46 index_reader->reset(new HashIndexReader(table, std::move(index_block)));
47
48 // Get prefixes block
49 BlockHandle prefixes_handle;
50 Status s =
51 FindMetaBlock(meta_index_iter, kHashIndexPrefixesBlock, &prefixes_handle);
52 if (!s.ok()) {
53 // TODO: log error
54 return Status::OK();
55 }
56
57 // Get index metadata block
58 BlockHandle prefixes_meta_handle;
59 s = FindMetaBlock(meta_index_iter, kHashIndexPrefixesMetadataBlock,
60 &prefixes_meta_handle);
61 if (!s.ok()) {
62 // TODO: log error
63 return Status::OK();
64 }
65
66 RandomAccessFileReader* const file = rep->file.get();
67 const Footer& footer = rep->footer;
68 const ImmutableCFOptions& ioptions = rep->ioptions;
69 const PersistentCacheOptions& cache_options = rep->persistent_cache_options;
70 MemoryAllocator* const memory_allocator =
71 GetMemoryAllocator(rep->table_options);
72
73 // Read contents for the blocks
74 BlockContents prefixes_contents;
75 BlockFetcher prefixes_block_fetcher(
76 file, prefetch_buffer, footer, ReadOptions(), prefixes_handle,
77 &prefixes_contents, ioptions, true /*decompress*/,
78 true /*maybe_compressed*/, BlockType::kHashIndexPrefixes,
79 UncompressionDict::GetEmptyDict(), cache_options, memory_allocator);
80 s = prefixes_block_fetcher.ReadBlockContents();
81 if (!s.ok()) {
82 return s;
83 }
84 BlockContents prefixes_meta_contents;
85 BlockFetcher prefixes_meta_block_fetcher(
86 file, prefetch_buffer, footer, ReadOptions(), prefixes_meta_handle,
87 &prefixes_meta_contents, ioptions, true /*decompress*/,
88 true /*maybe_compressed*/, BlockType::kHashIndexMetadata,
89 UncompressionDict::GetEmptyDict(), cache_options, memory_allocator);
90 s = prefixes_meta_block_fetcher.ReadBlockContents();
91 if (!s.ok()) {
92 // TODO: log error
93 return Status::OK();
94 }
95
96 BlockPrefixIndex* prefix_index = nullptr;
97 assert(rep->internal_prefix_transform.get() != nullptr);
98 s = BlockPrefixIndex::Create(rep->internal_prefix_transform.get(),
99 prefixes_contents.data,
100 prefixes_meta_contents.data, &prefix_index);
101 // TODO: log error
102 if (s.ok()) {
103 HashIndexReader* const hash_index_reader =
104 static_cast<HashIndexReader*>(index_reader->get());
105 hash_index_reader->prefix_index_.reset(prefix_index);
106 }
107
108 return Status::OK();
109 }
110
NewIterator(const ReadOptions & read_options,bool disable_prefix_seek,IndexBlockIter * iter,GetContext * get_context,BlockCacheLookupContext * lookup_context)111 InternalIteratorBase<IndexValue>* HashIndexReader::NewIterator(
112 const ReadOptions& read_options, bool disable_prefix_seek,
113 IndexBlockIter* iter, GetContext* get_context,
114 BlockCacheLookupContext* lookup_context) {
115 const BlockBasedTable::Rep* rep = table()->get_rep();
116 const bool no_io = (read_options.read_tier == kBlockCacheTier);
117 CachableEntry<Block> index_block;
118 const Status s =
119 GetOrReadIndexBlock(no_io, get_context, lookup_context, &index_block);
120 if (!s.ok()) {
121 if (iter != nullptr) {
122 iter->Invalidate(s);
123 return iter;
124 }
125
126 return NewErrorInternalIterator<IndexValue>(s);
127 }
128
129 Statistics* kNullStats = nullptr;
130 const bool total_order_seek =
131 read_options.total_order_seek || disable_prefix_seek;
132 // We don't return pinned data from index blocks, so no need
133 // to set `block_contents_pinned`.
134 auto it = index_block.GetValue()->NewIndexIterator(
135 internal_comparator(), internal_comparator()->user_comparator(),
136 rep->get_global_seqno(BlockType::kIndex), iter, kNullStats,
137 total_order_seek, index_has_first_key(), index_key_includes_seq(),
138 index_value_is_full(), false /* block_contents_pinned */,
139 prefix_index_.get());
140
141 assert(it != nullptr);
142 index_block.TransferTo(it);
143
144 return it;
145 }
146 } // namespace ROCKSDB_NAMESPACE
147