YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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.6k
bool Jsonb::IsScalar(const JEntry& jentry) {
32
43.6k
  uint32_t jentry_type = GetJEType(jentry);
33
43.6k
  return ((jentry_type) != kJEIsArray && 
(jentry_type) != kJEIsObject43.6k
);
34
43.6k
}
35
36
2.10M
size_t Jsonb::ComputeDataOffset(const size_t num_entries, const uint32_t container_type) {
37
2.10M
  const size_t num_jentries = (container_type & kJBArray) ? 
num_entries2.02M
:
2 * num_entries78.9k
;
38
2.10M
  return sizeof(JsonbHeader) + num_jentries * sizeof(JEntry);
39
2.10M
}
40
41
2.06M
Jsonb::Jsonb() {
42
2.06M
}
43
44
Jsonb::Jsonb(const std::string& jsonb)
45
6.67k
    : serialized_jsonb_(jsonb) {
46
6.67k
}
47
48
Jsonb::Jsonb(std::string&& jsonb)
49
0
    : serialized_jsonb_(std::move(jsonb)) {
50
0
}
51
52
18.0k
void Jsonb::Assign(const std::string& jsonb) {
53
18.0k
  serialized_jsonb_ = jsonb;
54
18.0k
}
55
56
0
void Jsonb::Assign(std::string&& jsonb) {
57
0
  serialized_jsonb_ = std::move(jsonb);
58
0
}
59
60
12.8k
std::string&& Jsonb::MoveSerializedJsonb() {
61
12.8k
  return std::move(serialized_jsonb_);
62
12.8k
}
63
64
162
const std::string& Jsonb::SerializedJsonb() const {
65
162
  return serialized_jsonb_;
66
162
}
67
68
0
bool Jsonb::operator==(const Jsonb& other) const {
69
0
  return serialized_jsonb_ == other.serialized_jsonb_;
70
0
}
71
72
2.04M
Status Jsonb::FromString(const std::string& json) {
73
  // Parse the json document.
74
2.04M
  rapidjson::Document document;
75
2.04M
  document.Parse<0>(json.c_str());
76
2.04M
  if (document.HasParseError()) {
77
4
    return STATUS(Corruption, "JSON text is corrupt",
78
4
                  rapidjson::GetParseError_En(document.GetParseError()));
79
4
  }
80
2.04M
  return FromRapidJson(document);
81
2.04M
}
82
83
2.04M
Status Jsonb::FromRapidJson(const rapidjson::Document& document) {
84
2.04M
  return ToJsonbInternal(document, &serialized_jsonb_);
85
2.04M
}
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
2.04M
                                                              std::string* jsonb) {
102
  // Compute the size we need to allocate for the metadata.
103
2.04M
  size_t metadata_offset = jsonb->size();
104
2.04M
  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
2.04M
  jsonb->resize(metadata_offset + jsonb_metadata_size);
108
109
  // Store the jsonb header at the appropriate place.
110
2.04M
  JsonbHeader jsonb_header = GetCount(narrow_cast<JsonbHeader>(num_entries)) | container_type;
111
2.04M
  BigEndian::Store32(&((*jsonb)[metadata_offset]), jsonb_header);
112
2.04M
  metadata_offset += sizeof(JsonbHeader);
113
114
2.04M
  return std::make_pair(metadata_offset, jsonb_metadata_size);
115
2.04M
}
116
117
CHECKED_STATUS Jsonb::ToJsonbProcessObject(const rapidjson::Value& document,
118
18.5k
                                           std::string* jsonb) {
119
18.5k
  DCHECK(document.IsObject());
120
121
  // Use a map since we need to store the keys in sorted order.
122
18.5k
  std::map<string, const rapidjson::Value&> kv_pairs;
123
25.0k
  for (const auto& member : document.GetObject()) {
124
25.0k
    kv_pairs.emplace(member.name.GetString(), member.value);
125
25.0k
  }
126
127
18.5k
  size_t metadata_offset, jsonb_metadata_size;
128
18.5k
  std::tie(metadata_offset, jsonb_metadata_size) = ComputeOffsetsAndJsonbHeader(kv_pairs.size(),
129
18.5k
                                                                                kJBObject, jsonb);
130
131
  // Now append the keys and store the key offsets in the jentry.
132
18.5k
  size_t data_begin_offset = jsonb->size();
133
25.0k
  for (const auto& entry : kv_pairs) {
134
25.0k
    jsonb->append(entry.first);
135
25.0k
    JEntry key_offset = narrow_cast<JEntry>(jsonb->size() - data_begin_offset);
136
25.0k
    JEntry jentry = GetOffset(key_offset) | kJEIsString; // keys are always strings.
137
25.0k
    BigEndian::Store32(&((*jsonb)[metadata_offset]), jentry);
138
25.0k
    metadata_offset += sizeof(JEntry);
139
25.0k
  }
140
141
  // Append the values to the buffer.
142
25.0k
  for (const auto& entry : kv_pairs) {
143
25.0k
    const rapidjson::Value& value = entry.second;
144
25.0k
    RETURN_NOT_OK(ProcessJsonValueAndMetadata(value, data_begin_offset, jsonb, &metadata_offset));
145
25.0k
  }
146
147
  // The metadata slice should now be empty.
148
18.5k
  if (data_begin_offset != metadata_offset) {
149
0
    return STATUS(Corruption, "Couldn't process entire data for json object");
150
0
  }
151
18.5k
  return Status::OK();
152
18.5k
}
153
154
CHECKED_STATUS Jsonb::ProcessJsonValueAndMetadata(const rapidjson::Value& value,
155
                                                  const size_t data_begin_offset,
156
                                                  std::string* jsonb,
157
2.05M
                                                  size_t* metadata_offset) {
158
2.05M
  JEntry jentry = 0;
159
2.05M
  switch (value.GetType()) {
160
2.02M
    case rapidjson::Type::kNullType:
161
2.02M
      jentry |= kJEIsNull;
162
2.02M
      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
40
    case rapidjson::Type::kArrayType:
170
40
      jentry |= kJEIsArray;
171
40
      RETURN_NOT_OK(ToJsonbInternal(value, jsonb));
172
40
      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
12.6k
    case rapidjson::Type::kNumberType:
178
12.6k
      if (value.IsInt()) {
179
12.4k
        jentry |= kJEIsInt;
180
12.4k
        util::AppendInt32ToKey(value.GetInt(), jsonb);
181
12.4k
      } else 
if (200
value.IsUint()200
) {
182
7
        jentry |= kJEIsUInt;
183
7
        util::AppendBigEndianUInt32(value.GetUint(), jsonb);
184
193
      } else if (value.IsInt64()) {
185
26
        jentry |= kJEIsInt64;
186
26
        util::AppendInt64ToKey(value.GetInt64(), jsonb);
187
167
      } else if (value.IsUint64()) {
188
13
        jentry |= kJEIsUInt64;
189
13
        util::AppendBigEndianUInt64(value.GetUint64(), jsonb);
190
154
      } else if (value.IsFloat()) {
191
43
        jentry |= kJEIsFloat;
192
43
        util::AppendFloatToKey(value.GetFloat(), jsonb);
193
111
      } else if (value.IsDouble()) {
194
12
        jentry |= kJEIsDouble;
195
12
        util::AppendDoubleToKey(value.GetDouble(), jsonb);
196
99
      } else {
197
99
        return STATUS(NotSupported, "Numeric type is not supported");
198
99
      }
199
12.5k
      break;
200
12.5k
    case rapidjson::Type::kStringType:
201
6.42k
      jentry |= kJEIsString;
202
6.42k
      jsonb->append(value.GetString());
203
6.42k
      break;
204
2.05M
  }
205
206
  // Store the offset.
207
2.05M
  auto offset = narrow_cast<JEntry>(jsonb->size() - data_begin_offset);
208
2.05M
  jentry |= GetOffset(offset);
209
210
  // Store the JEntry.
211
2.05M
  BigEndian::Store32(&((*jsonb)[*metadata_offset]), jentry);
212
2.05M
  (*metadata_offset) += sizeof(JEntry);
213
2.05M
  return Status::OK();
214
2.05M
}
215
216
CHECKED_STATUS Jsonb::ToJsonbProcessArray(const rapidjson::Value& document,
217
                                          const bool is_scalar,
218
2.02M
                                          std::string* jsonb) {
219
2.02M
  DCHECK(document.IsArray());
220
221
2.02M
  const auto& json_array = document.GetArray();
222
2.02M
  const size_t num_array_entries = json_array.Size();
223
224
2.02M
  uint32_t container_type = kJBArray;
225
2.02M
  if (is_scalar) {
226
    // Scalars are an array with one element and the special kJBScalar field set in the header.
227
2.02M
    DCHECK_EQ(num_array_entries, 1);
228
2.02M
    container_type |= kJBScalar;
229
2.02M
  }
230
231
2.02M
  size_t metadata_offset, jsonb_metadata_size;
232
2.02M
  std::tie(metadata_offset, jsonb_metadata_size) = ComputeOffsetsAndJsonbHeader(num_array_entries,
233
2.02M
                                                                                container_type,
234
2.02M
                                                                                jsonb);
235
2.02M
  const size_t data_begin_offset = jsonb->size();
236
  // Append the array members to the buffer.
237
2.02M
  for (const rapidjson::Value& value : json_array) {
238
2.02M
    RETURN_NOT_OK(ProcessJsonValueAndMetadata(value, data_begin_offset, jsonb, &metadata_offset));
239
2.02M
  }
240
241
  // The metadata slice should now be empty.
242
2.02M
  if (data_begin_offset != metadata_offset) {
243
0
    return STATUS(Corruption, "Couldn't process entire data for json array");
244
0
  }
245
2.02M
  return Status::OK();
246
2.02M
}
247
248
2.04M
CHECKED_STATUS Jsonb::ToJsonbInternal(const rapidjson::Value& document, std::string* jsonb) {
249
2.04M
  if (document.IsObject()) {
250
18.4k
    return ToJsonbProcessObject(document, jsonb);
251
2.02M
  } else if (document.IsArray()) {
252
53
    return ToJsonbProcessArray(document, /* is_scalar */ false, jsonb);
253
2.02M
  } 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
2.02M
    rapidjson::Document json_array;
257
2.02M
    json_array.SetArray();
258
259
2.02M
    rapidjson::Value tmpvalue;
260
2.02M
    tmpvalue.CopyFrom(document, json_array.GetAllocator());
261
2.02M
    json_array.PushBack(std::move(tmpvalue), json_array.GetAllocator());
262
2.02M
    return ToJsonbProcessArray(json_array, true, jsonb);
263
2.02M
  }
