/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 |