/Users/deen/code/yugabyte-db/src/yb/common/jsonb.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 | | #include "yb/common/jsonb.h" |
14 | | |
15 | | #include <rapidjson/error/en.h> |
16 | | |
17 | | #include "yb/common/common.pb.h" |
18 | | #include "yb/common/json_util.h" |
19 | | #include "yb/common/ql_value.h" |
20 | | |
21 | | #include "yb/gutil/casts.h" |
22 | | |
23 | | #include "yb/util/kv_util.h" |
24 | | #include "yb/util/result.h" |
25 | | #include "yb/util/status_format.h" |
26 | | #include "yb/util/varint.h" |
27 | | |
28 | | namespace yb { |
29 | | namespace common { |
30 | | |
31 | 43.2k | bool Jsonb::IsScalar(const JEntry& jentry) { |
32 | 43.2k | uint32_t jentry_type = GetJEType(jentry); |
33 | 43.2k | return ((jentry_type) != kJEIsArray && (jentry_type) != kJEIsObject); |
34 | 43.2k | } |
35 | | |
36 | 1.36M | size_t Jsonb::ComputeDataOffset(const size_t num_entries, const uint32_t container_type) { |
37 | 1.29M | const size_t num_jentries = (container_type & kJBArray) ? num_entries : 2 * num_entries; |
38 | 1.36M | return sizeof(JsonbHeader) + num_jentries * sizeof(JEntry); |
39 | 1.36M | } |
40 | | |
41 | 1.32M | Jsonb::Jsonb() { |
42 | 1.32M | } |
43 | | |
44 | | Jsonb::Jsonb(const std::string& jsonb) |
45 | 511 | : serialized_jsonb_(jsonb) { |
46 | 511 | } |
47 | | |
48 | | Jsonb::Jsonb(std::string&& jsonb) |
49 | 0 | : serialized_jsonb_(std::move(jsonb)) { |
50 | 0 | } |
51 | | |
52 | 17.8k | void Jsonb::Assign(const std::string& jsonb) { |
53 | 17.8k | serialized_jsonb_ = jsonb; |
54 | 17.8k | } |
55 | | |
56 | 0 | void Jsonb::Assign(std::string&& jsonb) { |
57 | 0 | serialized_jsonb_ = std::move(jsonb); |
58 | 0 | } |
59 | | |
60 | 7.78k | std::string&& Jsonb::MoveSerializedJsonb() { |
61 | 7.78k | return std::move(serialized_jsonb_); |
62 | 7.78k | } |
63 | | |
64 | 84 | const std::string& Jsonb::SerializedJsonb() const { |
65 | 84 | return serialized_jsonb_; |
66 | 84 | } |
67 | | |
68 | 0 | bool Jsonb::operator==(const Jsonb& other) const { |
69 | 0 | return serialized_jsonb_ == other.serialized_jsonb_; |
70 | 0 | } |
71 | | |
72 | 1.30M | Status Jsonb::FromString(const std::string& json) { |
73 | | // Parse the json document. |
74 | 1.30M | rapidjson::Document document; |
75 | 1.30M | document.Parse<0>(json.c_str()); |
76 | 1.30M | if (document.HasParseError()) { |
77 | 4 | return STATUS(Corruption, "JSON text is corrupt", |
78 | 4 | rapidjson::GetParseError_En(document.GetParseError())); |
79 | 4 | } |
80 | 1.30M | return FromRapidJson(document); |
81 | 1.30M | } |
82 | | |
83 | 1.30M | Status Jsonb::FromRapidJson(const rapidjson::Document& document) { |
84 | 1.30M | return ToJsonbInternal(document, &serialized_jsonb_); |
85 | 1.30M | } |
86 | | |
87 | 0 | Status Jsonb::FromRapidJson(const rapidjson::Value& value) { |
88 | 0 | rapidjson::Document document; |
89 | 0 | document.CopyFrom(value, document.GetAllocator()); |
90 | 0 | return FromRapidJson(document); |
91 | 0 | } |
92 | | |
93 | 110 | Status Jsonb::FromQLValuePB(const QLValuePB& value_pb) { |
94 | 110 | rapidjson::Document document; |
95 | 110 | RETURN_NOT_OK(ConvertQLValuePBToRapidJson(value_pb, &document)); |
96 | 110 | return FromRapidJson(document); |
97 | 110 | } |
98 | | |
99 | | std::pair<size_t, size_t> Jsonb::ComputeOffsetsAndJsonbHeader(size_t num_entries, |
100 | | uint32_t container_type, |
101 | 1.30M | std::string* jsonb) { |
102 | | // Compute the size we need to allocate for the metadata. |
103 | 1.30M | size_t metadata_offset = jsonb->size(); |
104 | 1.30M | const size_t jsonb_metadata_size = ComputeDataOffset(num_entries, container_type); |
105 | | |
106 | | // Resize the string to fit the jsonb header and the jentry for keys and values. |
107 | 1.30M | jsonb->resize(metadata_offset + jsonb_metadata_size); |
108 | | |
109 | | // Store the jsonb header at the appropriate place. |
110 | 1.30M | JsonbHeader jsonb_header = GetCount(narrow_cast<JsonbHeader>(num_entries)) | container_type; |
111 | 1.30M | BigEndian::Store32(&((*jsonb)[metadata_offset]), jsonb_header); |
112 | 1.30M | metadata_offset += sizeof(JsonbHeader); |
113 | | |
114 | 1.30M | return std::make_pair(metadata_offset, jsonb_metadata_size); |
115 | 1.30M | } |
116 | | |
117 | | CHECKED_STATUS Jsonb::ToJsonbProcessObject(const rapidjson::Value& document, |
118 | 13.5k | std::string* jsonb) { |
119 | 13.5k | DCHECK(document.IsObject()); |
120 | | |
121 | | // Use a map since we need to store the keys in sorted order. |
122 | 13.5k | std::map<string, const rapidjson::Value&> kv_pairs; |
123 | 20.0k | for (const auto& member : document.GetObject()) { |
124 | 20.0k | kv_pairs.emplace(member.name.GetString(), member.value); |
125 | 20.0k | } |
126 | | |
127 | 13.5k | size_t metadata_offset, jsonb_metadata_size; |
128 | 13.5k | std::tie(metadata_offset, jsonb_metadata_size) = ComputeOffsetsAndJsonbHeader(kv_pairs.size(), |
129 | 13.5k | kJBObject, jsonb); |
130 | | |
131 | | // Now append the keys and store the key offsets in the jentry. |
132 | 13.5k | size_t data_begin_offset = jsonb->size(); |
133 | 20.0k | for (const auto& entry : kv_pairs) { |
134 | 20.0k | jsonb->append(entry.first); |
135 | 20.0k | JEntry key_offset = narrow_cast<JEntry>(jsonb->size() - data_begin_offset); |
136 | 20.0k | JEntry jentry = GetOffset(key_offset) | kJEIsString; // keys are always strings. |
137 | 20.0k | BigEndian::Store32(&((*jsonb)[metadata_offset]), jentry); |
138 | 20.0k | metadata_offset += sizeof(JEntry); |
139 | 20.0k | } |
140 | | |
141 | | // Append the values to the buffer. |
142 | 20.0k | for (const auto& entry : kv_pairs) { |
143 | 20.0k | const rapidjson::Value& value = entry.second; |
144 | 20.0k | RETURN_NOT_OK(ProcessJsonValueAndMetadata(value, data_begin_offset, jsonb, &metadata_offset)); |
145 | 20.0k | } |
146 | | |
147 | | // The metadata slice should now be empty. |
148 | 13.5k | if (data_begin_offset != metadata_offset) { |
149 | 0 | return STATUS(Corruption, "Couldn't process entire data for json object"); |
150 | 0 | } |
151 | 13.5k | return Status::OK(); |
152 | 13.5k | } |
153 | | |
154 | | CHECKED_STATUS Jsonb::ProcessJsonValueAndMetadata(const rapidjson::Value& value, |
155 | | const size_t data_begin_offset, |
156 | | std::string* jsonb, |
157 | 1.31M | size_t* metadata_offset) { |
158 | 1.31M | JEntry jentry = 0; |
159 | 1.31M | switch (value.GetType()) { |
160 | 1.29M | case rapidjson::Type::kNullType: |
161 | 1.29M | jentry |= kJEIsNull; |
162 | 1.29M | break; |
163 | 33 | case rapidjson::Type::kFalseType: |
164 | 33 | jentry |= kJEIsBoolFalse; |
165 | 33 | break; |
166 | 52 | case rapidjson::Type::kTrueType: |
167 | 52 | jentry |= kJEIsBoolTrue; |
168 | 52 | break; |
169 | 38 | case rapidjson::Type::kArrayType: |
170 | 38 | jentry |= kJEIsArray; |
171 | 38 | RETURN_NOT_OK(ToJsonbInternal(value, jsonb)); |
172 | 38 | break; |
173 | 6.15k | case rapidjson::Type::kObjectType: |
174 | 6.15k | jentry |= kJEIsObject; |
175 | 6.15k | RETURN_NOT_OK(ToJsonbInternal(value, jsonb)); |
176 | 6.15k | break; |
177 | 7.66k | case rapidjson::Type::kNumberType: |
178 | 7.66k | if (value.IsInt()) { |
179 | 7.56k | jentry |= kJEIsInt; |
180 | 7.56k | util::AppendInt32ToKey(value.GetInt(), jsonb); |
181 | 101 | } else if (value.IsUint()) { |
182 | 7 | jentry |= kJEIsUInt; |
183 | 7 | util::AppendBigEndianUInt32(value.GetUint(), jsonb); |
184 | 94 | } else if (value.IsInt64()) { |
185 | 26 | jentry |= kJEIsInt64; |
186 | 26 | util::AppendInt64ToKey(value.GetInt64(), jsonb); |
187 | 68 | } else if (value.IsUint64()) { |
188 | 13 | jentry |= kJEIsUInt64; |
189 | 13 | util::AppendBigEndianUInt64(value.GetUint64(), jsonb); |
190 | 55 | } else if (value.IsFloat()) { |
191 | 43 | jentry |= kJEIsFloat; |
192 | 43 | util::AppendFloatToKey(value.GetFloat(), jsonb); |
193 | 12 | } else if (value.IsDouble()) { |
194 | 12 | jentry |= kJEIsDouble; |
195 | 12 | util::AppendDoubleToKey(value.GetDouble(), jsonb); |
196 | 0 | } else { |
197 | 0 | return STATUS(NotSupported, "Numeric type is not supported"); |
198 | 0 | } |
199 | 7.66k | break; |
200 | 6.34k | case rapidjson::Type::kStringType: |
201 | 6.34k | jentry |= kJEIsString; |
202 | 6.34k | jsonb->append(value.GetString()); |
203 | 6.34k | break; |
204 | 1.31M | } |
205 | | |
206 | | // Store the offset. |
207 | 1.31M | auto offset = narrow_cast<JEntry>(jsonb->size() - data_begin_offset); |
208 | 1.31M | jentry |= GetOffset(offset); |
209 | | |
210 | | // Store the JEntry. |
211 | 1.31M | BigEndian::Store32(&((*jsonb)[*metadata_offset]), jentry); |
212 | 1.31M | (*metadata_offset) += sizeof(JEntry); |
213 | 1.31M | return Status::OK(); |
214 | 1.31M | } |
215 | | |
216 | | CHECKED_STATUS Jsonb::ToJsonbProcessArray(const rapidjson::Value& document, |
217 | | const bool is_scalar, |
218 | 1.29M | std::string* jsonb) { |
219 | 1.29M | DCHECK(document.IsArray()); |
220 | | |
221 | 1.29M | const auto& json_array = document.GetArray(); |
222 | 1.29M | const size_t num_array_entries = json_array.Size(); |
223 | | |
224 | 1.29M | uint32_t container_type = kJBArray; |
225 | 1.29M | if (is_scalar) { |
226 | | // Scalars are an array with one element and the special kJBScalar field set in the header. |
227 | 1.29M | DCHECK_EQ(num_array_entries, 1); |
228 | 1.29M | container_type |= kJBScalar; |
229 | 1.29M | } |
230 | | |
231 | 1.29M | size_t metadata_offset, jsonb_metadata_size; |
232 | 1.29M | std::tie(metadata_offset, jsonb_metadata_size) = ComputeOffsetsAndJsonbHeader(num_array_entries, |
233 | 1.29M | container_type, |
234 | 1.29M | jsonb); |
235 | 1.29M | const size_t data_begin_offset = jsonb->size(); |
236 | | // Append the array members to the buffer. |
237 | 1.29M | for (const rapidjson::Value& value : json_array) { |
238 | 1.29M | RETURN_NOT_OK(ProcessJsonValueAndMetadata(value, data_begin_offset, jsonb, &metadata_offset)); |
239 | 1.29M | } |
240 | | |
241 | | // The metadata slice should now be empty. |
242 | 1.29M | if (data_begin_offset != metadata_offset) { |
243 | 0 | return STATUS(Corruption, "Couldn't process entire data for json array"); |
244 | 0 | } |
245 | 1.29M | return Status::OK(); |
246 | 1.29M | } |
247 | | |
248 | 1.31M | CHECKED_STATUS Jsonb::ToJsonbInternal(const rapidjson::Value& document, std::string* jsonb) { |
249 | 1.31M | if (document.IsObject()) { |
250 | 13.5k | return ToJsonbProcessObject(document, jsonb); |
251 | 1.29M | } else if (document.IsArray()) { |
252 | 51 | return ToJsonbProcessArray(document, /* is_scalar */ false, jsonb); |
253 | 1.29M | } else { |
254 | | // Scalar values are stored as an array with one element with a special field in the header |
255 | | // indicating it is a scalar. |
256 | 1.29M | rapidjson::Document json_array; |
257 | 1.29M | json_array.SetArray(); |
258 | | |
259 | 1.29M | rapidjson::Value tmpvalue; |
260 | 1.29M | tmpvalue.CopyFrom(document, json_array.GetAllocator()); |
261 | 1.29M | json_array.PushBack(std::move(tmpvalue), json_array.GetAllocator()); |
262 | 1.29M | return ToJsonbProcessArray(json_array, true, jsonb); |
263 | 1.29M | } |
264 | 1.31M | } |
265 | | |
266 | | namespace { |
267 | | |
268 | 4.10k | rapidjson::Value ValueFromSlice(rapidjson::Document* document, const Slice& value) { |
269 | 4.10k | return rapidjson::Value( |
270 | 4.10k | value.cdata(), narrow_cast<rapidjson::SizeType>(value.size()), document->GetAllocator()); |
271 | 4.10k | } |
272 | | |
273 | | template <typename T> |
274 | 1.23k | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { |
275 | 1.23k | document->AddMember( |
276 | 1.23k | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); |
277 | 1.23k | } jsonb.cc:_ZN2yb6common12_GLOBAL__N_116AddNumericMemberIiEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EERKNS_5SliceET_ Line | Count | Source | 274 | 839 | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { | 275 | 839 | document->AddMember( | 276 | 839 | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); | 277 | 839 | } |
jsonb.cc:_ZN2yb6common12_GLOBAL__N_116AddNumericMemberIjEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EERKNS_5SliceET_ Line | Count | Source | 274 | 46 | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { | 275 | 46 | document->AddMember( | 276 | 46 | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); | 277 | 46 | } |
jsonb.cc:_ZN2yb6common12_GLOBAL__N_116AddNumericMemberIxEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EERKNS_5SliceET_ Line | Count | Source | 274 | 140 | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { | 275 | 140 | document->AddMember( | 276 | 140 | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); | 277 | 140 | } |
jsonb.cc:_ZN2yb6common12_GLOBAL__N_116AddNumericMemberIyEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EERKNS_5SliceET_ Line | Count | Source | 274 | 70 | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { | 275 | 70 | document->AddMember( | 276 | 70 | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); | 277 | 70 | } |
jsonb.cc:_ZN2yb6common12_GLOBAL__N_116AddNumericMemberIdEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EERKNS_5SliceET_ Line | Count | Source | 274 | 65 | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { | 275 | 65 | document->AddMember( | 276 | 65 | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); | 277 | 65 | } |
jsonb.cc:_ZN2yb6common12_GLOBAL__N_116AddNumericMemberIfEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EERKNS_5SliceET_ Line | Count | Source | 274 | 70 | void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) { | 275 | 70 | document->AddMember( | 276 | 70 | ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator()); | 277 | 70 | } |
|
278 | | |
279 | | template <typename T> |
280 | 551 | void PushBackNumericMember(rapidjson::Document* document, T value) { |
281 | 551 | document->PushBack(rapidjson::Value(value), |
282 | 551 | document->GetAllocator()); |
283 | 551 | } jsonb.cc:_ZN2yb6common12_GLOBAL__N_121PushBackNumericMemberIiEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EET_ Line | Count | Source | 280 | 463 | void PushBackNumericMember(rapidjson::Document* document, T value) { | 281 | 463 | document->PushBack(rapidjson::Value(value), | 282 | 463 | document->GetAllocator()); | 283 | 463 | } |
Unexecuted instantiation: jsonb.cc:_ZN2yb6common12_GLOBAL__N_121PushBackNumericMemberIjEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EET_ Unexecuted instantiation: jsonb.cc:_ZN2yb6common12_GLOBAL__N_121PushBackNumericMemberIxEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EET_ Unexecuted instantiation: jsonb.cc:_ZN2yb6common12_GLOBAL__N_121PushBackNumericMemberIyEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EET_ Unexecuted instantiation: jsonb.cc:_ZN2yb6common12_GLOBAL__N_121PushBackNumericMemberIdEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EET_ jsonb.cc:_ZN2yb6common12_GLOBAL__N_121PushBackNumericMemberIfEEvPN9rapidjson15GenericDocumentINS3_4UTF8IcEENS3_19MemoryPoolAllocatorINS3_12CrtAllocatorEEES8_EET_ Line | Count | Source | 280 | 88 | void PushBackNumericMember(rapidjson::Document* document, T value) { | 281 | 88 | document->PushBack(rapidjson::Value(value), | 282 | 88 | document->GetAllocator()); | 283 | 88 | } |
|
284 | | } // anonymous namespace |
285 | | |
286 | | Status Jsonb::ScalarToString(const JEntry& element_metadata, const Slice& json_value, |
287 | 16.3k | string* result) { |
288 | 16.3k | switch (GetJEType(element_metadata)) { |
289 | 9.16k | case kJEIsString: { |
290 | 9.16k | *result = json_value.ToBuffer(); |
291 | 9.16k | break; |
292 | 0 | } |
293 | 6.80k | case kJEIsInt: { |
294 | 6.80k | int32_t value = util::DecodeInt32FromKey(json_value); |
295 | 6.80k | *result = std::to_string(value); |
296 | 6.80k | break; |
297 | 0 | } |
298 | 18 | case kJEIsUInt: { |
299 | 18 | uint32_t value = BigEndian::Load32(json_value.data()); |
300 | 18 | *result = std::to_string(value); |
301 | 18 | break; |
302 | 0 | } |
303 | 0 | case kJEIsInt64: { |
304 | 0 | int64_t value = util::DecodeInt64FromKey(json_value); |
305 | 0 | *result = std::to_string(value); |
306 | 0 | break; |
307 | 0 | } |
308 | 0 | case kJEIsUInt64: { |
309 | 0 | uint64_t value = BigEndian::Load64(json_value.data()); |
310 | 0 | *result = std::to_string(value); |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | case kJEIsDouble: { |
314 | 0 | double value = util::DecodeDoubleFromKey(json_value); |
315 | 0 | *result = std::to_string(value); |
316 | 0 | break; |
317 | 0 | } |
318 | 12 | case kJEIsFloat: { |
319 | 12 | float value = util::DecodeFloatFromKey(json_value); |
320 | 12 | *result = std::to_string(value); |
321 | 12 | break; |
322 | 0 | } |
323 | 0 | case kJEIsBoolFalse: { |
324 | 0 | *result = "false"; |
325 | 0 | break; |
326 | 0 | } |
327 | 0 | case kJEIsBoolTrue: { |
328 | 0 | *result = "true"; |
329 | 0 | break; |
330 | 0 | } |
331 | 344 | case kJEIsNull: { |
332 | 344 | *result = "null"; |
333 | 344 | break; |
334 | 0 | } |
335 | 0 | case kJEIsObject: FALLTHROUGH_INTENDED; |
336 | 0 | case kJEIsArray: |
337 | 0 | return STATUS(InvalidArgument, "Arrays and Objects not supported for this method"); |
338 | 16.3k | } |
339 | 16.3k | return Status::OK(); |
340 | 16.3k | } |
341 | | |
342 | | Status Jsonb::FromJsonbProcessObject(const Slice& jsonb, |
343 | | const JsonbHeader& jsonb_header, |
344 | 923 | rapidjson::Document* document) { |
345 | 923 | size_t metadata_begin_offset = sizeof(JsonbHeader); |
346 | | |
347 | 923 | size_t nelems = GetCount(jsonb_header); |
348 | 923 | const size_t data_begin_offset = ComputeDataOffset(nelems, kJBObject); |
349 | | |
350 | | // Now read the kv pairs and build the json. |
351 | 923 | document->SetObject(); |
352 | 3.41k | for (size_t i = 0; i < nelems; i++) { |
353 | 2.49k | Slice key; |
354 | 2.49k | RETURN_NOT_OK(GetObjectKey(i, jsonb, metadata_begin_offset, data_begin_offset, &key)); |
355 | 2.49k | Slice json_value; |
356 | 2.49k | JEntry value_metadata; |
357 | 2.49k | RETURN_NOT_OK(GetObjectValue(i, jsonb, metadata_begin_offset, data_begin_offset, nelems, |
358 | 2.49k | &json_value, &value_metadata)); |
359 | 2.49k | rapidjson::Value json_key = ValueFromSlice(document, key); |
360 | 2.49k | switch (GetJEType(value_metadata)) { |
361 | 329 | case kJEIsString: { |
362 | 329 | document->AddMember( |
363 | 329 | json_key, ValueFromSlice(document, json_value), document->GetAllocator()); |
364 | 329 | break; |
365 | 0 | } |
366 | 839 | case kJEIsInt: { |
367 | 839 | int32_t value = util::DecodeInt32FromKey(json_value); |
368 | 839 | AddNumericMember(document, key, value); |
369 | 839 | break; |
370 | 0 | } |
371 | 46 | case kJEIsUInt: { |
372 | 46 | uint32_t value = BigEndian::Load32(json_value.data()); |
373 | 46 | AddNumericMember(document, key, value); |
374 | 46 | break; |
375 | 0 | } |
376 | 140 | case kJEIsInt64: { |
377 | 140 | int64_t value = util::DecodeInt64FromKey(json_value); |
378 | 140 | AddNumericMember(document, key, value); |
379 | 140 | break; |
380 | 0 | } |
381 | 70 | case kJEIsUInt64: { |
382 | 70 | uint64_t value = BigEndian::Load64(json_value.data()); |
383 | 70 | AddNumericMember(document, key, value); |
384 | 70 | break; |
385 | 0 | } |
386 | 65 | case kJEIsDouble: { |
387 | 65 | double value = util::DecodeDoubleFromKey(json_value); |
388 | 65 | AddNumericMember(document, key, value); |
389 | 65 | break; |
390 | 0 | } |
391 | 70 | case kJEIsFloat: { |
392 | 70 | float value = util::DecodeFloatFromKey(json_value); |
393 | 70 | AddNumericMember(document, key, value); |
394 | 70 | break; |
395 | 0 | } |
396 | 70 | case kJEIsBoolFalse: { |
397 | 70 | document->AddMember(json_key, |
398 | 70 | rapidjson::Value(false), |
399 | 70 | document->GetAllocator()); |
400 | 70 | break; |
401 | 0 | } |
402 | 141 | case kJEIsBoolTrue: { |
403 | 141 | document->AddMember(json_key, |
404 | 141 | rapidjson::Value(true), |
405 | 141 | document->GetAllocator()); |
406 | 141 | break; |
407 | 0 | } |
408 | 251 | case kJEIsNull: { |
409 | 251 | document->AddMember(json_key, |
410 | 251 | rapidjson::Value(rapidjson::Type::kNullType), |
411 | 251 | document->GetAllocator()); |
412 | 251 | break; |
413 | 0 | } |
414 | 324 | case kJEIsObject: { |
415 | 324 | rapidjson::Document nested_container(&document->GetAllocator()); |
416 | 324 | nested_container.SetObject(); |
417 | 324 | RETURN_NOT_OK(FromJsonbInternal(json_value, &nested_container)); |
418 | 324 | document->AddMember(json_key, |
419 | 324 | std::move(nested_container), |
420 | 324 | document->GetAllocator()); |
421 | 324 | break; |
422 | 324 | } |
423 | 150 | case kJEIsArray: { |
424 | 150 | rapidjson::Document nested_container(&document->GetAllocator()); |
425 | 150 | nested_container.SetArray(); |
426 | 150 | RETURN_NOT_OK(FromJsonbInternal(json_value, &nested_container)); |
427 | 150 | document->AddMember(json_key, |
428 | 150 | std::move(nested_container), |
429 | 150 | document->GetAllocator()); |
430 | 150 | break; |
431 | 150 | } |
432 | 2.49k | } |
433 | 2.49k | } |
434 | 925 | return Status::OK(); |
435 | 923 | } |
436 | | |
437 | | Status Jsonb::GetObjectValue(size_t index, const Slice& jsonb, size_t metadata_begin_offset, |
438 | | size_t data_begin_offset, size_t num_kv_pairs, Slice *result, |
439 | 29.0k | JEntry* value_metadata) { |
440 | | // Compute the value index. |
441 | 29.0k | size_t key_index = metadata_begin_offset + (index * sizeof(JEntry)); |
442 | 29.0k | size_t value_index = key_index + num_kv_pairs * sizeof(JEntry); |
443 | 29.0k | if (value_index >= jsonb.size()) { |
444 | 0 | return STATUS(Corruption, "value index in jsonb out of bounds"); |
445 | 0 | } |
446 | | |
447 | | // Read the value metadata. |
448 | 29.0k | *value_metadata = BigEndian::Load32(jsonb.data() + value_index); |
449 | | |
450 | | // Read the value. |
451 | 29.0k | size_t value_end_offset = GetOffset(*value_metadata); |
452 | | |
453 | | // Process the value. |
454 | 29.0k | size_t value_offset; |
455 | 29.0k | size_t value_length; |
456 | 29.0k | std::tie(value_offset, value_length) = GetOffsetAndLength(value_index, jsonb, value_end_offset, |
457 | 29.0k | data_begin_offset, |
458 | 29.0k | metadata_begin_offset); |
459 | 29.0k | if (value_offset + value_length > jsonb.size()) { |
460 | 0 | return STATUS(Corruption, "json value data out of bounds in serialized jsonb"); |
461 | 0 | } |
462 | | |
463 | 29.0k | *result = Slice(jsonb.data() + value_offset, value_length); |
464 | 29.0k | return Status::OK(); |
465 | 29.0k | } |
466 | | |
467 | | Status Jsonb::GetObjectKey(size_t index, const Slice& jsonb, size_t metadata_begin_offset, |
468 | 35.6k | size_t data_begin_offset, Slice *result) { |
469 | | // Compute the key index. |
470 | 35.6k | size_t key_index = metadata_begin_offset + (index * sizeof(JEntry)); |
471 | 35.6k | if (key_index >= data_begin_offset) { |
472 | 0 | return STATUS(Corruption, "key index in jsonb out of bounds"); |
473 | 0 | } |
474 | | |
475 | | // Read the key metadata. |
476 | 35.6k | JEntry key_metadata = BigEndian::Load32(jsonb.data() + key_index); |
477 | | |
478 | | // Read the key. |
479 | 35.6k | size_t key_end_offset = GetOffset(key_metadata); |
480 | | |
481 | | // Process the key. |
482 | 35.6k | size_t key_offset; |
483 | 35.6k | size_t key_length; |
484 | 35.6k | std::tie(key_offset, key_length) = GetOffsetAndLength(key_index, jsonb, key_end_offset, |
485 | 35.6k | data_begin_offset, metadata_begin_offset); |
486 | 35.6k | if (key_offset + key_length > jsonb.size()) { |
487 | 0 | return STATUS(Corruption, "json key data out of bounds in serialized jsonb"); |
488 | 0 | } |
489 | | |
490 | 35.6k | *result = Slice(jsonb.data() + key_offset, key_length); |
491 | 35.6k | return Status::OK(); |
492 | 35.6k | } |
493 | | |
494 | | Status Jsonb::GetArrayElement(size_t index, const Slice& jsonb, |
495 | | size_t metadata_begin_offset, size_t data_begin_offset, |
496 | 938 | Slice* result, JEntry* element_metadata) { |
497 | 938 | size_t value_index = metadata_begin_offset + (index * sizeof(JEntry)); |
498 | 938 | if (value_index >= jsonb.size()) { |
499 | 0 | return STATUS(Corruption, "value index out of bounds"); |
500 | 0 | } |
501 | | |
502 | | // Read the metadata. |
503 | 938 | *element_metadata = BigEndian::Load32(jsonb.data() + value_index); |
504 | 938 | size_t value_end_offset = GetOffset(*element_metadata); |
505 | | |
506 | | // Process the value. |
507 | 938 | size_t value_offset; |
508 | 938 | size_t value_length; |
509 | 938 | std::tie(value_offset, value_length) = GetOffsetAndLength(value_index, jsonb, value_end_offset, |
510 | 938 | data_begin_offset, |
511 | 938 | metadata_begin_offset); |
512 | | |
513 | 938 | if (value_offset + value_length > jsonb.size()) { |
514 | 0 | return STATUS(Corruption, "json value out of bounds of serialized jsonb"); |
515 | 0 | } |
516 | 938 | *result = Slice(jsonb.data() + value_offset, value_length); |
517 | 938 | return Status::OK(); |
518 | 938 | } |
519 | | |
520 | | Status Jsonb::FromJsonbProcessArray(const Slice& jsonb, |
521 | | const JsonbHeader& jsonb_header, |
522 | 405 | rapidjson::Document* document) { |
523 | | |
524 | 405 | size_t metadata_begin_offset = sizeof(JsonbHeader); |
525 | 405 | size_t nelems = GetCount(jsonb_header); |
526 | 405 | size_t data_begin_offset = ComputeDataOffset(nelems, kJBArray); |
527 | | |
528 | | // Now read the array members. |
529 | 405 | document->SetArray(); |
530 | 1.32k | for (size_t i = 0; i < nelems; i++) { |
531 | 923 | Slice result; |
532 | 923 | JEntry element_metadata; |
533 | 923 | RETURN_NOT_OK(GetArrayElement(i, jsonb, metadata_begin_offset, data_begin_offset, &result, |
534 | 923 | &element_metadata)); |
535 | 923 | switch (GetJEType(element_metadata)) { |
536 | 50 | case kJEIsString: { |
537 | 50 | document->PushBack(ValueFromSlice(document, result), document->GetAllocator()); |
538 | 50 | break; |
539 | 0 | } |
540 | 463 | case kJEIsInt: { |
541 | 463 | int32_t value = util::DecodeInt32FromKey(result); |
542 | 463 | PushBackNumericMember(document, value); |
543 | 463 | break; |
544 | 0 | } |
545 | 0 | case kJEIsUInt: { |
546 | 0 | uint32_t value = BigEndian::Load32(result.data()); |
547 | 0 | PushBackNumericMember(document, value); |
548 | 0 | break; |
549 | 0 | } |
550 | 0 | case kJEIsInt64: { |
551 | 0 | int64_t value = util::DecodeInt64FromKey(result); |
552 | 0 | PushBackNumericMember(document, value); |
553 | 0 | break; |
554 | 0 | } |
555 | 0 | case kJEIsUInt64: { |
556 | 0 | uint64_t value = BigEndian::Load64(result.data()); |
557 | 0 | PushBackNumericMember(document, value); |
558 | 0 | break; |
559 | 0 | } |
560 | 0 | case kJEIsDouble: { |
561 | 0 | double value = util::DecodeDoubleFromKey(result); |
562 | 0 | PushBackNumericMember(document, value); |
563 | 0 | break; |
564 | 0 | } |
565 | 88 | case kJEIsFloat: { |
566 | 88 | float value = util::DecodeFloatFromKey(result); |
567 | 88 | PushBackNumericMember(document, value); |
568 | 88 | break; |
569 | 0 | } |
570 | 73 | case kJEIsBoolFalse: { |
571 | 73 | document->PushBack(rapidjson::Value(false), document->GetAllocator()); |
572 | 73 | break; |
573 | 0 | } |
574 | 79 | case kJEIsBoolTrue: { |
575 | 79 | document->PushBack(rapidjson::Value(true), document->GetAllocator()); |
576 | 79 | break; |
577 | 0 | } |
578 | 85 | case kJEIsNull: { |
579 | 85 | document->PushBack(rapidjson::Value(rapidjson::Type::kNullType), document->GetAllocator()); |
580 | 85 | break; |
581 | 0 | } |
582 | 84 | case kJEIsObject: { |
583 | 84 | rapidjson::Document nested_container(&document->GetAllocator()); |
584 | 84 | nested_container.SetObject(); |
585 | 84 | RETURN_NOT_OK(FromJsonbInternal(result, &nested_container)); |
586 | 84 | document->PushBack(std::move(nested_container), |
587 | 84 | document->GetAllocator()); |
588 | 84 | break; |
589 | 84 | } |
590 | 1 | case kJEIsArray: { |
591 | 1 | rapidjson::Document nested_container(&document->GetAllocator()); |
592 | 1 | nested_container.SetArray(); |
593 | 1 | RETURN_NOT_OK(FromJsonbInternal(result, &nested_container)); |
594 | 1 | document->PushBack(std::move(nested_container), |
595 | 1 | document->GetAllocator()); |
596 | 1 | break; |
597 | 1 | } |
598 | 923 | } |
599 | 923 | } |
600 | 405 | return Status::OK(); |
601 | 405 | } |
602 | | |
603 | 1.32k | Status Jsonb::FromJsonbInternal(const Slice& jsonb, rapidjson::Document* document) { |
604 | | // Read the jsonb header. |
605 | 1.32k | JsonbHeader jsonb_header = BigEndian::Load32(jsonb.data()); |
606 | | |
607 | 1.32k | if ((jsonb_header & kJBObject) == kJBObject) { |
608 | 921 | return FromJsonbProcessObject(jsonb, jsonb_header, document); |
609 | 921 | } |
610 | 408 | if ((jsonb_header & kJBArray) == kJBArray) { |
611 | 405 | rapidjson::Document array_doc(&document->GetAllocator()); |
612 | 405 | RETURN_NOT_OK(FromJsonbProcessArray(jsonb, jsonb_header, &array_doc)); |
613 | | |
614 | 405 | if ((jsonb_header & kJBScalar) && array_doc.GetArray().Size() == 1) { |
615 | | // This is actually a scalar, since jsonb stores scalars as arrays with one element. |
616 | | // Therefore, just return the single element. |
617 | 235 | document->CopyFrom(array_doc.GetArray()[0], document->GetAllocator()); |
618 | 170 | } else { |
619 | 170 | document->CopyFrom(array_doc, document->GetAllocator()); |
620 | 170 | } |
621 | 3 | } else { |
622 | 3 | return STATUS(InvalidArgument, "Invalid json type!"); |
623 | 3 | } |
624 | 405 | return Status::OK(); |
625 | 405 | } |
626 | | |
627 | | std::pair<size_t, size_t> Jsonb::GetOffsetAndLength(size_t element_metadata_offset, |
628 | | const Slice& jsonb, |
629 | | size_t element_end_offset, |
630 | | size_t data_begin_offset, |
631 | 65.6k | size_t metadata_begin_offset) { |
632 | 65.6k | if (element_metadata_offset == metadata_begin_offset) { |
633 | | // This is the first element. |
634 | 27.5k | return std::make_pair(data_begin_offset, element_end_offset); |
635 | 27.5k | } |
636 | | |
637 | 38.1k | DCHECK_GE(element_metadata_offset, sizeof(JsonbHeader)); |
638 | 38.1k | JEntry prev_element = |
639 | 38.1k | BigEndian::Load32(jsonb.data() + element_metadata_offset - sizeof(JEntry)); |
640 | 38.1k | size_t prev_element_offset = GetOffset(prev_element); |
641 | 38.1k | return std::make_pair(prev_element_offset + data_begin_offset, |
642 | 38.1k | element_end_offset - prev_element_offset); |
643 | | |
644 | 38.1k | } |
645 | | |
646 | 177 | Status Jsonb::ToRapidJson(rapidjson::Document* document) const { |
647 | 177 | return FromJsonbInternal(serialized_jsonb_, document); |
648 | 177 | } |
649 | | |
650 | 335 | Status Jsonb::ToJsonString(std::string* json) const { |
651 | 335 | return ToJsonStringInternal(serialized_jsonb_, json); |
652 | 335 | } |
653 | | |
654 | 592 | Status Jsonb::ToJsonStringInternal(const Slice& jsonb, std::string* json) { |
655 | 592 | rapidjson::Document document; |
656 | 592 | RETURN_NOT_OK(FromJsonbInternal(jsonb, &document)); |
657 | 592 | *DCHECK_NOTNULL(json) = WriteRapidJsonToString(document); |
658 | 592 | return Status::OK(); |
659 | 592 | } |
660 | | |
661 | | Status Jsonb::ApplyJsonbOperatorToArray(const Slice& jsonb, const QLJsonOperationPB& json_op, |
662 | | const JsonbHeader& jsonb_header, |
663 | 20 | Slice* result, JEntry* element_metadata) { |
664 | 20 | if(!json_op.operand().value().has_varint_value()) { |
665 | 3 | return STATUS_SUBSTITUTE(NotFound, "Couldn't apply json operator"); |
666 | 3 | } |
667 | | |
668 | | // For arrays, the argument needs to be an integer. |
669 | 17 | size_t num_array_entries = GetCount(jsonb_header); |
670 | | |
671 | | // Retrieve the array index and verify. |
672 | 17 | util::VarInt varint; |
673 | 17 | RETURN_NOT_OK(varint.DecodeFromComparable(json_op.operand().value().varint_value())); |
674 | 17 | int64_t array_index = VERIFY_RESULT(varint.ToInt64()); |
675 | | |
676 | 17 | if (array_index < 0 || implicit_cast<size_t>(array_index) >= num_array_entries) { |
677 | 2 | return STATUS_SUBSTITUTE(NotFound, "Array index: $0 out of bounds [0, $1)", |
678 | 2 | array_index, num_array_entries); |
679 | 2 | } |
680 | | |
681 | 15 | RETURN_NOT_OK(GetArrayElement(array_index, jsonb, sizeof(jsonb_header), |
682 | 15 | ComputeDataOffset(num_array_entries, kJBArray), result, |
683 | 15 | element_metadata)); |
684 | 15 | return Status::OK(); |
685 | 15 | } |
686 | | |
687 | | Status Jsonb::ApplyJsonbOperatorToObject(const Slice& jsonb, const QLJsonOperationPB& json_op, |
688 | | const JsonbHeader& jsonb_header, |
689 | 26.6k | Slice* result, JEntry* element_metadata) { |
690 | 26.6k | if (!json_op.operand().value().has_string_value()) { |
691 | 6 | return STATUS_SUBSTITUTE(NotFound, "Couldn't apply json operator"); |
692 | 6 | } |
693 | | |
694 | 26.6k | size_t num_kv_pairs = GetCount(jsonb_header); |
695 | 26.6k | const string& search_key = json_op.operand().value().string_value(); |
696 | | |
697 | 26.6k | size_t metadata_begin_offset = sizeof(jsonb_header); |
698 | 26.6k | size_t data_begin_offset = ComputeDataOffset(num_kv_pairs, kJBObject); |
699 | | |
700 | | // Binary search to find the key. |
701 | 26.6k | int64_t low = 0, high = num_kv_pairs - 1; |
702 | 26.6k | auto search_key_slice = Slice(search_key); |
703 | 33.2k | while (low <= high) { |
704 | 33.1k | size_t mid = low + (high - low)/2; |
705 | 33.1k | Slice mid_key; |
706 | 33.1k | RETURN_NOT_OK(GetObjectKey(mid, jsonb, metadata_begin_offset, data_begin_offset, &mid_key)); |
707 | | |
708 | 33.1k | if (mid_key == search_key_slice) { |
709 | 26.5k | RETURN_NOT_OK(GetObjectValue(mid, jsonb, sizeof(jsonb_header), |
710 | 26.5k | ComputeDataOffset(num_kv_pairs, kJBObject), num_kv_pairs, |
711 | 26.5k | result, element_metadata)); |
712 | 26.5k | return Status::OK(); |
713 | 6.60k | } else if (mid_key.ToBuffer() > search_key) { |
714 | 328 | high = mid - 1; |
715 | 6.27k | } else { |
716 | 6.27k | low = mid + 1; |
717 | 6.27k | } |
718 | 33.1k | } |
719 | 87 | return STATUS_SUBSTITUTE(NotFound, "Couldn't find key $0 in json document", search_key); |
720 | 26.6k | } |
721 | | |
722 | 17.8k | Status Jsonb::ApplyJsonbOperators(const QLJsonColumnOperationsPB& json_ops, QLValue* result) const { |
723 | 17.8k | const int num_ops = json_ops.json_operations().size(); |
724 | | |
725 | 17.8k | Slice jsonop_result; |
726 | 17.8k | Slice operand(serialized_jsonb_); |
727 | 17.8k | JEntry element_metadata; |
728 | 43.9k | for (int i = 0; i < num_ops; i++) { |
729 | 27.2k | const QLJsonOperationPB &op = json_ops.json_operations().Get(i); |
730 | 27.2k | const Status s = ApplyJsonbOperator(operand, op, &jsonop_result, |
731 | 27.2k | &element_metadata); |
732 | 27.2k | if (s.IsNotFound()) { |
733 | | // We couldn't apply the operator to the operand and hence return null as the result. |
734 | 682 | result->SetNull(); |
735 | 682 | return Status::OK(); |
736 | 682 | } |
737 | 26.5k | RETURN_NOT_OK(s); |
738 | | |
739 | 26.5k | if (IsScalar(element_metadata) && i != num_ops - 1) { |
740 | | // We have to apply another operation after this, but we received a scalar intermediate |
741 | | // result. |
742 | 505 | result->SetNull(); |
743 | 505 | return Status::OK(); |
744 | 505 | } |
745 | 26.0k | operand = jsonop_result; |
746 | 26.0k | } |
747 | | |
748 | | // In case of '->>', we need to return a string result. |
749 | 16.6k | if (num_ops > 0 && |
750 | 16.6k | json_ops.json_operations().Get(num_ops - 1).json_operator() == JsonOperatorPB::JSON_TEXT) { |
751 | 16.6k | if (IsScalar(element_metadata)) { |
752 | 16.3k | RETURN_NOT_OK(ScalarToString(element_metadata, jsonop_result, |
753 | 16.3k | result->mutable_string_value())); |
754 | 258 | } else { |
755 | 258 | string str_result; |
756 | 258 | RETURN_NOT_OK(ToJsonStringInternal(jsonop_result, &str_result)); |
757 | 258 | result->set_string_value(std::move(str_result)); |
758 | 258 | } |
759 | 16.6k | return Status::OK(); |
760 | 60 | } |
761 | | |
762 | 60 | string jsonb_result = jsonop_result.ToBuffer(); |
763 | 60 | if (IsScalar(element_metadata)) { |
764 | | // In case of a scalar that is received from an operation, convert it to a jsonb scalar. |
765 | 53 | RETURN_NOT_OK(CreateScalar(jsonop_result, |
766 | 53 | element_metadata, |
767 | 53 | &jsonb_result)); |
768 | 53 | } |
769 | 60 | result->set_jsonb_value(std::move(jsonb_result)); |
770 | 60 | return Status::OK(); |
771 | 60 | } |
772 | | |
773 | | Status Jsonb::ApplyJsonbOperator(const Slice& jsonb, const QLJsonOperationPB& json_op, |
774 | 27.2k | Slice* result, JEntry* element_metadata) { |
775 | | // Currently, both these operators are considered the same since we only handle strings. |
776 | 27.2k | DCHECK(json_op.json_operator() == JsonOperatorPB::JSON_OBJECT || |
777 | 27.2k | json_op.json_operator() == JsonOperatorPB::JSON_TEXT); |
778 | | |
779 | | // We only support strings and integers as the argument to the json operation currently. |
780 | 27.2k | DCHECK(json_op.operand().has_value()); |
781 | | |
782 | 27.2k | if (jsonb.size() < sizeof(JsonbHeader)) { |
783 | 0 | return STATUS(InvalidArgument, "Not enough data to process"); |
784 | 0 | } |
785 | | |
786 | 27.2k | JsonbHeader jsonb_header = BigEndian::Load32(jsonb.data()); |
787 | 27.2k | if ((jsonb_header & kJBScalar) && (jsonb_header & kJBArray)) { |
788 | | // This is a scalar value and no operators can be applied to it. |
789 | 584 | return STATUS(NotFound, "Cannot apply operators to scalar values"); |
790 | 26.6k | } else if (jsonb_header & kJBArray) { |
791 | 20 | return ApplyJsonbOperatorToArray(jsonb, json_op, jsonb_header, result, element_metadata); |
792 | 26.6k | } else if (jsonb_header & kJBObject) { |
793 | 26.6k | return ApplyJsonbOperatorToObject(jsonb, json_op, jsonb_header, result, element_metadata); |
794 | 26.6k | } |
795 | | |
796 | 0 | return STATUS(InvalidArgument, "Invalid json operation"); |
797 | 0 | } |
798 | | |
799 | | Status Jsonb::CreateScalar(const Slice& scalar, const JEntry& original_jentry, |
800 | 53 | std::string* scalar_jsonb) { |
801 | | // Build the header. |
802 | 53 | size_t metadata_begin_offset = sizeof(JsonbHeader); |
803 | 53 | size_t metadata_size = metadata_begin_offset + sizeof(JEntry); |
804 | 53 | size_t data_begin_offset = metadata_size; |
805 | | |
806 | | // Resize the result. |
807 | 53 | scalar_jsonb->resize(metadata_size); |
808 | 53 | scalar_jsonb->append(scalar.cdata(), scalar.size()); |
809 | | |
810 | 53 | JsonbHeader jsonb_header = (1 & kJBCountMask) | kJBArray | kJBScalar; |
811 | 53 | JEntry jentry = GetOffset(narrow_cast<JEntry>(scalar_jsonb->size() - data_begin_offset)) |
812 | 53 | | GetJEType(original_jentry); |
813 | | |
814 | | // Store the header. |
815 | 53 | BigEndian::Store32(&((*scalar_jsonb)[0]), jsonb_header); |
816 | | // Store the JEntry. |
817 | 53 | BigEndian::Store32(&((*scalar_jsonb)[metadata_begin_offset]), jentry); |
818 | 53 | return Status::OK(); |
819 | 53 | } |
820 | | |
821 | | } // namespace common |
822 | | } // namespace yb |