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