Coverage Report

Created: 2025-10-26 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/db/merge_operator.cc
Line
Count
Source
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under both the GPLv2 (found in the
3
//  COPYING file in the root directory) and Apache 2.0 License
4
//  (found in the LICENSE.Apache file in the root directory).
5
//
6
/**
7
 * Back-end implementation details specific to the Merge Operator.
8
 */
9
10
#include "rocksdb/merge_operator.h"
11
12
#include <type_traits>
13
14
#include "db/wide/wide_columns_helper.h"
15
#include "util/overload.h"
16
17
namespace ROCKSDB_NAMESPACE {
18
19
bool MergeOperator::FullMergeV2(const MergeOperationInput& merge_in,
20
0
                                MergeOperationOutput* merge_out) const {
21
  // If FullMergeV2 is not implemented, we convert the operand_list to
22
  // std::deque<std::string> and pass it to FullMerge
23
0
  std::deque<std::string> operand_list_str;
24
0
  for (auto& op : merge_in.operand_list) {
25
0
    operand_list_str.emplace_back(op.data(), op.size());
26
0
  }
27
0
  return FullMerge(merge_in.key, merge_in.existing_value, operand_list_str,
28
0
                   &merge_out->new_value, merge_in.logger);
29
0
}
30
31
bool MergeOperator::FullMergeV3(const MergeOperationInputV3& merge_in,
32
0
                                MergeOperationOutputV3* merge_out) const {
33
0
  assert(merge_out);
34
35
0
  Slice value_of_default;  // avoid warning about in_v2 pointing at this
36
0
  MergeOperationInput in_v2(merge_in.key, nullptr, merge_in.operand_list,
37
0
                            merge_in.logger);
38
39
0
  std::string new_value;
40
0
  Slice existing_operand(nullptr, 0);
41
0
  MergeOperationOutput out_v2(new_value, existing_operand);
42
43
0
  return std::visit(
44
0
      overload{
45
0
          [&](const auto& existing) -> bool {
46
0
            using T = std::decay_t<decltype(existing)>;
47
48
0
            if constexpr (std::is_same_v<T, Slice>) {
49
0
              in_v2.existing_value = &existing;
50
0
            }
51
52
0
            const bool result = FullMergeV2(in_v2, &out_v2);
53
0
            if (!result) {
54
0
              merge_out->op_failure_scope = out_v2.op_failure_scope;
55
0
              return false;
56
0
            }
57
58
0
            if (existing_operand.data()) {
59
0
              merge_out->new_value = existing_operand;
60
0
            } else {
61
0
              merge_out->new_value = std::move(new_value);
62
0
            }
63
64
0
            return true;
65
0
          },
Unexecuted instantiation: merge_operator.cc:bool rocksdb::MergeOperator::FullMergeV3(rocksdb::MergeOperator::MergeOperationInputV3 const&, rocksdb::MergeOperator::MergeOperationOutputV3*) const::$_0::operator()<std::__1::monostate>(std::__1::monostate const&) const
Unexecuted instantiation: merge_operator.cc:bool rocksdb::MergeOperator::FullMergeV3(rocksdb::MergeOperator::MergeOperationInputV3 const&, rocksdb::MergeOperator::MergeOperationOutputV3*) const::$_0::operator()<rocksdb::Slice>(rocksdb::Slice const&) const
66
0
          [&](const WideColumns& existing_columns) -> bool {
67
0
            const bool has_default_column =
68
0
                WideColumnsHelper::HasDefaultColumn(existing_columns);
69
70
0
            if (has_default_column) {
71
0
              value_of_default = existing_columns.front().value();
72
0
            }
73
74
0
            in_v2.existing_value = &value_of_default;
75
76
0
            const bool result = FullMergeV2(in_v2, &out_v2);
77
0
            if (!result) {
78
0
              merge_out->op_failure_scope = out_v2.op_failure_scope;
79
0
              return false;
80
0
            }
81
82
0
            merge_out->new_value = MergeOperationOutputV3::NewColumns();
83
0
            auto& new_columns = std::get<MergeOperationOutputV3::NewColumns>(
84
0
                merge_out->new_value);
85
0
            new_columns.reserve(has_default_column
86
0
                                    ? existing_columns.size()
87
0
                                    : (existing_columns.size() + 1));
88
89
0
            if (existing_operand.data()) {
90
0
              new_columns.emplace_back(kDefaultWideColumnName.ToString(),
91
0
                                       existing_operand.ToString());
92
0
            } else {
93
0
              new_columns.emplace_back(kDefaultWideColumnName.ToString(),
94
0
                                       std::move(new_value));
95
0
            }
96
97
0
            for (size_t i = has_default_column ? 1 : 0;
98
0
                 i < existing_columns.size(); ++i) {
99
0
              new_columns.emplace_back(existing_columns[i].name().ToString(),
100
0
                                       existing_columns[i].value().ToString());
101
0
            }
102
103
0
            return true;
104
0
          }},
105
0
      merge_in.existing_value);
106
0
}
107
108
// The default implementation of PartialMergeMulti, which invokes
109
// PartialMerge multiple times internally and merges two operands at
110
// a time.
111
bool MergeOperator::PartialMergeMulti(const Slice& key,
112
                                      const std::deque<Slice>& operand_list,
113
                                      std::string* new_value,
114
0
                                      Logger* logger) const {
115
0
  assert(operand_list.size() >= 2);
116
  // Simply loop through the operands
117
0
  Slice temp_slice(operand_list[0]);
118
119
0
  for (size_t i = 1; i < operand_list.size(); ++i) {
120
0
    auto& operand = operand_list[i];
121
0
    std::string temp_value;
122
0
    if (!PartialMerge(key, temp_slice, operand, &temp_value, logger)) {
123
0
      return false;
124
0
    }
125
0
    swap(temp_value, *new_value);
126
0
    temp_slice = Slice(*new_value);
127
0
  }
128
129
  // The result will be in *new_value. All merges succeeded.
130
0
  return true;
131
0
}
132
133
// Given a "real" merge from the library, call the user's
134
// associative merge function one-by-one on each of the operands.
135
// NOTE: It is assumed that the client's merge-operator will handle any errors.
136
bool AssociativeMergeOperator::FullMergeV2(
137
    const MergeOperationInput& merge_in,
138
0
    MergeOperationOutput* merge_out) const {
139
  // Simply loop through the operands
140
0
  Slice temp_existing;
141
0
  const Slice* existing_value = merge_in.existing_value;
142
0
  for (const auto& operand : merge_in.operand_list) {
143
0
    std::string temp_value;
144
0
    if (!Merge(merge_in.key, existing_value, operand, &temp_value,
145
0
               merge_in.logger)) {
146
0
      return false;
147
0
    }
148
0
    swap(temp_value, merge_out->new_value);
149
0
    temp_existing = Slice(merge_out->new_value);
150
0
    existing_value = &temp_existing;
151
0
  }
152
153
  // The result will be in *new_value. All merges succeeded.
154
0
  return true;
155
0
}
156
157
// Call the user defined simple merge on the operands;
158
// NOTE: It is assumed that the client's merge-operator will handle any errors.
159
bool AssociativeMergeOperator::PartialMerge(const Slice& key,
160
                                            const Slice& left_operand,
161
                                            const Slice& right_operand,
162
                                            std::string* new_value,
163
0
                                            Logger* logger) const {
164
0
  return Merge(key, &left_operand, right_operand, new_value, logger);
165
0
}
166
167
}  // namespace ROCKSDB_NAMESPACE