@nxtedition/rocksdb 7.1.34 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/BUILDING.md CHANGED
@@ -10,9 +10,9 @@
10
10
  - Copy `libfolly.a` to `/usr/lib/x86_64-linux-gnu`.
11
11
  - Copy headers to `/usr/lib/x86_64-linux-gnu/include`.
12
12
  - Copy boost headers from folly scratchpad to `/usr/lib/x86_64-linux-gnu/include`.
13
- - `npx prebuildify -t -t 17.8.0 --napi --strip --arch x64`
13
+ - `JOBS=8 npx prebuildify -t 18.11.0 --napi --strip --arch x64`
14
14
 
15
15
  # OSX
16
16
 
17
17
  - `brew install zstd`
18
- - `npx prebuildify -t -t 17.8.0 --napi --strip --arch x64`
18
+ - `JOBS=8 npx prebuildify -t 18.11.0 --napi --strip --arch arm64`
package/binding.cc CHANGED
@@ -26,7 +26,6 @@
26
26
  #include <thread>
27
27
  #include <vector>
28
28
 
29
- #include "max_rev_operator.h"
30
29
  #include "util.h"
31
30
 
32
31
  class NullLogger : public rocksdb::Logger {
@@ -38,7 +37,6 @@ class NullLogger : public rocksdb::Logger {
38
37
 
39
38
  struct Database;
40
39
  struct Iterator;
41
- struct Updates;
42
40
 
43
41
  struct ColumnFamily {
44
42
  napi_ref ref;
@@ -254,48 +252,6 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
254
252
  std::vector<BatchEntry> cache_;
255
253
  };
256
254
 
257
- struct Updates : public BatchIterator, public Closable {
258
- Updates(Database* database,
259
- const int64_t seqNumber,
260
- const bool keys,
261
- const bool values,
262
- const bool data,
263
- const rocksdb::ColumnFamilyHandle* column,
264
- const Encoding keyEncoding,
265
- const Encoding valueEncoding)
266
- : BatchIterator(database, keys, values, data, column, keyEncoding, valueEncoding),
267
- database_(database),
268
- start_(seqNumber) {}
269
-
270
- virtual ~Updates() { assert(!iterator_); }
271
-
272
- rocksdb::Status Close() override {
273
- iterator_.reset();
274
- return rocksdb::Status::OK();
275
- }
276
-
277
- napi_status Attach(napi_env env, napi_value context) {
278
- NAPI_STATUS_RETURN(napi_create_reference(env, context, 1, &ref_));
279
- database_->closables.insert(this);
280
- return napi_ok;
281
- }
282
-
283
- napi_status Detach(napi_env env) {
284
- database_->closables.erase(this);
285
- if (ref_) {
286
- NAPI_STATUS_RETURN(napi_delete_reference(env, ref_));
287
- }
288
- return napi_ok;
289
- }
290
-
291
- Database* database_;
292
- int64_t start_;
293
- std::unique_ptr<rocksdb::TransactionLogIterator> iterator_;
294
-
295
- private:
296
- napi_ref ref_ = nullptr;
297
- };
298
-
299
255
  struct BaseIterator : public Closable {
300
256
  BaseIterator(Database* database,
301
257
  rocksdb::ColumnFamilyHandle* column,
@@ -600,12 +556,8 @@ napi_status InitOptions(napi_env env, T& columnOptions, const U& options) {
600
556
  std::optional<std::string> mergeOperatorOpt;
601
557
  NAPI_STATUS_RETURN(GetProperty(env, options, "mergeOperator", mergeOperatorOpt));
602
558
  if (mergeOperatorOpt) {
603
- if (*mergeOperatorOpt == "maxRev") {
604
- columnOptions.merge_operator = std::make_shared<MaxRevOperator>();
605
- } else {
606
- ROCKS_STATUS_RETURN_NAPI(
607
- rocksdb::MergeOperator::CreateFromString(configOptions, *mergeOperatorOpt, &columnOptions.merge_operator));
608
- }
559
+ ROCKS_STATUS_RETURN_NAPI(
560
+ rocksdb::MergeOperator::CreateFromString(configOptions, *mergeOperatorOpt, &columnOptions.merge_operator));
609
561
  }
610
562
 
611
563
  std::optional<std::string> compactionPriority;
@@ -850,106 +802,6 @@ NAPI_METHOD(db_close) {
850
802
  return 0;
851
803
  }
852
804
 
853
- NAPI_METHOD(updates_init) {
854
- NAPI_ARGV(2);
855
-
856
- Database* database;
857
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
858
-
859
- const auto options = argv[1];
860
-
861
- int64_t since = 0;
862
- NAPI_STATUS_THROWS(GetProperty(env, options, "since", since));
863
-
864
- bool keys = true;
865
- NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
866
-
867
- bool values = true;
868
- NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
869
-
870
- bool data = true;
871
- NAPI_STATUS_THROWS(GetProperty(env, options, "data", data));
872
-
873
- Encoding keyEncoding = Encoding::String;
874
- NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
875
-
876
- Encoding valueEncoding = Encoding::String;
877
- NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
878
-
879
- rocksdb::ColumnFamilyHandle* column = nullptr;
880
- NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
881
-
882
- auto updates =
883
- std::unique_ptr<Updates>(new Updates(database, since, keys, values, data, column, keyEncoding, valueEncoding));
884
-
885
- napi_value result;
886
- NAPI_STATUS_THROWS(napi_create_external(env, updates.get(), Finalize<Updates>, updates.get(), &result));
887
-
888
- // Prevent GC of JS object before the iterator is closed (explicitly or on
889
- // db close) and keep track of non-closed iterators to end them on db close.
890
- NAPI_STATUS_THROWS(updates.release()->Attach(env, result));
891
-
892
- return result;
893
- }
894
-
895
- NAPI_METHOD(updates_next) {
896
- NAPI_ARGV(2);
897
-
898
- Updates* updates;
899
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&updates)));
900
-
901
- auto callback = argv[1];
902
-
903
- runAsync<rocksdb::BatchResult>(
904
- "leveldown.updates.next", env, callback,
905
- [=](auto& batchResult) {
906
- if (!updates->iterator_) {
907
- rocksdb::TransactionLogIterator::ReadOptions options;
908
- const auto status = updates->database_->db->GetUpdatesSince(updates->start_, &updates->iterator_, options);
909
- if (!status.ok()) {
910
- return status;
911
- }
912
- } else if (updates->iterator_->Valid()) {
913
- updates->iterator_->Next();
914
- }
915
-
916
- if (!updates->iterator_->Valid() || !updates->iterator_->status().ok()) {
917
- return updates->iterator_->status();
918
- }
919
-
920
- batchResult = updates->iterator_->GetBatch();
921
-
922
- return rocksdb::Status::OK();
923
- },
924
- [=](auto& batchResult, auto env, auto& argv) {
925
- if (!batchResult.writeBatchPtr) {
926
- return napi_ok;
927
- }
928
-
929
- argv.resize(5);
930
- NAPI_STATUS_RETURN(updates->Iterate(env, *batchResult.writeBatchPtr, &argv[1]));
931
- NAPI_STATUS_RETURN(napi_create_int64(env, batchResult.sequence, &argv[2]));
932
- NAPI_STATUS_RETURN(napi_create_int64(env, batchResult.writeBatchPtr->Count(), &argv[3]));
933
- NAPI_STATUS_RETURN(napi_create_int64(env, updates->start_, &argv[4]));
934
-
935
- return napi_ok;
936
- });
937
-
938
- return 0;
939
- }
940
-
941
- NAPI_METHOD(updates_close) {
942
- NAPI_ARGV(1);
943
-
944
- Updates* updates;
945
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&updates)));
946
-
947
- ROCKS_STATUS_THROWS_NAPI(updates->Close());
948
- NAPI_STATUS_THROWS(updates->Detach(env));
949
-
950
- return 0;
951
- }
952
-
953
805
  NAPI_METHOD(db_get_many) {
954
806
  NAPI_ARGV(4);
955
807
 
@@ -1731,10 +1583,6 @@ NAPI_INIT() {
1731
1583
  NAPI_EXPORT_FUNCTION(iterator_nextv);
1732
1584
  NAPI_EXPORT_FUNCTION(iterator_get_sequence);
1733
1585
 
1734
- NAPI_EXPORT_FUNCTION(updates_init);
1735
- NAPI_EXPORT_FUNCTION(updates_close);
1736
- NAPI_EXPORT_FUNCTION(updates_next);
1737
-
1738
1586
  NAPI_EXPORT_FUNCTION(batch_do);
1739
1587
  NAPI_EXPORT_FUNCTION(batch_init);
1740
1588
  NAPI_EXPORT_FUNCTION(batch_put);
@@ -250,6 +250,8 @@ option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
250
250
  option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
251
251
  option(FORCE_AVX2 "force building with AVX2, even when PORTABLE=ON" OFF)
252
252
  if(PORTABLE)
253
+ add_definitions(-DROCKSDB_PORTABLE)
254
+
253
255
  # MSVC does not need a separate compiler flag to enable SSE4.2; if nmmintrin.h
254
256
  # is available, it is available by default.
255
257
  if(FORCE_SSE42 AND NOT MSVC)
@@ -292,8 +294,7 @@ if(NOT MSVC)
292
294
  set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
293
295
  endif()
294
296
 
295
- if (NOT PORTABLE OR FORCE_SSE42)
296
- CHECK_CXX_SOURCE_COMPILES("
297
+ CHECK_CXX_SOURCE_COMPILES("
297
298
  #include <cstdint>
298
299
  #include <nmmintrin.h>
299
300
  #include <wmmintrin.h>
@@ -305,12 +306,11 @@ int main() {
305
306
  auto d = _mm_cvtsi128_si64(c);
306
307
  }
307
308
  " HAVE_SSE42)
308
- if(HAVE_SSE42)
309
- add_definitions(-DHAVE_SSE42)
310
- add_definitions(-DHAVE_PCLMUL)
311
- elseif(FORCE_SSE42)
312
- message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
313
- endif()
309
+ if(HAVE_SSE42)
310
+ add_definitions(-DHAVE_SSE42)
311
+ add_definitions(-DHAVE_PCLMUL)
312
+ elseif(FORCE_SSE42)
313
+ message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
314
314
  endif()
315
315
 
316
316
  # Check if -latomic is required or not
@@ -1888,6 +1888,43 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheFailureTest) {
1888
1888
  Destroy(options);
1889
1889
  }
1890
1890
 
1891
+ TEST_F(DBSecondaryCacheTest, TestSecondaryWithCompressedCache) {
1892
+ if (!Snappy_Supported()) {
1893
+ ROCKSDB_GTEST_SKIP("Compressed cache test requires snappy support");
1894
+ return;
1895
+ }
1896
+ LRUCacheOptions opts(2000 /* capacity */, 0 /* num_shard_bits */,
1897
+ false /* strict_capacity_limit */,
1898
+ 0.5 /* high_pri_pool_ratio */,
1899
+ nullptr /* memory_allocator */, kDefaultToAdaptiveMutex,
1900
+ kDontChargeCacheMetadata);
1901
+ std::shared_ptr<TestSecondaryCache> secondary_cache(
1902
+ new TestSecondaryCache(2048 * 1024));
1903
+ opts.secondary_cache = secondary_cache;
1904
+ std::shared_ptr<Cache> cache = NewLRUCache(opts);
1905
+ BlockBasedTableOptions table_options;
1906
+ table_options.block_cache_compressed = cache;
1907
+ table_options.no_block_cache = true;
1908
+ table_options.block_size = 1234;
1909
+ Options options = GetDefaultOptions();
1910
+ options.compression = kSnappyCompression;
1911
+ options.create_if_missing = true;
1912
+ options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1913
+ DestroyAndReopen(options);
1914
+ Random rnd(301);
1915
+ const int N = 6;
1916
+ for (int i = 0; i < N; i++) {
1917
+ // Partly compressible
1918
+ std::string p_v = rnd.RandomString(507) + std::string(500, ' ');
1919
+ ASSERT_OK(Put(Key(i), p_v));
1920
+ }
1921
+ ASSERT_OK(Flush());
1922
+ for (int i = 0; i < 2 * N; i++) {
1923
+ std::string v = Get(Key(i % N));
1924
+ ASSERT_EQ(1007, v.size());
1925
+ }
1926
+ }
1927
+
1891
1928
  TEST_F(LRUCacheSecondaryCacheTest, BasicWaitAllTest) {
1892
1929
  LRUCacheOptions opts(1024 /* capacity */, 2 /* num_shard_bits */,
1893
1930
  false /* strict_capacity_limit */,
@@ -158,6 +158,8 @@ void DBIter::Next() {
158
158
 
159
159
  local_stats_.next_count_++;
160
160
  if (ok && iter_.Valid()) {
161
+ ClearSavedValue();
162
+
161
163
  if (prefix_same_as_start_) {
162
164
  assert(prefix_extractor_ != nullptr);
163
165
  const Slice prefix = prefix_.GetUserKey();
@@ -544,8 +546,7 @@ bool DBIter::MergeValuesNewToOld() {
544
546
  // hit a put, merge the put value with operands and store the
545
547
  // final result in saved_value_. We are done!
546
548
  const Slice val = iter_.value();
547
- Status s = Merge(&val, ikey.user_key);
548
- if (!s.ok()) {
549
+ if (!Merge(&val, ikey.user_key)) {
549
550
  return false;
550
551
  }
551
552
  // iter_ is positioned after put
@@ -574,8 +575,7 @@ bool DBIter::MergeValuesNewToOld() {
574
575
  return false;
575
576
  }
576
577
  valid_ = true;
577
- Status s = Merge(&blob_value_, ikey.user_key);
578
- if (!s.ok()) {
578
+ if (!Merge(&blob_value_, ikey.user_key)) {
579
579
  return false;
580
580
  }
581
581
 
@@ -589,11 +589,18 @@ bool DBIter::MergeValuesNewToOld() {
589
589
  }
590
590
  return true;
591
591
  } else if (kTypeWideColumnEntity == ikey.type) {
592
- // TODO: support wide-column entities
593
- status_ = Status::NotSupported(
594
- "Merge currently not supported for wide-column entities");
595
- valid_ = false;
596
- return false;
592
+ if (!MergeEntity(iter_.value(), ikey.user_key)) {
593
+ return false;
594
+ }
595
+
596
+ // iter_ is positioned after put
597
+ iter_.Next();
598
+ if (!iter_.status().ok()) {
599
+ valid_ = false;
600
+ return false;
601
+ }
602
+
603
+ return true;
597
604
  } else {
598
605
  valid_ = false;
599
606
  status_ = Status::Corruption(
@@ -612,8 +619,7 @@ bool DBIter::MergeValuesNewToOld() {
612
619
  // a deletion marker.
613
620
  // feed null as the existing value to the merge operator, such that
614
621
  // client can differentiate this scenario and do things accordingly.
615
- Status s = Merge(nullptr, saved_key_.GetUserKey());
616
- if (!s.ok()) {
622
+ if (!Merge(nullptr, saved_key_.GetUserKey())) {
617
623
  return false;
618
624
  }
619
625
  assert(status_.ok());
@@ -636,6 +642,8 @@ void DBIter::Prev() {
636
642
  }
637
643
  }
638
644
  if (ok) {
645
+ ClearSavedValue();
646
+
639
647
  Slice prefix;
640
648
  if (prefix_same_as_start_) {
641
649
  assert(prefix_extractor_ != nullptr);
@@ -960,8 +968,7 @@ bool DBIter::FindValueForCurrentKey() {
960
968
  if (last_not_merge_type == kTypeDeletion ||
961
969
  last_not_merge_type == kTypeSingleDeletion ||
962
970
  last_not_merge_type == kTypeDeletionWithTimestamp) {
963
- s = Merge(nullptr, saved_key_.GetUserKey());
964
- if (!s.ok()) {
971
+ if (!Merge(nullptr, saved_key_.GetUserKey())) {
965
972
  return false;
966
973
  }
967
974
  return true;
@@ -976,8 +983,7 @@ bool DBIter::FindValueForCurrentKey() {
976
983
  return false;
977
984
  }
978
985
  valid_ = true;
979
- s = Merge(&blob_value_, saved_key_.GetUserKey());
980
- if (!s.ok()) {
986
+ if (!Merge(&blob_value_, saved_key_.GetUserKey())) {
981
987
  return false;
982
988
  }
983
989
 
@@ -985,15 +991,14 @@ bool DBIter::FindValueForCurrentKey() {
985
991
 
986
992
  return true;
987
993
  } else if (last_not_merge_type == kTypeWideColumnEntity) {
988
- // TODO: support wide-column entities
989
- status_ = Status::NotSupported(
990
- "Merge currently not supported for wide-column entities");
991
- valid_ = false;
992
- return false;
994
+ if (!MergeEntity(pinned_value_, saved_key_.GetUserKey())) {
995
+ return false;
996
+ }
997
+
998
+ return true;
993
999
  } else {
994
1000
  assert(last_not_merge_type == kTypeValue);
995
- s = Merge(&pinned_value_, saved_key_.GetUserKey());
996
- if (!s.ok()) {
1001
+ if (!Merge(&pinned_value_, saved_key_.GetUserKey())) {
997
1002
  return false;
998
1003
  }
999
1004
  return true;
@@ -1177,8 +1182,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
1177
1182
 
1178
1183
  if (ikey.type == kTypeValue) {
1179
1184
  const Slice val = iter_.value();
1180
- Status s = Merge(&val, saved_key_.GetUserKey());
1181
- if (!s.ok()) {
1185
+ if (!Merge(&val, saved_key_.GetUserKey())) {
1182
1186
  return false;
1183
1187
  }
1184
1188
  return true;
@@ -1197,8 +1201,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
1197
1201
  return false;
1198
1202
  }
1199
1203
  valid_ = true;
1200
- Status s = Merge(&blob_value_, saved_key_.GetUserKey());
1201
- if (!s.ok()) {
1204
+ if (!Merge(&blob_value_, saved_key_.GetUserKey())) {
1202
1205
  return false;
1203
1206
  }
1204
1207
 
@@ -1206,11 +1209,11 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
1206
1209
 
1207
1210
  return true;
1208
1211
  } else if (ikey.type == kTypeWideColumnEntity) {
1209
- // TODO: support wide-column entities
1210
- status_ = Status::NotSupported(
1211
- "Merge currently not supported for wide-column entities");
1212
- valid_ = false;
1213
- return false;
1212
+ if (!MergeEntity(iter_.value(), saved_key_.GetUserKey())) {
1213
+ return false;
1214
+ }
1215
+
1216
+ return true;
1214
1217
  } else {
1215
1218
  valid_ = false;
1216
1219
  status_ = Status::Corruption(
@@ -1220,8 +1223,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
1220
1223
  }
1221
1224
  }
1222
1225
 
1223
- Status s = Merge(nullptr, saved_key_.GetUserKey());
1224
- if (!s.ok()) {
1226
+ if (!Merge(nullptr, saved_key_.GetUserKey())) {
1225
1227
  return false;
1226
1228
  }
1227
1229
 
@@ -1244,7 +1246,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
1244
1246
  return true;
1245
1247
  }
1246
1248
 
1247
- Status DBIter::Merge(const Slice* val, const Slice& user_key) {
1249
+ bool DBIter::Merge(const Slice* val, const Slice& user_key) {
1248
1250
  Status s = MergeHelper::TimedFullMerge(
1249
1251
  merge_operator_, user_key, val, merge_context_.GetOperands(),
1250
1252
  &saved_value_, logger_, statistics_, clock_, &pinned_value_,
@@ -1252,14 +1254,33 @@ Status DBIter::Merge(const Slice* val, const Slice& user_key) {
1252
1254
  if (!s.ok()) {
1253
1255
  valid_ = false;
1254
1256
  status_ = s;
1255
- return s;
1257
+ return false;
1256
1258
  }
1257
1259
 
1258
1260
  SetValueAndColumnsFromPlain(pinned_value_.data() ? pinned_value_
1259
1261
  : saved_value_);
1260
1262
 
1261
1263
  valid_ = true;
1262
- return s;
1264
+ return true;
1265
+ }
1266
+
1267
+ bool DBIter::MergeEntity(const Slice& entity, const Slice& user_key) {
1268
+ Status s = MergeHelper::TimedFullMergeWithEntity(
1269
+ merge_operator_, user_key, entity, merge_context_.GetOperands(),
1270
+ &saved_value_, logger_, statistics_, clock_,
1271
+ /* update_num_ops_stats */ true);
1272
+ if (!s.ok()) {
1273
+ valid_ = false;
1274
+ status_ = s;
1275
+ return false;
1276
+ }
1277
+
1278
+ if (!SetValueAndColumnsFromEntity(saved_value_)) {
1279
+ return false;
1280
+ }
1281
+
1282
+ valid_ = true;
1283
+ return true;
1263
1284
  }
1264
1285
 
1265
1286
  // Move backwards until the key smaller than saved_key_.
@@ -318,7 +318,8 @@ class DBIter final : public Iterator {
318
318
  }
319
319
 
320
320
  // If user-defined timestamp is enabled, `user_key` includes timestamp.
321
- Status Merge(const Slice* val, const Slice& user_key);
321
+ bool Merge(const Slice* val, const Slice& user_key);
322
+ bool MergeEntity(const Slice& entity, const Slice& user_key);
322
323
 
323
324
  const SliceTransform* prefix_extractor_;
324
325
  Env* const env_;
@@ -1066,11 +1066,21 @@ static bool SaveValue(void* arg, const char* entry) {
1066
1066
  assert(s->do_merge);
1067
1067
 
1068
1068
  if (s->value || s->columns) {
1069
+ std::string result;
1069
1070
  *(s->status) = MergeHelper::TimedFullMerge(
1070
1071
  merge_operator, s->key->user_key(), &v,
1071
- merge_context->GetOperands(), s->value, s->columns, s->logger,
1072
- s->statistics, s->clock, /* result_operand */ nullptr,
1072
+ merge_context->GetOperands(), &result, s->logger, s->statistics,
1073
+ s->clock, /* result_operand */ nullptr,
1073
1074
  /* update_num_ops_stats */ true);
1075
+
1076
+ if (s->status->ok()) {
1077
+ if (s->value) {
1078
+ *(s->value) = std::move(result);
1079
+ } else {
1080
+ assert(s->columns);
1081
+ s->columns->SetPlainValue(result);
1082
+ }
1083
+ }
1074
1084
  }
1075
1085
  } else if (s->value) {
1076
1086
  s->value->assign(v.data(), v.size());
@@ -1115,11 +1125,27 @@ static bool SaveValue(void* arg, const char* entry) {
1115
1125
  } else if (*(s->merge_in_progress)) {
1116
1126
  assert(s->do_merge);
1117
1127
 
1118
- if (s->value || s->columns) {
1128
+ if (s->value) {
1129
+ Slice value_of_default;
1130
+ *(s->status) = WideColumnSerialization::GetValueOfDefaultColumn(
1131
+ v, value_of_default);
1132
+ if (s->status->ok()) {
1133
+ *(s->status) = MergeHelper::TimedFullMerge(
1134
+ merge_operator, s->key->user_key(), &value_of_default,
1135
+ merge_context->GetOperands(), s->value, s->logger,
1136
+ s->statistics, s->clock, /* result_operand */ nullptr,
1137
+ /* update_num_ops_stats */ true);
1138
+ }
1139
+ } else if (s->columns) {
1140
+ std::string result;
1119
1141
  *(s->status) = MergeHelper::TimedFullMergeWithEntity(
1120
1142
  merge_operator, s->key->user_key(), v,
1121
- merge_context->GetOperands(), s->value, s->columns, s->logger,
1122
- s->statistics, s->clock, /* update_num_ops_stats */ true);
1143
+ merge_context->GetOperands(), &result, s->logger, s->statistics,
1144
+ s->clock, /* update_num_ops_stats */ true);
1145
+
1146
+ if (s->status->ok()) {
1147
+ *(s->status) = s->columns->SetWideColumnValue(result);
1148
+ }
1123
1149
  }
1124
1150
  } else if (s->value) {
1125
1151
  Slice value_of_default;
@@ -1150,11 +1176,21 @@ static bool SaveValue(void* arg, const char* entry) {
1150
1176
  case kTypeRangeDeletion: {
1151
1177
  if (*(s->merge_in_progress)) {
1152
1178
  if (s->value || s->columns) {
1179
+ std::string result;
1153
1180
  *(s->status) = MergeHelper::TimedFullMerge(
1154
1181
  merge_operator, s->key->user_key(), nullptr,
1155
- merge_context->GetOperands(), s->value, s->columns, s->logger,
1156
- s->statistics, s->clock, /* result_operand */ nullptr,
1182
+ merge_context->GetOperands(), &result, s->logger, s->statistics,
1183
+ s->clock, /* result_operand */ nullptr,
1157
1184
  /* update_num_ops_stats */ true);
1185
+
1186
+ if (s->status->ok()) {
1187
+ if (s->value) {
1188
+ *(s->value) = std::move(result);
1189
+ } else {
1190
+ assert(s->columns);
1191
+ s->columns->SetPlainValue(result);
1192
+ }
1193
+ }
1158
1194
  }
1159
1195
  } else {
1160
1196
  *(s->status) = Status::NotFound();
@@ -1180,11 +1216,21 @@ static bool SaveValue(void* arg, const char* entry) {
1180
1216
  if (s->do_merge && merge_operator->ShouldMerge(
1181
1217
  merge_context->GetOperandsDirectionBackward())) {
1182
1218
  if (s->value || s->columns) {
1219
+ std::string result;
1183
1220
  *(s->status) = MergeHelper::TimedFullMerge(
1184
1221
  merge_operator, s->key->user_key(), nullptr,
1185
- merge_context->GetOperands(), s->value, s->columns, s->logger,
1186
- s->statistics, s->clock, /* result_operand */ nullptr,
1222
+ merge_context->GetOperands(), &result, s->logger, s->statistics,
1223
+ s->clock, /* result_operand */ nullptr,
1187
1224
  /* update_num_ops_stats */ true);
1225
+
1226
+ if (s->status->ok()) {
1227
+ if (s->value) {
1228
+ *(s->value) = std::move(result);
1229
+ } else {
1230
+ assert(s->columns);
1231
+ s->columns->SetPlainValue(result);
1232
+ }
1233
+ }
1188
1234
  }
1189
1235
 
1190
1236
  *(s->found_final_value) = true;