YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/docdb/doc_key.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#ifndef YB_DOCDB_DOC_KEY_H_
15
#define YB_DOCDB_DOC_KEY_H_
16
17
#include <ostream>
18
#include <vector>
19
20
#include <boost/container/small_vector.hpp>
21
#include <boost/optional/optional.hpp>
22
23
#include "yb/docdb/docdb_fwd.h"
24
#include "yb/docdb/key_bytes.h"
25
#include "yb/docdb/primitive_value.h"
26
27
#include "yb/rocksdb/env.h"
28
#include "yb/rocksdb/filter_policy.h"
29
30
#include "yb/util/ref_cnt_buffer.h"
31
#include "yb/util/slice.h"
32
#include "yb/util/strongly_typed_bool.h"
33
#include "yb/util/uuid.h"
34
35
namespace yb {
36
namespace docdb {
37
38
// ------------------------------------------------------------------------------------------------
39
// DocKey
40
// ------------------------------------------------------------------------------------------------
41
42
// A key that allows us to locate a document. This is the prefix of all RocksDB keys of records
43
// inside this document. A document key contains:
44
//   - An optional ID (cotable id or pgtable id).
45
//   - An optional fixed-width hash prefix.
46
//   - A group of primitive values representing "hashed" components (this is what the hash is
47
//     computed based on, so this group is present/absent together with the hash).
48
//   - A group of "range" components suitable for doing ordered scans.
49
//
50
// The encoded representation of the key is as follows:
51
//   - Optional ID:
52
//     * For cotable id, the byte ValueType::kTableId followed by a sixteen byte UUID.
53
//     * For pgtable id, the byte ValueType::kPgTableOid followed by a four byte PgTableId.
54
//   - Optional fixed-width hash prefix, followed by hashed components:
55
//     * The byte ValueType::kUInt16Hash, followed by two bytes of the hash prefix.
56
//     * Hashed components:
57
//       1. Each hash component consists of a type byte (ValueType) followed by the encoded
58
//          representation of the respective type (see PrimitiveValue's key encoding).
59
//       2. ValueType::kGroupEnd terminates the sequence.
60
//   - Range components are stored similarly to the hashed components:
61
//     1. Each range component consists of a type byte (ValueType) followed by the encoded
62
//        representation of the respective type (see PrimitiveValue's key encoding).
63
//     2. ValueType::kGroupEnd terminates the sequence.
64
YB_DEFINE_ENUM(
65
    DocKeyPart,
66
    (kUpToHashCode)
67
    (kUpToHash)
68
    (kUpToId)
69
    // Includes all doc key components up to hashed ones. If there are no hashed components -
70
    // includes the first range component.
71
    (kUpToHashOrFirstRange)
72
    (kWholeDocKey));
73
74
class DocKeyDecoder;
75
76
YB_STRONGLY_TYPED_BOOL(HybridTimeRequired)
77
78
// Whether to allow parts of the range component of a doc key that should not be present in stored
79
// doc key, but could be used during read, for instance kLowest and kHighest.
80
YB_STRONGLY_TYPED_BOOL(AllowSpecial)
81
82
class DocKey {
83
 public:
84
  // Constructs an empty document key with no hash component.
85
  DocKey();
86
87
  // Construct a document key with only a range component, but no hashed component.
88
  explicit DocKey(std::vector<PrimitiveValue> range_components);
89
90
  // Construct a document key including a hashed component and a range component. The hash value has
91
  // to be calculated outside of the constructor, and we're not assuming any specific hash function
92
  // here.
93
  // @param hash A hash value calculated using the appropriate hash function based on
94
  //             hashed_components.
95
  // @param hashed_components Components of the key that go into computing the hash prefix.
96
  // @param range_components Components of the key that we want to be able to do range scans on.
97
  DocKey(DocKeyHash hash,
98
         std::vector<PrimitiveValue> hashed_components,
99
         std::vector<PrimitiveValue> range_components = std::vector<PrimitiveValue>());
100
101
  DocKey(const Uuid& cotable_id,
102
         DocKeyHash hash,
103
         std::vector<PrimitiveValue> hashed_components,
104
         std::vector<PrimitiveValue> range_components = std::vector<PrimitiveValue>());
105
106
  DocKey(PgTableOid pgtable_id,
107
         DocKeyHash hash,
108
         std::vector<PrimitiveValue> hashed_components,
109
         std::vector<PrimitiveValue> range_components = std::vector<PrimitiveValue>());
110
111
  explicit DocKey(const Uuid& cotable_id);
112
113
  explicit DocKey(PgTableOid pgtable_id);
114
115
  // Constructors to create a DocKey for the given schema to support co-located tables.
116
  explicit DocKey(const Schema& schema);
117
  DocKey(const Schema& schema, DocKeyHash hash);
118
  DocKey(const Schema& schema, std::vector<PrimitiveValue> range_components);
119
  DocKey(const Schema& schema, DocKeyHash hash,
120
         std::vector<PrimitiveValue> hashed_components,
121
         std::vector<PrimitiveValue> range_components = std::vector<PrimitiveValue>());
122
123
  KeyBytes Encode() const;
124
  void AppendTo(KeyBytes* out) const;
125
126
  // Encodes DocKey to binary representation returning result as RefCntPrefix.
127
  RefCntPrefix EncodeAsRefCntPrefix() const;
128
129
  // Resets the state to an empty document key.
130
  void Clear();
131
132
  // Clear the range components of the document key only.
133
  void ClearRangeComponents();
134
135
  // Resize the range components:
136
  //  - drop elements (primitive values) from the end if new_size is smaller than the old size.
137
  //  - append default primitive values (kNullLow) if new_size is bigger than the old size.
138
  void ResizeRangeComponents(int new_size);
139
140
0
  const Uuid& cotable_id() const {
141
0
    return cotable_id_;
142
0
  }
143
144
20.3M
  bool has_cotable_id() const {
145
20.3M
    return !cotable_id_.IsNil();
146
20.3M
  }
147
148
0
  PgTableOid pgtable_id() const {
149
0
    return pgtable_id_;
150
0
  }
151
152
0
  bool has_pgtable_id() const {
153
0
    return pgtable_id_ > 0;
154
0
  }
155
156
4.61M
  DocKeyHash hash() const {
157
4.61M
    return hash_;
158
4.61M
  }
159
160
378
  const std::vector<PrimitiveValue>& hashed_group() const {
161
378
    return hashed_group_;
162
378
  }
163
164
378
  const std::vector<PrimitiveValue>& range_group() const {
165
378
    return range_group_;
166
378
  }
167
168
433k
  std::vector<PrimitiveValue>& hashed_group() {
169
433k
    return hashed_group_;
170
433k
  }
171
172
28.4M
  std::vector<PrimitiveValue>& range_group() {
173
28.4M
    return range_group_;
174
28.4M
  }
175
176
  // Decodes a document key from the given RocksDB key.
177
  // slice (in/out) - a slice corresponding to a RocksDB key. Any consumed bytes are removed.
178
  // part_to_decode specifies which part of key to decode.
179
  CHECKED_STATUS DecodeFrom(
180
      Slice* slice,
181
      DocKeyPart part_to_decode = DocKeyPart::kWholeDocKey,
182
      AllowSpecial allow_special = AllowSpecial::kFalse);
183
184
  // Decodes a document key from the given RocksDB key similar to the above but return the number
185
  // of bytes decoded from the input slice.
186
  Result<size_t> DecodeFrom(
187
      const Slice& slice,
188
      DocKeyPart part_to_decode = DocKeyPart::kWholeDocKey,
189
      AllowSpecial allow_special = AllowSpecial::kFalse);
190
191
  // Splits given RocksDB key into vector of slices that forms range_group of document key.
192
  static CHECKED_STATUS PartiallyDecode(Slice* slice,
193
                                        boost::container::small_vector_base<Slice>* out);
194
195
  // Decode just the hash code of a DocKey.
196
  static Result<DocKeyHash> DecodeHash(const Slice& slice);
197
198
  static Result<size_t> EncodedSize(
199
      Slice slice, DocKeyPart part, AllowSpecial allow_special = AllowSpecial::kFalse);
200
201
  // Returns size of the encoded `part` of DocKey and whether it has hash code present.
202
  static Result<std::pair<size_t, bool>> EncodedSizeAndHashPresent(Slice slice, DocKeyPart part);
203
204
  // Returns size of encoded hash part and whole part of DocKey.
205
  static Result<std::pair<size_t, size_t>> EncodedHashPartAndDocKeySizes(
206
      Slice slice, AllowSpecial allow_special = AllowSpecial::kFalse);
207
208
  // Decode the current document key from the given slice, but expect all bytes to be consumed, and
209
  // return an error status if that is not the case.
210
  CHECKED_STATUS FullyDecodeFrom(const rocksdb::Slice& slice);
211
212
  // Converts the document key to a human-readable representation.
213
  std::string ToString(AutoDecodeKeys auto_decode_keys = AutoDecodeKeys::kFalse) const;
214
  static std::string DebugSliceToString(Slice slice);
215
216
  // Check if it is an empty key.
217
8.04M
  bool empty() const {
218
8.04M
    return !hash_present_ && range_group_.empty();
219
8.04M
  }
220
221
  bool operator ==(const DocKey& other) const;
222
223
0
  bool operator !=(const DocKey& other) const {
224
0
    return !(*this == other);
225
0
  }
226
227
  bool HashedComponentsEqual(const DocKey& other) const;
228
229
  void AddRangeComponent(const PrimitiveValue& val);
230
231
  void SetRangeComponent(const PrimitiveValue& val, int idx);
232
233
  int CompareTo(const DocKey& other) const;
234
235
0
  bool operator <(const DocKey& other) const {
236
0
    return CompareTo(other) < 0;
237
0
  }
238
239
0
  bool operator <=(const DocKey& other) const {
240
0
    return CompareTo(other) <= 0;
241
0
  }
242
243
0
  bool operator >(const DocKey& other) const {
244
0
    return CompareTo(other) > 0;
245
0
  }
246
247
0
  bool operator >=(const DocKey& other) const {
248
0
    return CompareTo(other) >= 0;
249
0
  }
250
251
  bool BelongsTo(const Schema& schema) const;
252
253
0
  void set_cotable_id(const Uuid& cotable_id) {
254
0
    if (!cotable_id.IsNil()) {
255
0
      DCHECK_EQ(pgtable_id_, 0);
256
0
    }
257
0
    cotable_id_ = cotable_id;
258
0
  }
259
260
0
  void set_pgtable_id(const PgTableOid pgtable_id) {
261
0
    if (pgtable_id > 0) {
262
0
      DCHECK(cotable_id_.IsNil());
263
0
    }
264
0
    pgtable_id_ = pgtable_id;
265
0
  }
266
267
160k
  void set_hash(DocKeyHash hash) {
268
160k
    hash_ = hash;
269
160k
    hash_present_ = true;
270
160k
  }
271
272
6.92M
  bool has_hash() const {
273
6.92M
    return hash_present_;
274
6.92M
  }
275
276
  // Converts a redis string key to a doc key
277
  static DocKey FromRedisKey(uint16_t hash, const std::string& key);
278
  static KeyBytes EncodedFromRedisKey(uint16_t hash, const std::string &key);
279
280
 private:
281
  class DecodeFromCallback;
282
  friend class DecodeFromCallback;
283
284
  template<class Callback>
285
  static CHECKED_STATUS DoDecode(DocKeyDecoder* decoder,
286
                                 DocKeyPart part_to_decode,
287
                                 AllowSpecial allow_special,
288
                                 const Callback& callback);
289
290
  // Uuid of the non-primary table this DocKey belongs to co-located in a tablet. Nil for the
291
  // primary or single-tenant table.
292
  Uuid cotable_id_;
293
294
  // Postgres table OID of the non-primary table this DocKey belongs to in colocated tables.
295
  // 0 for primary or single tenant table.
296
  PgTableOid pgtable_id_;
297
298
  // TODO: can we get rid of this field and just use !hashed_group_.empty() instead?
299
  bool hash_present_;
300
301
  DocKeyHash hash_;
302
  std::vector<PrimitiveValue> hashed_group_;
303
  std::vector<PrimitiveValue> range_group_;
304
};
305
306
template <class Collection>
307
47.8M
void AppendDocKeyItems(const Collection& doc_key_items, KeyBytes* result) {
308
40.5M
  for (const auto& item : doc_key_items) {
309
40.5M
    item.AppendToKey(result);
310
40.5M
  }
311
47.8M
  result->AppendGroupEnd();
312
47.8M
}
_ZN2yb5docdb17AppendDocKeyItemsINSt3__16vectorINS0_14PrimitiveValueENS2_9allocatorIS4_EEEEEEvRKT_PNS0_8KeyBytesE
Line
Count
Source
307
47.8M
void AppendDocKeyItems(const Collection& doc_key_items, KeyBytes* result) {
308
40.4M
  for (const auto& item : doc_key_items) {
309
40.4M
    item.AppendToKey(result);
310
40.4M
  }
311
47.8M
  result->AppendGroupEnd();
312
47.8M
}
_ZN2yb5docdb17AppendDocKeyItemsISt16initializer_listINS0_14PrimitiveValueEEEEvRKT_PNS0_8KeyBytesE
Line
Count
Source
307
81.4k
void AppendDocKeyItems(const Collection& doc_key_items, KeyBytes* result) {
308
47.4k
  for (const auto& item : doc_key_items) {
309
47.4k
    item.AppendToKey(result);
310
47.4k
  }
311
81.4k
  result->AppendGroupEnd();
312
81.4k
}
313
314
class DocKeyEncoderAfterHashStep {
315
 public:
316
26.3M
  explicit DocKeyEncoderAfterHashStep(KeyBytes* out) : out_(out) {}
317
318
  template <class Collection>
319
26.4M
  void Range(const Collection& range_group) {
320
26.4M
    AppendDocKeyItems(range_group, out_);
321
26.4M
  }
_ZN2yb5docdb26DocKeyEncoderAfterHashStep5RangeISt16initializer_listINS0_14PrimitiveValueEEEEvRKT_
Line
Count
Source
319
40.7k
  void Range(const Collection& range_group) {
320
40.7k
    AppendDocKeyItems(range_group, out_);
321
40.7k
  }
_ZN2yb5docdb26DocKeyEncoderAfterHashStep5RangeINSt3__16vectorINS0_14PrimitiveValueENS3_9allocatorIS5_EEEEEEvRKT_
Line
Count
Source
319
26.3M
  void Range(const Collection& range_group) {
320
26.3M
    AppendDocKeyItems(range_group, out_);
321
26.3M
  }
322
323
 private:
324
  KeyBytes* out_;
325
};
326
327
class DocKeyEncoderAfterTableIdStep {
328
 public:
329
26.6M
  explicit DocKeyEncoderAfterTableIdStep(KeyBytes* out) : out_(out) {
330
26.6M
  }
331
332
  template <class Collection>
333
  DocKeyEncoderAfterHashStep Hash(
334
17.2M
      bool hash_present, uint16_t hash, const Collection& hashed_group) {
335
17.2M
    if (!hash_present) {
336
4.89M
      return DocKeyEncoderAfterHashStep(out_);
337
4.89M
    }
338
339
12.3M
    return Hash(hash, hashed_group);
340
12.3M
  }
341
342
  template <class Collection>
343
21.5M
  DocKeyEncoderAfterHashStep Hash(uint16_t hash, const Collection& hashed_group) {
344
    // We are not setting the "more items in group" bit on the hash field because it is not part
345
    // of "hashed" or "range" groups.
346
21.5M
    AppendHash(hash, out_);
347
21.5M
    AppendDocKeyItems(hashed_group, out_);
348
349
21.5M
    return DocKeyEncoderAfterHashStep(out_);
350
21.5M
  }
_ZN2yb5docdb29DocKeyEncoderAfterTableIdStep4HashINSt3__16vectorINS0_14PrimitiveValueENS3_9allocatorIS5_EEEEEENS0_26DocKeyEncoderAfterHashStepEtRKT_
Line
Count
Source
343
21.4M
  DocKeyEncoderAfterHashStep Hash(uint16_t hash, const Collection& hashed_group) {
344
    // We are not setting the "more items in group" bit on the hash field because it is not part
345
    // of "hashed" or "range" groups.
346
21.4M
    AppendHash(hash, out_);
347
21.4M
    AppendDocKeyItems(hashed_group, out_);
348
349
21.4M
    return DocKeyEncoderAfterHashStep(out_);
350
21.4M
  }
_ZN2yb5docdb29DocKeyEncoderAfterTableIdStep4HashISt16initializer_listINS0_14PrimitiveValueEEEENS0_26DocKeyEncoderAfterHashStepEtRKT_
Line
Count
Source
343
40.7k
  DocKeyEncoderAfterHashStep Hash(uint16_t hash, const Collection& hashed_group) {
344
    // We are not setting the "more items in group" bit on the hash field because it is not part
345
    // of "hashed" or "range" groups.
346
40.7k
    AppendHash(hash, out_);
347
40.7k
    AppendDocKeyItems(hashed_group, out_);
348
349
40.7k
    return DocKeyEncoderAfterHashStep(out_);
350
40.7k
  }
351
352
  template <class HashCollection, class RangeCollection>
353
  void HashAndRange(uint16_t hash, const HashCollection& hashed_group,
354
9.10M
                    const RangeCollection& range_collection) {
355
9.10M
    Hash(hash, hashed_group).Range(range_collection);
356
9.10M
  }
357
358
  void HashAndRange(uint16_t hash, const std::initializer_list<PrimitiveValue>& hashed_group,
359
40.7k
                    const std::initializer_list<PrimitiveValue>& range_collection) {
360
40.7k
    Hash(hash, hashed_group).Range(range_collection);
361
40.7k
  }
362
363
 private:
364
  KeyBytes* out_;
365
};
366
367
class DocKeyEncoder {
368
 public:
369
26.6M
  explicit DocKeyEncoder(KeyBytes* out) : out_(out) {}
370
371
  DocKeyEncoderAfterTableIdStep CotableId(const Uuid& cotable_id);
372
373
  DocKeyEncoderAfterTableIdStep PgtableId(const PgTableOid pgtable_id);
374
375
  DocKeyEncoderAfterTableIdStep Schema(const Schema& schema);
376
377
 private:
378
  KeyBytes* out_;
379
};
380
381
class DocKeyDecoder {
382
 public:
383
384M
  explicit DocKeyDecoder(const Slice& input) : input_(input) {}
384
385
  Result<bool> DecodeCotableId(Uuid* uuid = nullptr);
386
  Result<bool> DecodePgtableId(PgTableOid* pgtable_id = nullptr);
387
388
  Result<bool> HasPrimitiveValue();
389
390
  Result<bool> DecodeHashCode(
391
      uint16_t* out = nullptr, AllowSpecial allow_special = AllowSpecial::kFalse);
392
393
  Result<bool> DecodeHashCode(AllowSpecial allow_special);
394
395
  CHECKED_STATUS DecodePrimitiveValue(
396
      PrimitiveValue* out = nullptr, AllowSpecial allow_special = AllowSpecial::kFalse);
397
398
  CHECKED_STATUS DecodePrimitiveValue(AllowSpecial allow_special);
399
400
  CHECKED_STATUS ConsumeGroupEnd();
401
402
  bool GroupEnded() const;
403
404
694M
  const Slice& left_input() const {
405
694M
    return input_;
406
694M
  }
407
408
31.7M
  size_t ConsumedSizeFrom(const uint8_t* start) const {
409
31.7M
    return input_.data() - start;
410
31.7M
  }
411
412
440M
  Slice* mutable_input() {
413
440M
    return &input_;
414
440M
  }
415
416
  CHECKED_STATUS DecodeToRangeGroup();
417
418
 private:
419
  Slice input_;
420
};
421
422
// Clears range components from provided key. Returns true if they were exists.
423
Result<bool> ClearRangeComponents(KeyBytes* out, AllowSpecial allow_special = AllowSpecial::kFalse);
424
425
// Returns true if both keys have hashed components and them are equal or both keys don't have
426
// hashed components and first range components are equal and false otherwise.
427
Result<bool> HashedOrFirstRangeComponentsEqual(const Slice& lhs, const Slice& rhs);
428
429
bool DocKeyBelongsTo(Slice doc_key, const Schema& schema);
430
431
// Consumes single primitive value from start of slice.
432
// Returns true when value was consumed, false when group end is found. The group end byte is
433
// consumed in the latter case.
434
Result<bool> ConsumePrimitiveValueFromKey(Slice* slice);
435
436
// Consume a group of document key components, ending with ValueType::kGroupEnd.
437
// @param slice - the current point at which we are decoding a key
438
// @param result - vector to append decoded values to.
439
Status ConsumePrimitiveValuesFromKey(rocksdb::Slice* slice,
440
                                     std::vector<PrimitiveValue>* result);
441
442
Result<boost::optional<DocKeyHash>> DecodeDocKeyHash(const Slice& encoded_key);
443
444
0
inline std::ostream& operator <<(std::ostream& out, const DocKey& doc_key) {
445
0
  out << doc_key.ToString();
446
0
  return out;
447
0
}
448
449
// ------------------------------------------------------------------------------------------------
450
// SubDocKey
451
// ------------------------------------------------------------------------------------------------
452
453
// A key pointing to a subdocument. Consists of a DocKey identifying the document, a list of
454
// primitive values leading to the subdocument in question, from the outermost to innermost order,
455
// and an optional hybrid_time of when the subdocument (which may itself be a primitive value) was
456
// last fully overwritten or deleted.
457
//
458
// Keys stored in RocksDB should always have the hybrid_time field set. However, it is useful to
459
// make the hybrid_time field optional while a SubDocKey is being constructed. If the hybrid_time
460
// is not set, it is omitted from the encoded representation of a SubDocKey.
461
//
462
// Implementation note: we use HybridTime::kInvalid to represent an omitted hybrid_time.
463
// We rely on that being the default-constructed value of a HybridTime.
464
//
465
// TODO: this should be renamed to something more generic, e.g. Key or LogicalKey, to reflect that
466
// this is actually the logical representation of keys that we store in the RocksDB key-value store.
467
class SubDocKey {
468
 public:
469
130M
  SubDocKey() {}
470
543
  explicit SubDocKey(const DocKey& doc_key) : doc_key_(doc_key) {}
471
63.5k
  explicit SubDocKey(DocKey&& doc_key) : doc_key_(std::move(doc_key)) {}
472
473
  SubDocKey(const DocKey& doc_key, HybridTime hybrid_time)
474
      : doc_key_(doc_key),
475
1.88M
        doc_ht_(DocHybridTime(hybrid_time)) {
476
1.88M
  }
477
478
  SubDocKey(DocKey&& doc_key,
479
            HybridTime hybrid_time)
480
      : doc_key_(std::move(doc_key)),
481
0
        doc_ht_(DocHybridTime(hybrid_time)) {
482
0
  }
483
484
  SubDocKey(const DocKey& doc_key, const DocHybridTime& hybrid_time)
485
      : doc_key_(doc_key),
486
0
        doc_ht_(std::move(hybrid_time)) {
487
0
  }
488
489
  SubDocKey(const DocKey& doc_key,
490
            DocHybridTime doc_hybrid_time,
491
            const std::vector<PrimitiveValue>& subkeys)
492
      : doc_key_(doc_key),
493
        doc_ht_(doc_hybrid_time),
494
0
        subkeys_(subkeys) {
495
0
  }
496
497
  SubDocKey(const DocKey& doc_key,
498
            HybridTime hybrid_time,
499
            const std::vector<PrimitiveValue>& subkeys)
500
      : doc_key_(doc_key),
501
        doc_ht_(DocHybridTime(hybrid_time)),
502
0
        subkeys_(subkeys) {
503
0
  }
504
505
  template <class ...T>
506
  SubDocKey(const DocKey& doc_key, T... subkeys_and_maybe_hybrid_time)
507
      : doc_key_(doc_key),
508
2.09M
        doc_ht_(DocHybridTime::kInvalid) {
509
2.09M
    AppendSubKeysAndMaybeHybridTime(subkeys_and_maybe_hybrid_time...);
510
2.09M
  }
Unexecuted instantiation: _ZN2yb5docdb9SubDocKeyC2IJNS0_14PrimitiveValueENS_10HybridTimeEEEERKNS0_6DocKeyEDpT_
Unexecuted instantiation: _ZN2yb5docdb9SubDocKeyC2IJNS0_14PrimitiveValueES3_S3_NS_10HybridTimeEEEERKNS0_6DocKeyEDpT_
_ZN2yb5docdb9SubDocKeyC2IJNS0_14PrimitiveValueEEEERKNS0_6DocKeyEDpT_
Line
Count
Source
508
2.08M
        doc_ht_(DocHybridTime::kInvalid) {
509
2.08M
    AppendSubKeysAndMaybeHybridTime(subkeys_and_maybe_hybrid_time...);
510
2.08M
  }
_ZN2yb5docdb9SubDocKeyC2IJNS0_14PrimitiveValueES3_EEERKNS0_6DocKeyEDpT_
Line
Count
Source
508
10.5k
        doc_ht_(DocHybridTime::kInvalid) {
509
10.5k
    AppendSubKeysAndMaybeHybridTime(subkeys_and_maybe_hybrid_time...);
510
10.5k
  }
511
512
  CHECKED_STATUS FromDocPath(const DocPath& doc_path);
513
514
  // Return the subkeys within this SubDocKey
515
0
  const std::vector<PrimitiveValue>& subkeys() const {
516
0
    return subkeys_;
517
0
  }
518
519
10
  std::vector<PrimitiveValue>& subkeys() {
520
10
    return subkeys_;
521
10
  }
522
523
  // Append a sequence of sub-keys to this key.
524
  template<class ...T>
525
  void AppendSubKeysAndMaybeHybridTime(PrimitiveValue subdoc_key,
526
10.5k
                                       T... subkeys_and_maybe_hybrid_time) {
527
10.5k
    subkeys_.push_back(std::move(subdoc_key));
528
10.5k
    AppendSubKeysAndMaybeHybridTime(subkeys_and_maybe_hybrid_time...);
529
10.5k
  }
Unexecuted instantiation: _ZN2yb5docdb9SubDocKey31AppendSubKeysAndMaybeHybridTimeIJNS0_14PrimitiveValueES3_NS_10HybridTimeEEEEvS3_DpT_
Unexecuted instantiation: _ZN2yb5docdb9SubDocKey31AppendSubKeysAndMaybeHybridTimeIJNS0_14PrimitiveValueENS_10HybridTimeEEEEvS3_DpT_
_ZN2yb5docdb9SubDocKey31AppendSubKeysAndMaybeHybridTimeIJNS0_14PrimitiveValueEEEEvS3_DpT_
Line
Count
Source
526
10.5k
                                       T... subkeys_and_maybe_hybrid_time) {
527
10.5k
    subkeys_.push_back(std::move(subdoc_key));
528
10.5k
    AppendSubKeysAndMaybeHybridTime(subkeys_and_maybe_hybrid_time...);
529
10.5k
  }
530
531
  template<class ...T>
532
2.09M
  void AppendSubKeysAndMaybeHybridTime(PrimitiveValue subdoc_key) {
533
2.09M
    subkeys_.emplace_back(std::move(subdoc_key));
534
2.09M
  }
535
536
  template<class ...T>
537
0
  void AppendSubKeysAndMaybeHybridTime(PrimitiveValue subdoc_key, HybridTime hybrid_time) {
538
0
    DCHECK(!has_hybrid_time());
539
0
    subkeys_.emplace_back(subdoc_key);
540
0
    DCHECK(hybrid_time.is_valid());
541
0
    doc_ht_ = DocHybridTime(hybrid_time);
542
0
  }
543
544
  void AppendSubKey(PrimitiveValue subkey);
545
546
  void RemoveLastSubKey();
547
548
  void KeepPrefix(size_t num_sub_keys_to_keep);
549
550
0
  void remove_hybrid_time() {
551
0
    doc_ht_ = DocHybridTime::kInvalid;
552
0
  }
553
554
  void Clear();
555
556
0
  bool IsValid() const {
557
0
    return !doc_key_.empty();
558
0
  }
559
560
2.16M
  KeyBytes Encode() const { return DoEncode(true /* include_hybrid_time */); }
561
11.5k
  KeyBytes EncodeWithoutHt() const { return DoEncode(false /* include_hybrid_time */); }
562
563
  // Decodes a SubDocKey from the given slice, typically retrieved from a RocksDB key.
564
  // @param slice
565
  //     A pointer to the slice containing the bytes to decode the SubDocKey from. This slice is
566
  //     modified, with consumed bytes being removed.
567
  // @param require_hybrid_time
568
  //     Whether a hybrid_time is required in the end of the SubDocKey. If this is true, we require
569
  //     a ValueType::kHybridTime byte followed by a hybrid_time to be present in the input slice.
570
  //     Otherwise, we allow decoding an incomplete SubDocKey without a hybrid_time in the end. Note
571
  //     that we also allow input that has a few bytes in the end but not enough to represent a
572
  //     hybrid_time.
573
  // @param allow_special
574
  //     Whether it is allowed to have special value types in slice, that are used during seek.
575
  //     If such value type is found, decoding is stopped w/o error.
576
  CHECKED_STATUS DecodeFrom(rocksdb::Slice* slice,
577
                            HybridTimeRequired require_hybrid_time = HybridTimeRequired::kTrue,
578
                            AllowSpecial allow_special = AllowSpecial::kFalse);
579
580
  // Similar to DecodeFrom, but requires that the entire slice is decoded, and thus takes a const
581
  // reference to a slice. This still respects the require_hybrid_time parameter, but in case a
582
  // hybrid_time is omitted, we don't allow any extra bytes to be present in the slice.
583
  CHECKED_STATUS FullyDecodeFrom(
584
      const rocksdb::Slice& slice,
585
      HybridTimeRequired hybrid_time_required = HybridTimeRequired::kTrue);
586
587
  // Splits given RocksDB key into vector of slices that forms range_group of document key and
588
  // hybrid_time.
589
  static CHECKED_STATUS PartiallyDecode(Slice* slice,
590
                                        boost::container::small_vector_base<Slice>* out);
591
592
  // Splits the given RocksDB sub key into a vector of slices that forms the range group of document
593
  // key and sub keys.
594
  //
595
  // We don't use Result<...> to be able to reuse memory allocated by out.
596
  //
597
  // When key does not start with a hash component, the returned prefix would start with the first
598
  // range component.
599
  //
600
  // For instance, for a (hash_value, h1, h2, r1, r2, s1) doc key the following values will be
601
  // returned:
602
  // encoded_length(hash_value, h1, h2) <-------------- (includes the kGroupEnd of the hashed part)
603
  // encoded_length(hash_value, h1, h2, r1)
604
  // encoded_length(hash_value, h1, h2, r1, r2)
605
  // encoded_length(hash_value, h1, h2, r1, r2, s1) <------- (includes kGroupEnd of the range part).
606
  static CHECKED_STATUS DecodePrefixLengths(
607
      Slice slice, boost::container::small_vector_base<size_t>* out);
608
609
  // Fills out with ends of SubDocKey components.  First item in out will be size of ID part
610
  // (cotable id or pgtable id) of DocKey (0 if ID is not present), second size of whole DocKey,
611
  // third size of DocKey + size of first subkey, and so on.
612
  //
613
  // To illustrate,
614
  // * for key
615
  //     SubDocKey(DocKey(0xfca0, [3], []), [SystemColumnId(0); HT{ physical: 1581475435181551 }])
616
  //   aka
617
  //     47FCA0488000000321214A80238001B5E605A0CA10804A
618
  //   (and with spaces to make it clearer)
619
  //     47FCA0 4880000003 21 21 4A80 238001B5E605A0CA10804A
620
  //   the ends will be
621
  //     {0, 10, 12}
622
  // * for key
623
  //     SubDocKey(DocKey(PgTableId=16385, [], [5]), [SystemColumnId(0); HT{ physical: ... }])
624
  //   aka
625
  //     30000040014880000005214A80238001B5E700309553804A
626
  //   (and with spaces to make it clearer)
627
  //     3000004001 4880000005 21 4A80 238001B5E700309553804A
628
  //   the ends will be
629
  //     {5, 11, 13}
630
  // * for key
631
  //     SubDocKey(DocKey(PgTableId=16385, [], []), [HT{ physical: 1581471227403848 }])
632
  //   aka
633
  //     300000400121238001B5E7006E61B7804A
634
  //   (and with spaces to make it clearer)
635
  //     3000004001 21 238001B5E7006E61B7804A
636
  //   the ends will be
637
  //     {5}
638
  //
639
  // If out is not empty, then it will be interpreted as partial result for this decoding operation
640
  // and the appropriate prefix will be skipped.
641
  static CHECKED_STATUS DecodeDocKeyAndSubKeyEnds(
642
      Slice slice, boost::container::small_vector_base<size_t>* out);
643
644
  // Attempts to decode a subkey at the beginning of the given slice, consuming the corresponding
645
  // prefix of the slice. Returns false if there is no next subkey, as indicated by the slice being
646
  // empty or encountering an encoded hybrid time.
647
  static Result<bool> DecodeSubkey(Slice* slice);
648
649
  CHECKED_STATUS FullyDecodeFromKeyWithOptionalHybridTime(const rocksdb::Slice& slice);
650
651
  std::string ToString(AutoDecodeKeys auto_decode_keys = AutoDecodeKeys::kFalse) const;
652
  static std::string DebugSliceToString(Slice slice);
653
  static Result<std::string> DebugSliceToStringAsResult(Slice slice);
654
655
756
  const DocKey& doc_key() const {
656
756
    return doc_key_;
657
756
  }
658
659
54.1M
  DocKey& doc_key() {
660
54.1M
    return doc_key_;
661
54.1M
  }
662
663
81
  size_t num_subkeys() const {
664
81
    return subkeys_.size();
665
81
  }
666
667
  bool StartsWith(const SubDocKey& prefix) const;
668
669
  bool operator ==(const SubDocKey& other) const;
670
671
0
  bool operator !=(const SubDocKey& other) const {
672
0
    return !(*this == other);
673
0
  }
674
675
0
  const PrimitiveValue& last_subkey() const {
676
0
    assert(!subkeys_.empty());
677
0
    return subkeys_.back();
678
0
  }
679
680
  int CompareTo(const SubDocKey& other) const;
681
  int CompareToIgnoreHt(const SubDocKey& other) const;
682
683
0
  bool operator <(const SubDocKey& other) const {
684
0
    return CompareTo(other) < 0;
685
0
  }
686
687
0
  bool operator <=(const SubDocKey& other) const {
688
0
    return CompareTo(other) <= 0;
689
0
  }
690
691
0
  bool operator >(const SubDocKey& other) const {
692
0
    return CompareTo(other) > 0;
693
0
  }
694
695
0
  bool operator >=(const SubDocKey& other) const {
696
0
    return CompareTo(other) >= 0;
697
0
  }
698
699
0
  HybridTime hybrid_time() const {
700
0
    DCHECK(has_hybrid_time());
701
0
    return doc_ht_.hybrid_time();
702
0
  }
703
704
27.1M
  const DocHybridTime& doc_hybrid_time() const {
705
27.1M
    DCHECK(has_hybrid_time());
706
27.1M
    return doc_ht_;
707
27.1M
  }
708
709
0
  void set_hybrid_time(const DocHybridTime& hybrid_time) {
710
0
    DCHECK(hybrid_time.is_valid());
711
0
    doc_ht_ = hybrid_time;
712
0
  }
713
714
64.0M
  bool has_hybrid_time() const {
715
64.0M
    return doc_ht_.is_valid();
716
64.0M
  }
717
718
  // Generate a RocksDB key that would allow us to seek to the smallest SubDocKey that has a
719
  // lexicographically higher sequence of subkeys than this one, but is not an extension of this
720
  // sequence of subkeys.  In other words, ensure we advance to the next field (subkey) either
721
  // within the object (subdocument) we are currently scanning, or at any higher level, including
722
  // advancing to the next document key.
723
  //
724
  // E.g. assuming the SubDocKey this is being called on is #2 from the following example,
725
  // performing a RocksDB seek on the return value of this takes us to #7.
726
  //
727
  // 1. SubDocKey(DocKey([], ["a"]), [HT(1)]) -> {}
728
  // 2. SubDocKey(DocKey([], ["a"]), ["x", HT(1)]) -> {} ---------------------------.
729
  // 3. SubDocKey(DocKey([], ["a"]), ["x", "x", HT(2)]) -> null                     |
730
  // 4. SubDocKey(DocKey([], ["a"]), ["x", "x", HT(1)]) -> {}                       |
731
  // 5. SubDocKey(DocKey([], ["a"]), ["x", "x", "y", HT(1)]) -> {}                  |
732
  // 6. SubDocKey(DocKey([], ["a"]), ["x", "x", "y", "x", HT(1)]) -> true           |
733
  // 7. SubDocKey(DocKey([], ["a"]), ["y", HT(3)]) -> {}                  <---------
734
  // 8. SubDocKey(DocKey([], ["a"]), ["y", "y", HT(3)]) -> {}
735
  // 9. SubDocKey(DocKey([], ["a"]), ["y", "y", "x", HT(3)]) ->
736
  //
737
  // This is achieved by simply appending a byte that is higher than any ValueType in an encoded
738
  // representation of a SubDocKey that extends the vector of subkeys present in the current one,
739
  // or has the same vector of subkeys, i.e. key/value pairs #3-6 in the above example. HybridTime
740
  // is omitted from the resulting encoded representation.
741
  KeyBytes AdvanceOutOfSubDoc() const;
742
743
  // Similar to AdvanceOutOfSubDoc, but seek to the smallest key that skips documents with this
744
  // DocKey and DocKeys that have the same hash components but add more range components to it.
745
  //
746
  // E.g. assuming the SubDocKey this is being called on is #2 from the following example:
747
  //
748
  //  1. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), [HT(1)]) -> {}
749
  //  2. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["x", HT(1)]) -> {} <----------------.
750
  //  3. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["x", "x", HT(2)]) -> null           |
751
  //  4. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["x", "x", HT(1)]) -> {}             |
752
  //  5. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["x", "x", "y", HT(1)]) -> {}        |
753
  //  6. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["x", "x", "y", "x", HT(1)]) -> true |
754
  //  7. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["y", HT(3)]) -> {}                  |
755
  //  8. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["y", "y", HT(3)]) -> {}             |
756
  //  9. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"]), ["y", "y", "x", HT(3)]) -> {}        |
757
  // ...                                                                                        |
758
  // 20. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d", "e"]), ["y", HT(3)]) -> {}             |
759
  // 21. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d", "e"]), ["z", HT(3)]) -> {}             |
760
  // 22. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "f"]), [HT(1)]) -> {}      <--- (*** 1 ***)-|
761
  // 23. SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "f"]), ["x", HT(1)]) -> {}                  |
762
  // ...                                                                                        |
763
  // 30. SubDocKey(DocKey(0x2345, ["a", "c"], ["c", "f"]), [HT(1)]) -> {}      <--- (*** 2 ***)-
764
  // 31. SubDocKey(DocKey(0x2345, ["a", "c"], ["c", "f"]), ["x", HT(1)]) -> {}
765
  //
766
  // SubDocKey(DocKey(0x1234, ["a", "b"], ["c", "d"])).AdvanceOutOfDocKeyPrefix() will seek to #22
767
  // (*** 1 ***), pass doc keys with additional range components when they are present.
768
  //
769
  // And when given a doc key without range component like below, it can help seek pass all doc
770
  // keys with the same hash components, e.g.
771
  // SubDocKey(DocKey(0x1234, ["a", "b"], [])).AdvanceOutOfDocKeyPrefix() will seek to #30
772
  // (*** 2 ***).
773
774
  KeyBytes AdvanceOutOfDocKeyPrefix() const;
775
776
 private:
777
  class DecodeCallback;
778
  friend class DecodeCallback;
779
780
  // Attempts to decode and consume a subkey from the beginning of the given slice.
781
  // A non-error false result means e.g. that the slice is empty or if the next thing is an encoded
782
  // hybrid time.
783
  template<class Callback>
784
  static Result<bool> DecodeSubkey(Slice* slice, const Callback& callback);
785
786
  template<class Callback>
787
  static Status DoDecode(rocksdb::Slice* slice,
788
                         HybridTimeRequired require_hybrid_time,
789
                         AllowSpecial allow_special,
790
                         const Callback& callback);
791
792
  KeyBytes DoEncode(bool include_hybrid_time) const;
793
794
  DocKey doc_key_;
795
  DocHybridTime doc_ht_;
796
797
  // TODO: make this a small_vector.
798
  std::vector<PrimitiveValue> subkeys_;
799
};
800
801
0
inline std::ostream& operator <<(std::ostream& out, const SubDocKey& subdoc_key) {
802
0
  out << subdoc_key.ToString();
803
0
  return out;
804
0
}
805
806
// A best-effort to decode the given sequence of key bytes as either a DocKey or a SubDocKey.
807
// If not possible to decode, return the key_bytes directly as a readable string.
808
std::string BestEffortDocDBKeyToStr(const KeyBytes &key_bytes);
809
std::string BestEffortDocDBKeyToStr(const rocksdb::Slice &slice);
810
811
class DocDbAwareFilterPolicyBase : public rocksdb::FilterPolicy {
812
 public:
813
1.26M
  explicit DocDbAwareFilterPolicyBase(size_t filter_block_size_bits, rocksdb::Logger* logger) {
814
1.26M
    builtin_policy_.reset(rocksdb::NewFixedSizeFilterPolicy(
815
1.26M
        filter_block_size_bits, rocksdb::FilterPolicy::kDefaultFixedSizeFilterErrorRate, logger));
816
1.26M
  }
817
818
  void CreateFilter(const rocksdb::Slice* keys, int n, std::string* dst) const override;
819
820
  bool KeyMayMatch(const rocksdb::Slice& key, const rocksdb::Slice& filter) const override;
821
822
  rocksdb::FilterBitsBuilder* GetFilterBitsBuilder() const override;
823
824
  rocksdb::FilterBitsReader* GetFilterBitsReader(const rocksdb::Slice& contents) const override;
825
826
  FilterType GetFilterType() const override;
827
828
 private:
829
  std::unique_ptr<const rocksdb::FilterPolicy> builtin_policy_;
830
};
831
832
// This filter policy only takes into account hashed components of keys for filtering.
833
class DocDbAwareHashedComponentsFilterPolicy : public DocDbAwareFilterPolicyBase {
834
 public:
835
  DocDbAwareHashedComponentsFilterPolicy(size_t filter_block_size_bits, rocksdb::Logger* logger)
836
422k
      : DocDbAwareFilterPolicyBase(filter_block_size_bits, logger) {}
837
838
422k
  const char* Name() const override { return "DocKeyHashedComponentsFilter"; }
839
840
  const KeyTransformer* GetKeyTransformer() const override;
841
};
842
843
// Together with the fix for BlockBasedTableBuild::Add
844
// (https://github.com/yugabyte/yugabyte-db/issues/6435) we also disable DocKeyV2Filter
845
// for range-partitioned tablets. For hash-partitioned tablets it will be supported during read
846
// path and will work the same way as DocDbAwareV3FilterPolicy.
847
class DocDbAwareV2FilterPolicy : public DocDbAwareFilterPolicyBase {
848
 public:
849
  DocDbAwareV2FilterPolicy(size_t filter_block_size_bits, rocksdb::Logger* logger)
850
422k
      : DocDbAwareFilterPolicyBase(filter_block_size_bits, logger) {}
851
852
422k
  const char* Name() const override { return "DocKeyV2Filter"; }
853
854
  const KeyTransformer* GetKeyTransformer() const override;
855
};
856
857
// This filter policy takes into account following parts of keys for filtering:
858
// - For range-based partitioned tables (such tables have 0 hashed components):
859
// use all hash components of the doc key.
860
// - For hash-based partitioned tables (such tables have >0 hashed components):
861
// use first range component of the doc key.
862
class DocDbAwareV3FilterPolicy : public DocDbAwareFilterPolicyBase {
863
 public:
864
  DocDbAwareV3FilterPolicy(size_t filter_block_size_bits, rocksdb::Logger* logger)
865
422k
      : DocDbAwareFilterPolicyBase(filter_block_size_bits, logger) {}
866
867
1.92M
  const char* Name() const override { return "DocKeyV3Filter"; }
868
869
  const KeyTransformer* GetKeyTransformer() const override;
870
};
871
872
}  // namespace docdb
873
}  // namespace yb
874
875
#endif  // YB_DOCDB_DOC_KEY_H_