264
2.04M
}
265
266
namespace {
267
268
16.7k
rapidjson::Value ValueFromSlice(rapidjson::Document* document, const Slice& value) {
269
16.7k
  return rapidjson::Value(
270
16.7k
      value.cdata(), narrow_cast<rapidjson::SizeType>(value.size()), document->GetAllocator());
271
16.7k
}
272
273
template <typename T>
274
7.43k
void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) {
275
7.43k
  document->AddMember(
276
7.43k
      ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator());
277
7.43k
}
jsonb.cc:void yb::common::(anonymous namespace)::AddNumericMember<int>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, yb::Slice const&, int)
Line
Count
Source
274
7.04k
void AddNumericMember(rapidjson::Document* document, const Slice& key, T value) {
275
7.04k
  document->AddMember(
276
7.04k
      ValueFromSlice(document, key), rapidjson::Value(value), document->GetAllocator());
277
7.04k
}
jsonb.cc:void yb::common::(anonymous namespace)::AddNumericMember<unsigned int>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, yb::Slice const&, unsigned int)
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:void yb::common::(anonymous namespace)::AddNumericMember<long long>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, yb::Slice const&, long long)
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:void yb::common::(anonymous namespace)::AddNumericMember<unsigned long long>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, yb::Slice const&, unsigned long long)
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:void yb::common::(anonymous namespace)::AddNumericMember<double>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, yb::Slice const&, double)
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:void yb::common::(anonymous namespace)::AddNumericMember<float>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, yb::Slice const&, float)
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
597
void PushBackNumericMember(rapidjson::Document* document, T value) {
281
597
  document->PushBack(rapidjson::Value(value),
282
597
                     document->GetAllocator());
283
597
}
jsonb.cc:void yb::common::(anonymous namespace)::PushBackNumericMember<int>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, int)
Line
Count
Source
280
509
void PushBackNumericMember(rapidjson::Document* document, T value) {
281
509
  document->PushBack(rapidjson::Value(value),
282
509
                     document->GetAllocator());
283
509
}
Unexecuted instantiation: jsonb.cc:void yb::common::(anonymous namespace)::PushBackNumericMember<unsigned int>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, unsigned int)
Unexecuted instantiation: jsonb.cc:void yb::common::(anonymous namespace)::PushBackNumericMember<long long>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, long long)
Unexecuted instantiation: jsonb.cc:void yb::common::(anonymous namespace)::PushBackNumericMember<unsigned long long>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, unsigned long long)
Unexecuted instantiation: jsonb.cc:void yb::common::(anonymous namespace)::PushBackNumericMember<double>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, double)
jsonb.cc:void yb::common::(anonymous namespace)::PushBackNumericMember<float>(rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>*, float)
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.5k
                             string* result) {
288
16.5k
  switch (GetJEType(element_metadata)) {
289
9.17k
    case kJEIsString: {
290
9.17k
      *result = json_value.ToBuffer();
291
9.17k
      break;
292
0
    }
293
6.98k
    case kJEIsInt: {
294
6.98k
      int32_t value = util::DecodeInt32FromKey(json_value);
295
6.98k
      *result = std::to_string(value);
296
6.98k
      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
346
    case kJEIsNull: {
332
346
      *result = "null";
333
346
      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.5k
  }
339
16.5k
  return Status::OK();
340
16.5k
}
341
342
Status Jsonb::FromJsonbProcessObject(const Slice& jsonb,
343
                                     const JsonbHeader& jsonb_header,
344
7.01k
                                     rapidjson::Document* document) {
345
7.01k
  size_t metadata_begin_offset = sizeof(JsonbHeader);
346
347
7.01k
  size_t nelems = GetCount(jsonb_header);
348
7.01k
  const size_t data_begin_offset = ComputeDataOffset(nelems, kJBObject);
349
350
  // Now read the kv pairs and build the json.
351
7.01k
  document->SetObject();
352
15.8k
  for (size_t i = 0; i < nelems; 
i++8.81k
) {
353
8.80k
    Slice key;
354
8.80k
    RETURN_NOT_OK(GetObjectKey(i, jsonb, metadata_begin_offset, data_begin_offset, &key));
355
8.80k
    Slice json_value;
356
8.80k
    JEntry value_metadata;
357
8.80k
    RETURN_NOT_OK(GetObjectValue(i, jsonb, metadata_begin_offset, data_begin_offset, nelems,
358
8.80k
                                 &json_value, &value_metadata));
359
8.80k
    rapidjson::Value json_key = ValueFromSlice(document, key);
360
8.80k
    switch (GetJEType(value_metadata)) {
361
448
      case kJEIsString: {
362
448
        document->AddMember(
363
448
            json_key, ValueFromSlice(document, json_value), document->GetAllocator());
364
448
        break;
365
0
      }
366
7.04k
      case kJEIsInt: {
367
7.04k
        int32_t value = util::DecodeInt32FromKey(json_value);
368
7.04k
        AddNumericMember(document, key, value);
369
7.04k
        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
248
      case kJEIsNull: {
409
248
        document->AddMember(json_key,
410
248
                            rapidjson::Value(rapidjson::Type::kNullType),
411
248
                            document->GetAllocator());
412
248
        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
154
      case kJEIsArray: {
424
154
        rapidjson::Document nested_container(&document->GetAllocator());
425
154
        nested_container.SetArray();
426
154
        RETURN_NOT_OK(FromJsonbInternal(json_value, &nested_container));
427
154
        document->AddMember(json_key,
428
154
                            std::move(nested_container),
429
154
                            document->GetAllocator());
430
154
        break;
431
154
      }
432
8.80k
    }
433
8.80k
  }
434
7.02k
  return Status::OK();
435
7.01k
}
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
35.5k
                             JEntry* value_metadata) {
440
  // Compute the value index.
441
35.5k
  size_t key_index = metadata_begin_offset + (index * sizeof(JEntry));
442
35.5k
  size_t value_index = key_index + num_kv_pairs * sizeof(JEntry);
443
35.5k
  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
35.5k
  *value_metadata = BigEndian::Load32(jsonb.data() + value_index);
449
450
  // Read the value.
451
35.5k
  size_t value_end_offset = GetOffset(*value_metadata);
452
453
  // Process the value.
454
35.5k
  size_t value_offset;
455
35.5k
  size_t value_length;
456
35.5k
  std::tie(value_offset, value_length) = GetOffsetAndLength(value_index, jsonb, value_end_offset,
457
35.5k
                                                            data_begin_offset,
458
35.5k
                                                            metadata_begin_offset);
459
35.5k
  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
35.5k
  *result = Slice(jsonb.data() + value_offset, value_length);
464
35.5k
  return Status::OK();
465
35.5k
}
466
467
Status Jsonb::GetObjectKey(size_t index, const Slice& jsonb, size_t metadata_begin_offset,
468
42.3k
                           size_t data_begin_offset, Slice *result) {
469
  // Compute the key index.
470
42.3k
  size_t key_index = metadata_begin_offset + (index * sizeof(JEntry));
471
42.3k
  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
42.3k
  JEntry key_metadata = BigEndian::Load32(jsonb.data() + key_index);
477
478
  // Read the key.
479
42.3k
  size_t key_end_offset = GetOffset(key_metadata);
480
481
  // Process the key.
482
42.3k
  size_t key_offset;
483
42.3k
  size_t key_length;
484
42.3k
  std::tie(key_offset, key_length) = GetOffsetAndLength(key_index, jsonb, key_end_offset,
485
42.3k
                                                        data_begin_offset, metadata_begin_offset);
486
42.3k
  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
42.3k
  *result = Slice(jsonb.data() + key_offset, key_length);
491
42.3k
  return Status::OK();
492
42.3k
}
493
494
Status Jsonb::GetArrayElement(size_t index, const Slice& jsonb,
495
                              size_t metadata_begin_offset, size_t data_begin_offset,
496
1.02k
                              Slice* result, JEntry* element_metadata) {
497
1.02k
  size_t value_index = metadata_begin_offset + (index * sizeof(JEntry));
498
1.02k
  if (value_index >= jsonb.size()) {
499
0
    return STATUS(Corruption, "value index out of bounds");
500
0
  }
501
502
  // Read the metadata.
503
1.02k
  *element_metadata = BigEndian::Load32(jsonb.data() + value_index);
504
1.02k
  size_t value_end_offset = GetOffset(*element_metadata);
505
506
  // Process the value.
507
1.02k
  size_t value_offset;
508
1.02k
  size_t value_length;
509
1.02k
  std::tie(value_offset, value_length) = GetOffsetAndLength(value_index, jsonb, value_end_offset,
510
1.02k
                                                            data_begin_offset,
511
1.02k
                                                            metadata_begin_offset);
512
513
1.02k
  if (value_offset + value_length > jsonb.size()) {
514
0
    return STATUS(Corruption, "json value out of bounds of serialized jsonb");
515
0
  }
516
1.02k
  *result = Slice(jsonb.data() + value_offset, value_length);
517
1.02k
  return Status::OK();
518
1.02k
}
519
520
Status Jsonb::FromJsonbProcessArray(const Slice& jsonb,
521
                                    const JsonbHeader& jsonb_header,
522
484
                                    rapidjson::Document* document) {
523
524
484
  size_t metadata_begin_offset = sizeof(JsonbHeader);
525
484
  size_t nelems = GetCount(jsonb_header);
526
484
  size_t data_begin_offset = ComputeDataOffset(nelems, kJBArray);
527
528
  // Now read the array members.
529
484
  document->SetArray();
530
1.49k
  for (size_t i = 0; i < nelems; 
i++1.01k
) {
531
1.01k
    Slice result;
532
1.01k
    JEntry element_metadata;
533
1.01k
    RETURN_NOT_OK(GetArrayElement(i, jsonb, metadata_begin_offset, data_begin_offset, &result,
534
1.01k
                                  &element_metadata));
535
1.01k
    switch (GetJEType(element_metadata)) {
536
88
      case kJEIsString: {
537
88
        document->PushBack(ValueFromSlice(document, result), document->GetAllocator());
538
88
        break;
539
0
      }
540
509
      case kJEIsInt: {
541
509
        int32_t value = util::DecodeInt32FromKey(result);
542
509
        PushBackNumericMember(document, value);
543
509
        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
88
      case kJEIsObject: {
583
88
        rapidjson::Document nested_container(&document->GetAllocator());
584
88
        nested_container.SetObject();
585
88
        RETURN_NOT_OK(FromJsonbInternal(result, &nested_container));
586
88
        document->PushBack(std::move(nested_container),
587
88
                           document->GetAllocator());
588
88
        break;
589
88
      }
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
1.01k
    }
599
1.01k
  }
600
485
  return Status::OK();
601
484
}
602
603
7.50k
Status Jsonb::FromJsonbInternal(const Slice& jsonb, rapidjson::Document* document) {
604
  // Read the jsonb header.
605
7.50k
  JsonbHeader jsonb_header = BigEndian::Load32(jsonb.data());
606
607
7.50k
  if ((jsonb_header & kJBObject) == kJBObject) {
608
7.01k
    return FromJsonbProcessObject(jsonb, jsonb_header, document);
609
7.01k
  }
610
487
  if ((jsonb_header & kJBArray) == kJBArray) {
611
484
    rapidjson::Document array_doc(&document->GetAllocator());
612
484
    RETURN_NOT_OK(FromJsonbProcessArray(jsonb, jsonb_header, &array_doc));
613
614
484
    if ((jsonb_header & kJBScalar) && 
array_doc.GetArray().Size() == 1311
) {
615
      // This is actually a scalar, since jsonb stores scalars as arrays with one element.
616
      // Therefore, just return the single element.
617
311
      document->CopyFrom(array_doc.GetArray()[0], document->GetAllocator());
618
311
    } else {
619
173
      document->CopyFrom(array_doc, document->GetAllocator());
620
173
    }
621
484
  } else {
622
3
    return STATUS(InvalidArgument, "Invalid json type!");
623
3
  }
624
484
  return Status::OK();
625
487
}
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
78.9k
                                                    size_t metadata_begin_offset) {
632
78.9k
  if (element_metadata_offset == metadata_begin_offset) {
633
    // This is the first element.
634
33.9k
    return std::make_pair(data_begin_offset, element_end_offset);
635
33.9k
  }
636
637
45.0k
  DCHECK_GE(element_metadata_offset, sizeof(JsonbHeader));
638
45.0k
  JEntry prev_element =
639
45.0k
      BigEndian::Load32(jsonb.data() + element_metadata_offset - sizeof(JEntry));
640
45.0k
  size_t prev_element_offset = GetOffset(prev_element);
641
45.0k
  return std::make_pair(prev_element_offset + data_begin_offset,
642
45.0k
                        element_end_offset - prev_element_offset);
643
644
78.9k
}
645
646
276
Status Jsonb::ToRapidJson(rapidjson::Document* document) const {
647
276
  return FromJsonbInternal(serialized_jsonb_, document);
648
276
}
649
650
6.40k
Status Jsonb::ToJsonString(std::string* json) const {
651
6.40k
  return ToJsonStringInternal(serialized_jsonb_, json);
652
6.40k
}
653
654
6.65k
Status Jsonb::ToJsonStringInternal(const Slice& jsonb, std::string* json) {
655
6.65k
  rapidjson::Document document;
656
6.65k
  RETURN_NOT_OK(FromJsonbInternal(jsonb, &document));
657
6.65k
  *DCHECK_NOTNULL(json) = WriteRapidJsonToString(document);
658
6.65k
  return Status::OK();
659
6.65k
}
660
661
Status Jsonb::ApplyJsonbOperatorToArray(const Slice& jsonb, const QLJsonOperationPB& json_op,
662
                                        const JsonbHeader& jsonb_header,
663
24
                                        Slice* result, JEntry* element_metadata) {
664
24
  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
21
  size_t num_array_entries = GetCount(jsonb_header);
670
671
  // Retrieve the array index and verify.
672
21
  util::VarInt varint;
673
21
  RETURN_NOT_OK(varint.DecodeFromComparable(json_op.operand().value().varint_value()));
674
21
  int64_t array_index = VERIFY_RESULT(varint.ToInt64());
675
676
21
  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
19
  RETURN_NOT_OK(GetArrayElement(array_index, jsonb, sizeof(jsonb_header),
682
19
                                ComputeDataOffset(num_array_entries, kJBArray), result,
683
19
                                element_metadata));
684
19
  return Status::OK();
685
19
}
686
687
Status Jsonb::ApplyJsonbOperatorToObject(const Slice& jsonb, const QLJsonOperationPB& json_op,
688
                                         const JsonbHeader& jsonb_header,
689
26.8k
                                         Slice* result, JEntry* element_metadata) {
690
26.8k
  if (!json_op.operand().value().has_string_value()) {
691
6
    return STATUS_SUBSTITUTE(NotFound, "Couldn't apply json operator");
692
6
  }
693
694
26.8k
  size_t num_kv_pairs = GetCount(jsonb_header);
695
26.8k
  const string& search_key = json_op.operand().value().string_value();
696
697
26.8k
  size_t metadata_begin_offset = sizeof(jsonb_header);
698
26.8k
  size_t data_begin_offset = ComputeDataOffset(num_kv_pairs, kJBObject);
699
700
  // Binary search to find the key.
701
26.8k
  int64_t low = 0, high = num_kv_pairs - 1;
702
26.8k
  auto search_key_slice = Slice(search_key);
703
33.6k
  while (low <= high) {
704
33.5k
    size_t mid = low + (high - low)/2;
705
33.5k
    Slice mid_key;
706
33.5k
    RETURN_NOT_OK(GetObjectKey(mid, jsonb, metadata_begin_offset, data_begin_offset, &mid_key));
707
708
33.5k
    if (mid_key == search_key_slice) {
709
26.7k
      RETURN_NOT_OK(GetObjectValue(mid, jsonb, sizeof(jsonb_header),
710
26.7k
                                   ComputeDataOffset(num_kv_pairs, kJBObject), num_kv_pairs,
711
26.7k
                                   result, element_metadata));
712
26.7k
      return Status::OK();
713
26.7k
    } else 
if (6.78k
mid_key.ToBuffer() > search_key6.78k
) {
714
503
      high = mid - 1;
715
6.28k
    } else {
716
6.28k
      low = mid + 1;
717
6.28k
    }
718
33.5k
  }
719
90
  return STATUS_SUBSTITUTE(NotFound, "Couldn't find key $0 in json document", search_key);
720
26.8k
}
721
722
18.0k
Status Jsonb::ApplyJsonbOperators(const QLJsonColumnOperationsPB& json_ops, QLValue* result) const {
723
18.0k
  const int num_ops = json_ops.json_operations().size();
724
725
18.0k
  Slice jsonop_result;
726
18.0k
  Slice operand(serialized_jsonb_);
727
18.0k
  JEntry element_metadata;
728
44.3k
  for (int i = 0; i < num_ops; 
i++26.2k
) {
729
27.4k
    const QLJsonOperationPB &op = json_ops.json_operations().Get(i);
730
27.4k
    const Status s = ApplyJsonbOperator(operand, op, &jsonop_result,
731
27.4k
                                        &element_metadata);
732
27.4k
    if (s.IsNotFound()) {
733
      // We couldn't apply the operator to the operand and hence return null as the result.
734
681
      result->SetNull();
735
681
      return Status::OK();
736
681
    }
737
26.7k
    RETURN_NOT_OK(s);
738
739
26.7k
    if (IsScalar(element_metadata) && 
i != num_ops - 117.0k
) {
740
      // We have to apply another operation after this, but we received a scalar intermediate
741
      // result.
742
511
      result->SetNull();
743
511
      return Status::OK();
744
511
    }
745
26.2k
    operand = jsonop_result;
746
26.2k
  }
747
748
  // In case of '->>', we need to return a string result.
749
16.8k
  if (num_ops > 0 &&
750
16.8k
      json_ops.json_operations().Get(num_ops - 1).json_operator() == JsonOperatorPB::JSON_TEXT) {
751
16.7k
    if (IsScalar(element_metadata)) {
752
16.5k
      RETURN_NOT_OK(ScalarToString(element_metadata, jsonop_result,
753
16.5k
                                   result->mutable_string_value()));
754
16.5k
    } 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.7k
    return Status::OK();
760
16.7k
  }
761
762
61
  string jsonb_result = jsonop_result.ToBuffer();
763
61
  if (IsScalar(element_metadata)) {
764
    // In case of a scalar that is received from an operation, convert it to a jsonb scalar.
765
54
    RETURN_NOT_OK(CreateScalar(jsonop_result,
766
54
                               element_metadata,
767
54
                               &jsonb_result));
768
54
  }
769
61
  result->set_jsonb_value(std::move(jsonb_result));
770
61
  return Status::OK();
771
61
}
772
773
Status Jsonb::ApplyJsonbOperator(const Slice& jsonb, const QLJsonOperationPB& json_op,
774
27.4k
                                 Slice* result, JEntry* element_metadata) {
775
  // Currently, both these operators are considered the same since we only handle strings.
776
27.4k
  DCHECK(json_op.json_operator() == JsonOperatorPB::JSON_OBJECT ||
777
27.4k
         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.4k
  DCHECK(json_op.operand().has_value());
781
782
27.4k
  if (jsonb.size() < sizeof(JsonbHeader)) {
783
0
    return STATUS(InvalidArgument, "Not enough data to process");
784
0
  }
785
786
27.4k
  JsonbHeader jsonb_header = BigEndian::Load32(jsonb.data());
787
27.4k
  if ((jsonb_header & kJBScalar) && 
(jsonb_header & kJBArray)580
) {
788
    // This is a scalar value and no operators can be applied to it.
789
580
    return STATUS(NotFound, "Cannot apply operators to scalar values");
790
26.8k
  } else if (jsonb_header & kJBArray) {
791
24
    return ApplyJsonbOperatorToArray(jsonb, json_op, jsonb_header, result, element_metadata);
792
26.8k
  } else if (jsonb_header & kJBObject) {
793
26.8k
    return ApplyJsonbOperatorToObject(jsonb, json_op, jsonb_header, result, element_metadata);
794
26.8k
  }
795
796
0
  return STATUS(InvalidArgument, "Invalid json operation");
797
27.4k
}
798
799
Status Jsonb::CreateScalar(const Slice& scalar, const JEntry& original_jentry,
800
54
                           std::string* scalar_jsonb) {
801
  // Build the header.
802
54
  size_t metadata_begin_offset = sizeof(JsonbHeader);
803
54
  size_t metadata_size = metadata_begin_offset + sizeof(JEntry);
804
54
  size_t data_begin_offset = metadata_size;
805
806
  // Resize the result.
807
54
  scalar_jsonb->resize(metadata_size);
808
54
  scalar_jsonb->append(scalar.cdata(), scalar.size());
809
810
54
  JsonbHeader jsonb_header = (1 & kJBCountMask) | kJBArray | kJBScalar;
811
54
  JEntry jentry = GetOffset(narrow_cast<JEntry>(scalar_jsonb->size() - data_begin_offset))
812
54
                  | GetJEType(original_jentry);
813
814
  // Store the header.
815
54
  BigEndian::Store32(&((*scalar_jsonb)[0]), jsonb_header);
816
  // Store the JEntry.
817
54
  BigEndian::Store32(&((*scalar_jsonb)[metadata_begin_offset]), jentry);
818
54
  return Status::OK();
819
54
}
820
821
} // namespace common
822
} // namespace yb