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