fix: json.merge exception crash (#3409)

json.merge would throw an exception when the json object did not contain the element to replace because RecursiveMerge functions used &dest->at(k_v.key()) which threw the exception. Remove RecursiveMerge completely and use the one implemented in jsoncons lib.

* add test
* replace RecursiveMerge with mergepatch::apply_merge_patch
* add exception handling for that flow

---------

Signed-off-by: kostas <kostas@dragonflydb.io>
This commit is contained in:
Kostas Kyrimis 2024-07-31 17:03:36 +03:00 committed by GitHub
parent 558a22d5b8
commit 7e911c100a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 16 additions and 23 deletions

View file

@ -13,6 +13,7 @@
#include <jsoncons_ext/jsonpatch/jsonpatch.hpp>
#include <jsoncons_ext/jsonpath/jsonpath.hpp>
#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
#include <jsoncons_ext/mergepatch/mergepatch.hpp>
#include "base/flags.h"
#include "base/logging.h"
@ -1450,28 +1451,6 @@ OpStatus OpMSet(const OpArgs& op_args, const ShardArgs& args) {
return result;
}
// Implements the recursive algorithm from
// https://datatracker.ietf.org/doc/html/rfc7386#section-2
void RecursiveMerge(const JsonType& patch, JsonType* dest) {
if (!patch.is_object()) {
*dest = patch;
return;
}
if (!dest->is_object()) {
*dest = JsonType(json_object_arg, dest->get_allocator());
}
for (const auto& k_v : patch.object_range()) {
if (k_v.value().is_null()) {
dest->erase(k_v.key());
} else {
RecursiveMerge(k_v.value(), &dest->at(k_v.key()));
}
}
}
OpStatus OpMerge(const OpArgs& op_args, string_view key, std::string_view json_str) {
std::optional<JsonType> parsed_json = JsonFromString(json_str);
if (!parsed_json) {
@ -1485,7 +1464,14 @@ OpStatus OpMerge(const OpArgs& op_args, string_view key, std::string_view json_s
op_args.shard->search_indices()->RemoveDoc(key, op_args.db_cntx, it_res->it->second);
JsonType* obj = it_res->it->second.GetJson();
RecursiveMerge(*parsed_json, obj);
try {
// https://datatracker.ietf.org/doc/html/rfc7386#section-2
mergepatch::apply_merge_patch(*obj, *parsed_json);
} catch (const std::exception& e) {
LOG_EVERY_T(ERROR, 1) << "Exception in OpMerge: " << e.what() << " with obj: " << *obj
<< " and patch: " << *parsed_json;
return OpStatus::INVALID_VALUE;
}
it_res->post_updater.Run();
op_args.shard->search_indices()->AddDoc(key, op_args.db_cntx, it_res->it->second);
return OpStatus::OK;