YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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