/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 |