YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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
943M
SubDocument::SubDocument(ValueType value_type) : PrimitiveValue(value_type) {
42
943M
  if (IsCollectionType(value_type)) {
43
830M
    EnsureContainerAllocated();
44
830M
  }
45
943M
}
46
47
836M
SubDocument::SubDocument() : SubDocument(ValueType::kObject) {}
Unexecuted instantiation: yb::docdb::SubDocument::SubDocument()
yb::docdb::SubDocument::SubDocument()
Line
Count
Source
47
836M
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: yb::docdb::SubDocument::SubDocument(yb::docdb::ListExtendOrder)
Unexecuted instantiation: yb::docdb::SubDocument::SubDocument(yb::docdb::ListExtendOrder)
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
3.09G
SubDocument::~SubDocument() {
66
3.09G
  switch (type_) {
67
830M
    case ValueType::kObject: FALLTHROUGH_INTENDED;
68
830M
    case ValueType::kRedisList: FALLTHROUGH_INTENDED;
69
830M
    case ValueType::kRedisSortedSet: FALLTHROUGH_INTENDED;
70
830M
    case ValueType::kRedisSet: FALLTHROUGH_INTENDED;
71
830M
    case ValueType::kRedisTS: FALLTHROUGH_INTENDED;
72
830M
    case ValueType::kSSForward: FALLTHROUGH_INTENDED;
73
830M
    case ValueType::kSSReverse:
74
833M
      if (
has_valid_container()830M
) {
75
833M
        delete &object_container();
76
833M
      }
77
830M
      break;
78
2
    case ValueType::kArray:
79
2
      if (has_valid_container()) {
80
2
        delete &array_container();
81
2
      }
82
2
      break;
83
2.27G
    default:
84
2.27G
      break;
85
3.09G
  }
86
3.09G
}
87
88
492k
SubDocument::SubDocument(const SubDocument& other) {
89
492k
  if (IsPrimitiveValueType(other.type_) ||
90
492k
      
other.type_ == ValueType::kInvalid199k
||
91
492k
      
other.type_ == ValueType::kTombstone199k
) {
92
292k
    new(this) PrimitiveValue(other);
93
292k
  } else {
94
199k
    type_ = other.type_;
95
199k
    ttl_seconds_ = other.ttl_seconds_;
96
199k
    write_time_ = other.write_time_;
97
199k
    complex_data_structure_ = nullptr;
98
199k
    switch (type_) {
99
199k
      case ValueType::kObject:
100
199k
        if (other.has_valid_object_container()) {
101
199k
          complex_data_structure_ = new ObjectContainer(other.object_container());
102
199k
        }
103
199k
        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
199k
    }
113
199k
  }
114
492k
}
115
116
6.02M
bool SubDocument::operator ==(const SubDocument& other) const {
117
6.02M
  if (type_ != other.type_) {
118
0
    return false;
119
0
  }
120
6.02M
  if (IsPrimitiveValueType(type_)) {
121
2.18M
    return this->PrimitiveValue::operator==(other);
122
2.18M
  }
123
3.84M
  switch (type_) {
124
3.84M
    case ValueType::kObject:
125
3.84M
      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
3.84M
      if (has_valid_container()) {
130
3.84M
        return object_container() == other.object_container();
131
3.84M
      } 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
3.84M
  }
147
  // We'll get here if both container pointers are null.
148
0
  return true;
149
3.84M
}
150
151
10.9k
Status SubDocument::ConvertToCollection(ValueType value_type) {
152
10.9k
  if (!has_valid_object_container()) {
153
0
    return STATUS(InvalidArgument, "Subdocument doesn't have valid object container");
154
0
  }
155
10.9k
  type_ = value_type;
156
10.9k
  return Status::OK();
157
10.9k
}
158
159
1.54G
void SubDocument::MoveFrom(SubDocument* other) {
160
1.54G
  if (this == other) {
161
0
    return;
162
0
  }
163
164
1.54G
  if (IsPrimitiveValueType(other->type_)) {
165
1.22G
    new(this) PrimitiveValue(std::move(*other));
166
1.22G
  } else {
167
    // For objects/arrays the internal state is just a type and a pointer.
168
312M
    extend_order_ = other->extend_order_;
169
312M
    type_ = other->type_;
170
312M
    ttl_seconds_ = other->ttl_seconds_;
171
312M
    write_time_ = other->write_time_;
172
312M
    complex_data_structure_ = other->complex_data_structure_;
173
    // The internal state of the other subdocument is now owned by this one.
174
312M
#ifndef NDEBUG
175
    // Another layer of protection against trying to use the old state in debug mode.
176
312M
    memset(static_cast<void*>(other), 0xab, sizeof(SubDocument));  // Fill with a random value.
177
312M
#endif
178
312M
    other->type_ = ValueType::kNullLow;  // To avoid deallocation of the old object's memory.
179
312M
  }
180
1.54G
}
181
182
10.8k
Status SubDocument::ConvertToRedisTS() {
183
10.8k
  return ConvertToCollection(ValueType::kRedisTS);
184
10.8k
}
185
186
6
Status SubDocument::ConvertToRedisSet() {
187
6
  return ConvertToCollection(ValueType::kRedisSet);
188
6
}
189
190
12
Status SubDocument::ConvertToRedisSortedSet() {
191
12
  return ConvertToCollection(ValueType::kRedisSortedSet);
192
12
}
193
194
0
Status SubDocument::ConvertToRedisList() {
195
0
  return ConvertToCollection(ValueType::kRedisList);
196
0
}
197
198
1.83M
Status SubDocument::NumChildren(size_t *num_children) {
199
1.83M
  if (!has_valid_object_container()) {
200
0
    return STATUS(IllegalState, "Not a valid object container");
201
0
  }
202
1.83M
  *num_children = object_container().size();
203
1.83M
  return Status::OK();
204
1.83M
}
205
206
600M
SubDocument* SubDocument::GetChild(const PrimitiveValue& key) {
207
600M
  if (!has_valid_object_container()) {
208
0
    return nullptr;
209
0
  }
210
600M
  auto& obj_container = object_container();
211
600M
  auto iter = obj_container.find(key);
212
600M
  if (iter == obj_container.end()) {
213
1.10k
    return nullptr;
214
600M
  } else {
215
600M
    return &iter->second;
216
600M
  }
217
600M
}
218
219
1.28M
const SubDocument* SubDocument::GetChild(const PrimitiveValue& key) const {
220
1.28M
  if (!has_valid_object_container()) {
221
0
    return nullptr;
222
0
  }
223
1.28M
  const auto& obj_container = object_container();
224
1.28M
  auto iter = obj_container.find(key);
225
1.28M
  if (iter == obj_container.end()) {
226
187k
    return nullptr;
227
1.09M
  } else {
228
1.09M
    return &iter->second;
229
1.09M
  }
230
1.28M
}
231
232
6.55M
std::pair<SubDocument*, bool> SubDocument::GetOrAddChild(const PrimitiveValue& key) {
233
6.55M
  DCHECK(IsObjectType(type_));
234
6.55M
  EnsureContainerAllocated();
235
6.55M
  auto& obj_container = object_container();
236
6.55M
  auto iter = obj_container.find(key);
237
6.55M
  if (iter == obj_container.end()) {
238
6.34M
    auto ret = obj_container.insert(make_pair(key, SubDocument()));
239
6.34M
    CHECK(ret.second);
240
6.34M
    return make_pair(&ret.first->second, true);  // New subdocument created.
241
6.34M
  } else {
242
211k
    return make_pair(&iter->second, false);  // No new subdocument created.
243
211k
  }
244
6.55M
}
245
246
0
void SubDocument::AddListElement(SubDocument&& value) {
247
0
  DCHECK_EQ(ValueType::kArray, type_);
248
0
  EnsureContainerAllocated();
249
0
  array_container().emplace_back(std::move(value));
250
0
}
251
252
719M
void SubDocument::SetChild(const PrimitiveValue& key, SubDocument&& value) {
253
719M
  EnsureObjectAllocated();
254
719M
  auto& obj_container = object_container();
255
719M
  auto existing_element = obj_container.find(key);
256
724M
  if (
existing_element == obj_container.end()719M
) {
257
724M
    const bool inserted_value = obj_container.emplace(key, std::move(value)).second;
258
724M
    CHECK(inserted_value);
259
18.4E
  } else {
260
18.4E
    existing_element->second = std::move(value);
261
18.4E
  }
262
719M
}
263
264
21.6k
bool SubDocument::DeleteChild(const PrimitiveValue& key) {
265
21.6k
  CHECK_EQ(ValueType::kObject, type_);
266
21.6k
  if (!has_valid_object_container())
267
0
    return false;
268
21.6k
  return object_container().erase(key) > 0;
269
21.6k
}
270
271
801k
string SubDocument::ToString() const {
272
801k
  ostringstream ss;
273
801k
  ss << *this;
274
801k
  return ss.str();
275
801k
}
276
277
801k
ostream& operator <<(ostream& out, const SubDocument& subdoc) {
278
801k
  SubDocumentToStreamInternal(out, subdoc, 0);
279
801k
  return out;
280
801k
}
281
282
void SubDocumentToStreamInternal(ostream& out,
283
                                 const SubDocument& subdoc,
284
12.0M
                                 const int indent) {
285
12.0M
  if (subdoc.IsPrimitive() ||
286
12.0M
      
subdoc.value_type() == ValueType::kInvalid7.69M
||
287
12.0M
      
subdoc.value_type() == ValueType::kTombstone7.69M
) {
288
4.37M
    out << static_cast<const PrimitiveValue*>(&subdoc)->ToString();
289
4.37M
    return;
290
4.37M
  }
291
7.69M
  switch (subdoc.value_type()) {
292
0
    case ValueType::kRedisSortedSet: FALLTHROUGH_INTENDED;
293
7.69M
    case ValueType::kObject: {
294
7.69M
      out << "{";
295
7.69M
      if (subdoc.container_allocated()) {
296
7.69M
        bool first_pair = true;
297
11.2M
        for (const auto& key_value : subdoc.object_container()) {
298
11.2M
          if (!first_pair) {
299
3.57M
            out << ",";
300
3.57M
          }
301
11.2M
          first_pair = false;
302
11.2M
          out << "\n" << string(indent + 2, ' ') << key_value.first.ToString() << ": ";
303
11.2M
          SubDocumentToStreamInternal(out, key_value.second, indent + 2);
304
11.2M
        }
305
7.69M
        if (!first_pair) {
306
7.69M
          out << "\n" << string(indent, ' ');
307
7.69M
        }
308
7.69M
      }
309
7.69M
      out << "}";
310
7.69M
      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
7.69M
  }
347
7.69M
}
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
719M
void SubDocument::EnsureObjectAllocated() {
371
719M
  type_ = ValueType::kObject;
372
719M
  EnsureContainerAllocated();
373
719M
}
374
375
1.55G
void SubDocument::EnsureContainerAllocated() {
376
1.55G
  if (complex_data_structure_ == nullptr) {
377
830M
    if (
IsObjectType(type_)830M
) {
378
830M
      complex_data_structure_ = new ObjectContainer();
379
18.4E
    } else if (type_ == ValueType::kArray) {
380
2
      complex_data_structure_ = new ArrayContainer();
381
2
    }
382
830M
  }
383
1.55G
}
384
385
SubDocument SubDocument::FromQLValuePB(const QLValuePB& value,
386
                                       SortingType sorting_type,
387
798
                                       TSOpcode write_instr) {
388
798
  switch (value.value_case()) {
389
336
    case QLValuePB::kMapValue: {
390
336
      QLMapValuePB map = value.map_value();
391
      // this equality should be ensured by checks before getting here
392
336
      DCHECK_EQ(map.keys_size(), map.values_size());
393
394
336
      SubDocument map_doc;
395
1.00k
      for (int i = 0; i < map.keys_size(); 
i++672
) {
396
672
        PrimitiveValue pv_key = PrimitiveValue::FromQLValuePB(map.keys(i), sorting_type);
397
672
        SubDocument pv_val = SubDocument::FromQLValuePB(map.values(i), sorting_type, write_instr);
398
672
        map_doc.SetChild(pv_key, std::move(pv_val));
399
672
      }
400
      // ensure container allocated even if map is empty
401
336
      map_doc.EnsureContainerAllocated();
402
336
      return map_doc;
403
0
    }
404
0
    case QLValuePB::kSetValue: {
405
0
      QLSeqValuePB set = value.set_value();
406
0
      SubDocument set_doc;
407
0
      for (auto& elem : set.elems()) {
408
0
        PrimitiveValue pv_key = PrimitiveValue::FromQLValuePB(elem, sorting_type);
409
0
        if (write_instr == TSOpcode::kSetRemove || write_instr == TSOpcode::kMapRemove) {
410
          // representing sets elems as keys pointing to tombstones to remove those entries
411
0
          set_doc.SetChildPrimitive(pv_key, PrimitiveValue::kTombstone);
412
0
        }  else {
413
          // representing sets elems as keys pointing to empty (null) values
414
0
          set_doc.SetChildPrimitive(pv_key, PrimitiveValue());
415
0
        }
416
0
      }
417
      // ensure container allocated even if set is empty
418
0
      set_doc.EnsureContainerAllocated();
419
0
      return set_doc;
420
0
    }
421
0
    case QLValuePB::kListValue: {
422
0
      QLSeqValuePB list = value.list_value();
423
0
      SubDocument list_doc(ValueType::kArray);
424
      // ensure container allocated even if list is empty
425
0
      list_doc.EnsureContainerAllocated();
426
0
      for (int i = 0; i < list.elems_size(); i++) {
427
0
        SubDocument pv_val = SubDocument::FromQLValuePB(list.elems(i), sorting_type, write_instr);
428
0
        list_doc.AddListElement(std::move(pv_val));
429
0
      }
430
0
      return list_doc;
431
0
    }
432
433
462
    default:
434
462
      return SubDocument(PrimitiveValue::FromQLValuePB(value, sorting_type));
435
798
  }
436
798
}
437
438
void SubDocument::ToQLValuePB(const SubDocument& doc,
439
                              const shared_ptr<QLType>& ql_type,
440
600M
                              QLValuePB* ql_value) {
441
  // interpreting empty collections as null values following Cassandra semantics
442
600M
  if (ql_type->HasComplexValues() && 
(1.01k
!doc.has_valid_object_container()1.01k
||
443
1.01k
                                       
doc.object_num_keys() == 0723
)) {
444
370
    SetNull(ql_value);
445
370
    return;
446
370
  }
447
448
600M
  switch (ql_type->main()) {
449
304
    case MAP: {
450
304
      const shared_ptr<QLType>& keys_type = ql_type->params()[0];
451
304
      const shared_ptr<QLType>& values_type = ql_type->params()[1];
452
304
      QLMapValuePB *value_pb = ql_value->mutable_map_value();
453
304
      value_pb->clear_keys();
454
304
      value_pb->clear_values();
455
594
      for (auto &pair : doc.object_container()) {
456
594
        QLValuePB *key = value_pb->add_keys();
457
594
        PrimitiveValue::ToQLValuePB(pair.first, keys_type, key);
458
594
        QLValuePB *value = value_pb->add_values();
459
594
        SubDocument::ToQLValuePB(pair.second, values_type, value);
460
594
      }
461
304
      return;
462
0
    }
463
133
    case SET: {
464
133
      const shared_ptr<QLType>& elems_type = ql_type->params()[0];
465
133
      QLSeqValuePB *value_pb = ql_value->mutable_set_value();
466
133
      value_pb->clear_elems();
467
274
      for (auto &pair : doc.object_container()) {
468
274
        QLValuePB *elem = value_pb->add_elems();
469
274
        PrimitiveValue::ToQLValuePB(pair.first, elems_type, elem);
470
        // set elems are represented as subdocument keys so we ignore the (empty) values
471
274
      }
472
133
      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
600M
    default: {
502
600M
      return PrimitiveValue::ToQLValuePB(doc, ql_type, ql_value);
503
0
    }
504
600M
  }
505
0
  LOG(FATAL) << "Unsupported datatype in SubDocument: " << ql_type->ToString();
506
0
}
507
508
11.0k
int SubDocument::object_num_keys() const {
509
11.0k
  DCHECK(IsObjectType(type_));
510
11.0k
  if (!has_valid_object_container()) {
511
0
    return 0;
512
0
  }
513
11.0k
  return narrow_cast<int>(object_container().size());
514
11.0k
}
515
516
7.69M
bool SubDocument::container_allocated() const {
517
7.69M
  CHECK(IsCollectionType(type_));
518
7.69M
  return complex_data_structure_ != nullptr;
519
7.69M
}
520
521
2.76G
bool SubDocument::has_valid_object_container() const {
522
2.76G
  return 
(IsObjectType(type_))2.76G
&& has_valid_container();
523
2.76G
}
524
525
2
bool SubDocument::has_valid_array_container() const {
526
2
  return type_ == ValueType::kArray && has_valid_container();
527
2
}
528
529
}  // namespace docdb
530
}  // namespace yb