YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/docdb/subdocument.cc
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
#include "yb/docdb/subdocument.h"
15
16
#include <map>
17
#include <vector>
18
19
#include "yb/common/ql_type.h"
20
#include "yb/common/ql_value.h"
21
22
#include "yb/docdb/value_type.h"
23
24
#include "yb/gutil/casts.h"
25
26
#include "yb/util/status.h"
27
28
using std::endl;
29
using std::make_pair;
30
using std::map;
31
using std::ostringstream;
32
using std::shared_ptr;
33
using std::string;
34
using std::vector;
35
36
using yb::bfql::TSOpcode;
37
38
namespace yb {
39
namespace docdb {
40
41
339M
SubDocument::SubDocument(ValueType value_type) : PrimitiveValue(value_type) {
42
339M
  if (IsCollectionType(value_type)) {
43
298M
    EnsureContainerAllocated();
44
298M
  }
45
339M
}
46
47
299M
SubDocument::SubDocument() : SubDocument(ValueType::kObject) {}
Unexecuted instantiation: _ZN2yb5docdb11SubDocumentC2Ev
_ZN2yb5docdb11SubDocumentC1Ev
Line
Count
Source
47
299M
SubDocument::SubDocument() : SubDocument(ValueType::kObject) {}
48
49
0
SubDocument::SubDocument(ListExtendOrder extend_order) : SubDocument(ValueType::kArray) {
50
0
  extend_order_ = extend_order;
51
0
}
Unexecuted instantiation: _ZN2yb5docdb11SubDocumentC2ENS0_15ListExtendOrderE
Unexecuted instantiation: _ZN2yb5docdb11SubDocumentC1ENS0_15ListExtendOrderE
52
53
SubDocument::SubDocument(
54
0
    const std::vector<PrimitiveValue> &elements, ListExtendOrder extend_order) {
55
0
  type_ = ValueType::kArray;
56
0
  extend_order_ = extend_order;
57
0
  complex_data_structure_ = new ArrayContainer();
58
0
  array_container().reserve(elements.size());
59
0
  for (auto& elt : elements) {
60
0
    array_container().emplace_back(elt);
61
0
  }
62
0
}
63
64
65
1.11G
SubDocument::~SubDocument() {
66
1.11G
  switch (type_) {
67
298M
    case ValueType::kObject: FALLTHROUGH_INTENDED;
68
298M
    case ValueType::kRedisList: FALLTHROUGH_INTENDED;
69
298M
    case ValueType::kRedisSortedSet: FALLTHROUGH_INTENDED;
70
298M
    case ValueType::kRedisSet: FALLTHROUGH_INTENDED;
71
298M
    case ValueType::kRedisTS: FALLTHROUGH_INTENDED;
72
298M
    case ValueType::kSSForward: FALLTHROUGH_INTENDED;
73
298M
    case ValueType::kSSReverse:
74
299M
      if (has_valid_container()) {
75
299M
        delete &object_container();
76
299M
      }
77
298M
      break;
78
78
    case ValueType::kArray:
79
78
      if (has_valid_container()) {
80
78
        delete &array_container();
81
78
      }
82
78
      break;
83
823M
    default:
84
823M
      break;
85
1.11G
  }
86
1.11G
}
87
88
63.2k
SubDocument::SubDocument(const SubDocument& other) {
89
63.2k
  if (IsPrimitiveValueType(other.type_) ||
90
31.5k
      other.type_ == ValueType::kInvalid ||
91
31.6k
      other.type_ == ValueType::kTombstone) {
92
31.6k
    new(this) PrimitiveValue(other);
93
31.5k
  } else {
94
31.5k
    type_ = other.type_;
95
31.5k
    ttl_seconds_ = other.ttl_seconds_;
96
31.5k
    write_time_ = other.write_time_;
97
31.5k
    complex_data_structure_ = nullptr;
98
31.5k
    switch (type_) {
99
31.5k
      case ValueType::kObject:
100
31.5k
        if (other.has_valid_object_container()) {
101
31.5k
          complex_data_structure_ = new ObjectContainer(other.object_container());
102
31.5k
        }
103
31.5k
        break;
104
0
      case ValueType::kArray:
105
0
        if (other.has_valid_array_container()) {
106
0
          complex_data_structure_ = new ArrayContainer(other.array_container());
107
0
        }
108
0
        break;
109
0
      default:
110
0
        LOG(FATAL) << "Trying to copy an invalid/unsupported SubDocument type: "
111
0
                   << docdb::ToString(type_);
112
31.5k
    }
113
31.5k
  }
114
63.2k
}
115
116
0
bool SubDocument::operator ==(const SubDocument& other) const {
117
0
  if (type_ != other.type_) {
118
0
    return false;
119
0
  }
120
0
  if (IsPrimitiveValueType(type_)) {
121
0
    return this->PrimitiveValue::operator==(other);
122
0
  }
123
0
  switch (type_) {
124
0
    case ValueType::kObject:
125
0
      if (has_valid_container() != other.has_valid_container()) {
126
0
        return has_valid_container() ? object_container().empty()
127
0
                                     : other.object_container().empty();
128
0
      }
129
0
      if (has_valid_container()) {
130
0
        return object_container() == other.object_container();
131
0
      } else {
132
0
        return true;  // Both container pointers are nullptr.
133
0
      }
134
0
    case ValueType::kArray:
135
0
      if (has_valid_container() != other.has_valid_container()) {
136
0
        return has_valid_container() ? array_container().empty()
137
0
                                     : other.array_container().empty();
138
0
      }
139
0
      if (has_valid_container()) {
140
0
        return array_container() == other.array_container();
141
0
      } else {
142
0
        return true;
143
0
      }
144
0
    default:
145
0
      LOG(FATAL) << "Trying to compare SubDocuments of invalid type: " << docdb::ToString(type_);
146
0
  }
147
  // We'll get here if both container pointers are null.
148
0
  return true;
149
0
}
150
151
16.0k
Status SubDocument::ConvertToCollection(ValueType value_type) {
152
16.0k
  if (!has_valid_object_container()) {
153
0
    return STATUS(InvalidArgument, "Subdocument doesn't have valid object container");
154
0
  }
155
16.0k
  type_ = value_type;
156
16.0k
  return Status::OK();
157
16.0k
}
158
159
548M
void SubDocument::MoveFrom(SubDocument* other) {
160
548M
  if (this == other) {
161
0
    return;
162
0
  }
163
164
548M
  if (IsPrimitiveValueType(other->type_)) {
165
432M
    new(this) PrimitiveValue(std::move(*other));
166
115M
  } else {
167
    // For objects/arrays the internal state is just a type and a pointer.
168
115M
    extend_order_ = other->extend_order_;
169
115M
    type_ = other->type_;
170
115M
    ttl_seconds_ = other->ttl_seconds_;
171
115M
    write_time_ = other->write_time_;
172
115M
    complex_data_structure_ = other->complex_data_structure_;
173
    // The internal state of the other subdocument is now owned by this one.
174
115M
#ifndef NDEBUG
175
    // Another layer of protection against trying to use the old state in debug mode.
176
115M
    memset(static_cast<void*>(other), 0xab, sizeof(SubDocument));  // Fill with a random value.
177
115M
#endif
178
115M
    other->type_ = ValueType::kNullLow;  // To avoid deallocation of the old object's memory.
179
115M
  }
180
548M
}
181
182
5.53k
Status SubDocument::ConvertToRedisTS() {
183
5.53k
  return ConvertToCollection(ValueType::kRedisTS);
184
5.53k
}
185
186
0
Status SubDocument::ConvertToRedisSet() {
187
0
  return ConvertToCollection(ValueType::kRedisSet);
188
0
}
189
190
10.5k
Status SubDocument::ConvertToRedisSortedSet() {
191
10.5k
  return ConvertToCollection(ValueType::kRedisSortedSet);
192
10.5k
}
193
194
0
Status SubDocument::ConvertToRedisList() {
195
0
  return ConvertToCollection(ValueType::kRedisList);
196
0
}
197
198
943k
Status SubDocument::NumChildren(size_t *num_children) {
199
943k
  if (!has_valid_object_container()) {
200
0
    return STATUS(IllegalState, "Not a valid object container");
201
0
  }
202
943k
  *num_children = object_container().size();
203
943k
  return Status::OK();
204
943k
}
205
206
203M
SubDocument* SubDocument::GetChild(const PrimitiveValue& key) {
207
203M
  if (!has_valid_object_container()) {
208
0
    return nullptr;
209
0
  }
210
203M
  auto& obj_container = object_container();
211
203M
  auto iter = obj_container.find(key);
212
203M
  if (iter == obj_container.end()) {
213
0
    return nullptr;
214
203M
  } else {
215
203M
    return &iter->second;
216
203M
  }
217
203M
}
218
219
0
const SubDocument* SubDocument::GetChild(const PrimitiveValue& key) const {
220
0
  if (!has_valid_object_container()) {
221
0
    return nullptr;
222
0
  }
223
0
  const auto& obj_container = object_container();
224
0
  auto iter = obj_container.find(key);
225
0
  if (iter == obj_container.end()) {
226
0
    return nullptr;
227
0
  } else {
228
0
    return &iter->second;
229
0
  }
230
0
}
231
232
12.1k
std::pair<SubDocument*, bool> SubDocument::GetOrAddChild(const PrimitiveValue& key) {
233
12.1k
  DCHECK(IsObjectType(type_));
234
12.1k
  EnsureContainerAllocated();
235
12.1k
  auto& obj_container = object_container();
236
12.1k
  auto iter = obj_container.find(key);
237
12.1k
  if (iter == obj_container.end()) {
238
12.1k
    auto ret = obj_container.insert(make_pair(key, SubDocument()));
239
12.1k
    CHECK(ret.second);
240
12.1k
    return make_pair(&ret.first->second, true);  // New subdocument created.
241
18.4E
  } else {
242
18.4E
    return make_pair(&iter->second, false);  // No new subdocument created.
243
18.4E
  }
244
12.1k
}
245
246
133
void SubDocument::AddListElement(SubDocument&& value) {
247
133
  DCHECK_EQ(ValueType::kArray, type_);
248
133
  EnsureContainerAllocated();
249
133
  array_container().emplace_back(std::move(value));
250
133
}
251
252
256M
void SubDocument::SetChild(const PrimitiveValue& key, SubDocument&& value) {
253
256M
  EnsureObjectAllocated();
254
256M
  auto& obj_container = object_container();
255
256M
  auto existing_element = obj_container.find(key);
256
258M
  if (existing_element == obj_container.end()) {
257
258M
    const bool inserted_value = obj_container.emplace(key, std::move(value)).second;
258
258M
    CHECK(inserted_value);
259
18.4E
  } else {
260
18.4E
    existing_element->second = std::move(value);
261
18.4E
  }
262
256M
}
263
264
0
bool SubDocument::DeleteChild(const PrimitiveValue& key) {
265
0
  CHECK_EQ(ValueType::kObject, type_);
266
0
  if (!has_valid_object_container())
267
0
    return false;
268
0
  return object_container().erase(key) > 0;
269
0
}
270
271
0
string SubDocument::ToString() const {
272
0
  ostringstream ss;
273
0
  ss << *this;
274
0
  return ss.str();
275
0
}
276
277
0
ostream& operator <<(ostream& out, const SubDocument& subdoc) {
278
0
  SubDocumentToStreamInternal(out, subdoc, 0);
279
0
  return out;
280
0
}
281
282
void SubDocumentToStreamInternal(ostream& out,
283
                                 const SubDocument& subdoc,
284
0
                                 const int indent) {
285
0
  if (subdoc.IsPrimitive() ||
286
0
      subdoc.value_type() == ValueType::kInvalid ||
287
0
      subdoc.value_type() == ValueType::kTombstone) {
288
0
    out << static_cast<const PrimitiveValue*>(&subdoc)->ToString();
289
0
    return;
290
0
  }
291
0
  switch (subdoc.value_type()) {
292
0
    case ValueType::kRedisSortedSet: FALLTHROUGH_INTENDED;
293
0
    case ValueType::kObject: {
294
0
      out << "{";
295
0
      if (subdoc.container_allocated()) {
296
0
        bool first_pair = true;
297
0
        for (const auto& key_value : subdoc.object_container()) {
298
0
          if (!first_pair) {
299
0
            out << ",";
300
0
          }
301
0
          first_pair = false;
302
0
          out << "\n" << string(indent + 2, ' ') << key_value.first.ToString() << ": ";
303
0
          SubDocumentToStreamInternal(out, key_value.second, indent + 2);
304
0
        }
305
0
        if (!first_pair) {
306
0
          out << "\n" << string(indent, ' ');
307
0
        }
308
0
      }
309
0
      out << "}";
310
0
      break;
311
0
    }
312
0
    case ValueType::kArray: {
313
0
      out << "[";
314
0
      if (subdoc.container_allocated()) {
315
0
        out << (subdoc.GetExtendOrder() == ListExtendOrder::APPEND ? "APPEND" : "PREPEND") << "\n";
316
0
        const auto& list = subdoc.array_container();
317
0
        size_t i = 0;
318
0
        for (; i < list.size(); i++) {
319
0
          if (i != 0) {
320
0
            out << ",";
321
0
          }
322
0
          out << "\n" << string(indent + 2, ' ') << i << ": ";
323
0
          SubDocumentToStreamInternal(out, list[i], indent + 2);
324
0
        }
325
0
        if (i > 0) {
326
0
          out << "\n" << string(indent, ' ');
327
0
        }
328
0
      }
329
0
      out << "]";
330
0
      break;
331
0
    }
332
0
    case ValueType::kRedisSet: {
333
0
      SubDocCollectionToStreamInternal(out, subdoc, indent, "(", ")");
334
0
      break;
335
0
    }
336
0
    case ValueType::kRedisList: {
337
0
      SubDocCollectionToStreamInternal(out, subdoc, indent, "[", "]");
338
0
      break;
339
0
    }
340
0
    case ValueType::kRedisTS: {
341
0
      SubDocCollectionToStreamInternal(out, subdoc, indent, "<", ">");
342
0
      break;
343
0
    }
344
0
    default:
345
0
      LOG(FATAL) << "Invalid subdocument type: " << ToString(subdoc.value_type());
346
0
  }
347
0
}
348
349
void SubDocCollectionToStreamInternal(ostream& out,
350
                                      const SubDocument& subdoc,
351
                                      const int indent,
352
                                      const string& begin_delim,
353
0
                                      const string& end_delim) {
354
0
  out << begin_delim;
355
0
  if (subdoc.container_allocated()) {
356
0
    const auto& keys = subdoc.object_container();
357
0
    for (auto iter = keys.begin(); iter != keys.end(); iter++) {
358
0
      if (iter != keys.begin()) {
359
0
        out << ",";
360
0
      }
361
0
      out << "\n" << string(indent + 2, ' ') << (*iter).first.ToString();
362
0
    }
363
0
    if (!keys.empty()) {
364
0
      out << "\n" << string(indent, ' ');
365
0
    }
366
0
  }
367
0
  out << end_delim;
368
0
}
369
370
256M
void SubDocument::EnsureObjectAllocated() {
371
256M
  type_ = ValueType::kObject;
372
256M
  EnsureContainerAllocated();
373
256M
}
374
375
554M
void SubDocument::EnsureContainerAllocated() {
376
554M
  if (complex_data_structure_ == nullptr) {
377
298M
    if (IsObjectType(type_)) {
378
298M
      complex_data_structure_ = new ObjectContainer();
379
92.7k
    } else if (type_ == ValueType::kArray) {
380
78
      complex_data_structure_ = new ArrayContainer();
381
78
    }
382
298M
  }
383
554M
}
384
385
SubDocument SubDocument::FromQLValuePB(const QLValuePB& value,
386
                                       SortingType sorting_type,
387
15.2M
                                       TSOpcode write_instr) {
388
15.2M
  switch (value.value_case()) {
389
126
    case QLValuePB::kMapValue: {
390
126
      QLMapValuePB map = value.map_value();
391
      // this equality should be ensured by checks before getting here
392
126
      DCHECK_EQ(map.keys_size(), map.values_size());
393
394
126
      SubDocument map_doc;
395
348
      for (int i = 0; i < map.keys_size(); i++) {
396
222
        PrimitiveValue pv_key = PrimitiveValue::FromQLValuePB(map.keys(i), sorting_type);
397
222
        SubDocument pv_val = SubDocument::FromQLValuePB(map.values(i), sorting_type, write_instr);
398
222
        map_doc.SetChild(pv_key, std::move(pv_val));
399
222
      }
400
      // ensure container allocated even if map is empty
401
126
      map_doc.EnsureContainerAllocated();
402
126
      return map_doc;
403
0
    }
404
76
    case QLValuePB::kSetValue: {
405
76
      QLSeqValuePB set = value.set_value();
406
76
      SubDocument set_doc;
407
125
      for (auto& elem : set.elems()) {
408
125
        PrimitiveValue pv_key = PrimitiveValue::FromQLValuePB(elem, sorting_type);
409
125
        if (write_instr == TSOpcode::kSetRemove || write_instr == TSOpcode::kMapRemove ) {
410
          // representing sets elems as keys pointing to tombstones to remove those entries
411
13
          set_doc.SetChildPrimitive(pv_key, PrimitiveValue::kTombstone);
412
112
        }  else {
413
          // representing sets elems as keys pointing to empty (null) values
414
112
          set_doc.SetChildPrimitive(pv_key, PrimitiveValue());
415
112
        }
416
125
      }
417
      // ensure container allocated even if set is empty
418
76
      set_doc.EnsureContainerAllocated();
419
76
      return set_doc;
420
0
    }
421
78
    case QLValuePB::kListValue: {
422
78
      QLSeqValuePB list = value.list_value();
423
78
      SubDocument list_doc(ValueType::kArray);
424
      // ensure container allocated even if list is empty
425
78
      list_doc.EnsureContainerAllocated();
426
211
      for (int i = 0; i < list.elems_size(); i++) {
427
133
        SubDocument pv_val = SubDocument::FromQLValuePB(list.elems(i), sorting_type, write_instr);
428
133
        list_doc.AddListElement(std::move(pv_val));
429
133
      }
430
78
      return list_doc;
431
0
    }
432
433
15.2M
    default:
434
15.2M
      return SubDocument(PrimitiveValue::FromQLValuePB(value, sorting_type));
435
15.2M
  }
436
15.2M
}
437
438
void SubDocument::ToQLValuePB(const SubDocument& doc,
439
                              const shared_ptr<QLType>& ql_type,
440
203M
                              QLValuePB* ql_value) {
441
  // interpreting empty collections as null values following Cassandra semantics
442
203M
  if (ql_type->HasComplexValues() && (!doc.has_valid_object_container() ||
443
722
                                       doc.object_num_keys() == 0)) {
444
370
    SetNull(ql_value);
445
370
    return;
446
370
  }
447
448
203M
  switch (ql_type->main()) {
449
303
    case MAP: {
450
303
      const shared_ptr<QLType>& keys_type = ql_type->params()[0];
451
303
      const shared_ptr<QLType>& values_type = ql_type->params()[1];
452
303
      QLMapValuePB *value_pb = ql_value->mutable_map_value();
453
303
      value_pb->clear_keys();
454
303
      value_pb->clear_values();
455
595
      for (auto &pair : doc.object_container()) {
456
595
        QLValuePB *key = value_pb->add_keys();
457
595
        PrimitiveValue::ToQLValuePB(pair.first, keys_type, key);
458
595
        QLValuePB *value = value_pb->add_values();
459
595
        SubDocument::ToQLValuePB(pair.second, values_type, value);
460
595
      }
461
303
      return;
462
0
    }
463
132
    case SET: {
464
132
      const shared_ptr<QLType>& elems_type = ql_type->params()[0];
465
132
      QLSeqValuePB *value_pb = ql_value->mutable_set_value();
466
132
      value_pb->clear_elems();
467
272
      for (auto &pair : doc.object_container()) {
468
272
        QLValuePB *elem = value_pb->add_elems();
469
272
        PrimitiveValue::ToQLValuePB(pair.first, elems_type, elem);
470
        // set elems are represented as subdocument keys so we ignore the (empty) values
471
272
      }
472
132
      return;
473
0
    }
474
158
    case LIST: {
475
158
      const shared_ptr<QLType>& elems_type = ql_type->params()[0];
476
158
      QLSeqValuePB *value_pb = ql_value->mutable_list_value();
477
158
      value_pb->clear_elems();
478
346
      for (auto &pair : doc.object_container()) {
479
        // list elems are represented as subdocument values with keys only used for ordering
480
346
        QLValuePB *elem = value_pb->add_elems();
481
346
        SubDocument::ToQLValuePB(pair.second, elems_type, elem);
482
346
      }
483
158
      return;
484
0
    }
485
51
    case USER_DEFINED_TYPE: {
486
51
      const shared_ptr<QLType>& keys_type = QLType::Create(INT16);
487
51
      QLMapValuePB *value_pb = ql_value->mutable_map_value();
488
51
      value_pb->clear_keys();
489
51
      value_pb->clear_values();
490
100
      for (auto &pair : doc.object_container()) {
491
100
        QLValuePB *key = value_pb->add_keys();
492
100
        PrimitiveValue::ToQLValuePB(pair.first, keys_type, key);
493
100
        QLValuePB *value = value_pb->add_values();
494
100
        SubDocument::ToQLValuePB(pair.second, ql_type->param_type(key->int16_value()), value);
495
100
      }
496
51
      return;
497
0
    }
498
0
    case TUPLE:
499
0
      break;
500
501
203M
    default: {
502
203M
      return PrimitiveValue::ToQLValuePB(doc, ql_type, ql_value);
503
0
    }
504
0
  }
505
0
  LOG(FATAL) << "Unsupported datatype in SubDocument: " << ql_type->ToString();
506
0
}
507
508
32.2k
int SubDocument::object_num_keys() const {
509
32.2k
  DCHECK(IsObjectType(type_));
510
32.2k
  if (!has_valid_object_container()) {
511
0
    return 0;
512
0
  }
513
32.2k
  return narrow_cast<int>(object_container().size());
514
32.2k
}
515
516
0
bool SubDocument::container_allocated() const {
517
0
  CHECK(IsCollectionType(type_));
518
0
  return complex_data_structure_ != nullptr;
519
0
}
520
521
960M
bool SubDocument::has_valid_object_container() const {
522
960M
  return (IsObjectType(type_)) && has_valid_container();
523
960M
}
524
525
289
bool SubDocument::has_valid_array_container() const {
526
289
  return type_ == ValueType::kArray && has_valid_container();
527
289
}
528
529
}  // namespace docdb
530
}  // namespace yb