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 #pragma once
6 
7 #ifndef ROCKSDB_LITE
8 
9 #include <limits>
10 #include <string>
11 #include <vector>
12 
13 #include "options/db_options.h"
14 #include "port/port.h"
15 #include "rocksdb/comparator.h"
16 #include "rocksdb/iterator.h"
17 #include "rocksdb/slice.h"
18 #include "rocksdb/status.h"
19 #include "rocksdb/utilities/write_batch_with_index.h"
20 
21 namespace ROCKSDB_NAMESPACE {
22 
23 class MergeContext;
24 struct Options;
25 
26 // Key used by skip list, as the binary searchable index of WriteBatchWithIndex.
27 struct WriteBatchIndexEntry {
WriteBatchIndexEntryWriteBatchIndexEntry28   WriteBatchIndexEntry(size_t o, uint32_t c, size_t ko, size_t ksz)
29       : offset(o),
30         column_family(c),
31         key_offset(ko),
32         key_size(ksz),
33         search_key(nullptr) {}
34   // Create a dummy entry as the search key. This index entry won't be backed
35   // by an entry from the write batch, but a pointer to the search key. Or a
36   // special flag of offset can indicate we are seek to first.
37   // @_search_key: the search key
38   // @_column_family: column family
39   // @is_forward_direction: true for Seek(). False for SeekForPrev()
40   // @is_seek_to_first: true if we seek to the beginning of the column family
41   //                    _search_key should be null in this case.
WriteBatchIndexEntryWriteBatchIndexEntry42   WriteBatchIndexEntry(const Slice* _search_key, uint32_t _column_family,
43                        bool is_forward_direction, bool is_seek_to_first)
44       // For SeekForPrev(), we need to make the dummy entry larger than any
45       // entry who has the same search key. Otherwise, we'll miss those entries.
46       : offset(is_forward_direction ? 0 : port::kMaxSizet),
47         column_family(_column_family),
48         key_offset(0),
49         key_size(is_seek_to_first ? kFlagMinInCf : 0),
50         search_key(_search_key) {
51     assert(_search_key != nullptr || is_seek_to_first);
52   }
53 
54   // If this flag appears in the key_size, it indicates a
55   // key that is smaller than any other entry for the same column family.
56   static const size_t kFlagMinInCf = port::kMaxSizet;
57 
is_min_in_cfWriteBatchIndexEntry58   bool is_min_in_cf() const {
59     assert(key_size != kFlagMinInCf ||
60            (key_offset == 0 && search_key == nullptr));
61     return key_size == kFlagMinInCf;
62   }
63 
64   // offset of an entry in write batch's string buffer. If this is a dummy
65   // lookup key, in which case search_key != nullptr, offset is set to either
66   // 0 or max, only for comparison purpose. Because when entries have the same
67   // key, the entry with larger offset is larger, offset = 0 will make a seek
68   // key small or equal than all the entries with the seek key, so that Seek()
69   // will find all the entries of the same key. Similarly, offset = MAX will
70   // make the entry just larger than all entries with the search key so
71   // SeekForPrev() will see all the keys with the same key.
72   size_t offset;
73   uint32_t column_family;  // c1olumn family of the entry.
74   size_t key_offset;       // offset of the key in write batch's string buffer.
75   size_t key_size;         // size of the key. kFlagMinInCf indicates
76                            // that this is a dummy look up entry for
77                            // SeekToFirst() to the beginning of the column
78                            // family. We use the flag here to save a boolean
79                            // in the struct.
80 
81   const Slice* search_key;  // if not null, instead of reading keys from
82                             // write batch, use it to compare. This is used
83                             // for lookup key.
84 };
85 
86 class ReadableWriteBatch : public WriteBatch {
87  public:
88   explicit ReadableWriteBatch(size_t reserved_bytes = 0, size_t max_bytes = 0)
WriteBatch(reserved_bytes,max_bytes)89       : WriteBatch(reserved_bytes, max_bytes) {}
90   // Retrieve some information from a write entry in the write batch, given
91   // the start offset of the write entry.
92   Status GetEntryFromDataOffset(size_t data_offset, WriteType* type, Slice* Key,
93                                 Slice* value, Slice* blob, Slice* xid) const;
94 };
95 
96 class WriteBatchEntryComparator {
97  public:
WriteBatchEntryComparator(const Comparator * _default_comparator,const ReadableWriteBatch * write_batch)98   WriteBatchEntryComparator(const Comparator* _default_comparator,
99                             const ReadableWriteBatch* write_batch)
100       : default_comparator_(_default_comparator), write_batch_(write_batch) {}
101   // Compare a and b. Return a negative value if a is less than b, 0 if they
102   // are equal, and a positive value if a is greater than b
103   int operator()(const WriteBatchIndexEntry* entry1,
104                  const WriteBatchIndexEntry* entry2) const;
105 
106   int CompareKey(uint32_t column_family, const Slice& key1,
107                  const Slice& key2) const;
108 
SetComparatorForCF(uint32_t column_family_id,const Comparator * comparator)109   void SetComparatorForCF(uint32_t column_family_id,
110                           const Comparator* comparator) {
111     if (column_family_id >= cf_comparators_.size()) {
112       cf_comparators_.resize(column_family_id + 1, nullptr);
113     }
114     cf_comparators_[column_family_id] = comparator;
115   }
116 
default_comparator()117   const Comparator* default_comparator() { return default_comparator_; }
118 
119  private:
120   const Comparator* default_comparator_;
121   std::vector<const Comparator*> cf_comparators_;
122   const ReadableWriteBatch* write_batch_;
123 };
124 
125 class WriteBatchWithIndexInternal {
126  public:
127   enum Result { kFound, kDeleted, kNotFound, kMergeInProgress, kError };
128 
129   // If batch contains a value for key, store it in *value and return kFound.
130   // If batch contains a deletion for key, return Deleted.
131   // If batch contains Merge operations as the most recent entry for a key,
132   //   and the merge process does not stop (not reaching a value or delete),
133   //   prepend the current merge operands to *operands,
134   //   and return kMergeInProgress
135   // If batch does not contain this key, return kNotFound
136   // Else, return kError on error with error Status stored in *s.
137   static WriteBatchWithIndexInternal::Result GetFromBatch(
138       const ImmutableDBOptions& ioptions, WriteBatchWithIndex* batch,
139       ColumnFamilyHandle* column_family, const Slice& key,
140       MergeContext* merge_context, WriteBatchEntryComparator* cmp,
141       std::string* value, bool overwrite_key, Status* s);
142 };
143 
144 }  // namespace ROCKSDB_NAMESPACE
145 #endif  // !ROCKSDB_LITE
146