diff --git a/.gitignore b/.gitignore index 7f53a20..990f3bf 100644 --- a/.gitignore +++ b/.gitignore @@ -287,3 +287,4 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs +.DS_Store diff --git a/Tests/CustomMergeOperatorTests.cs b/Tests/CustomMergeOperatorTests.cs new file mode 100644 index 0000000..b6bf83f --- /dev/null +++ b/Tests/CustomMergeOperatorTests.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RocksDbSharp; + +namespace Tests; + +[TestClass] +public class CustomMergeOperatorTests +{ + [TestMethod] + public void CustomMergeOperatorAppendsMultipleValues() + { + var mergeOp = MergeOperators.Create( + "StringAppend", + (ReadOnlySpan key, MergeOperators.OperandsEnumerator operands, out bool success) => + { + var result = new List(); + for (int i = 0; i < operands.Count; i++) + { + result.AddRange(operands.Get(i).ToArray()); + } + success = true; + return result.ToArray(); + }, + (ReadOnlySpan key, bool hasExisting, ReadOnlySpan existing, + MergeOperators.OperandsEnumerator operands, out bool success) => + { + var result = hasExisting ? existing.ToArray().ToList() : new List(); + for (int i = 0; i < operands.Count; i++) + { + result.AddRange(operands.Get(i).ToArray()); + } + success = true; + return result.ToArray(); + }); + + var opts = new ColumnFamilyOptions().SetMergeOperator(mergeOp); + var dbPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + using var db = RocksDb.Open(new DbOptions().SetCreateIfMissing(), + dbPath, + new ColumnFamilies(opts)); + + db.Merge("key"u8.ToArray(), "hello"u8.ToArray()); + db.Merge("key"u8.ToArray(), "world"u8.ToArray()); + + var value = db.Get("key"u8.ToArray()); + + Assert.IsNotNull(value); + Assert.AreEqual("helloworld", Encoding.UTF8.GetString(value)); + } + finally + { + if (Directory.Exists(dbPath)) + { + Directory.Delete(dbPath, recursive: true); + } + } + } +} \ No newline at end of file diff --git a/build-codegen/Generate.cs b/build-codegen/Generate.cs index 2b68fe0..6ddf6c6 100644 --- a/build-codegen/Generate.cs +++ b/build-codegen/Generate.cs @@ -86,7 +86,7 @@ private static string ManualEnumName(NativeEnum nativeEnum) } if (funcName == "rocksdb_mergeoperator_create_full_merge" && arg.Name == "success") { - yield return ("out unsigned_char_ptr", "default"); + yield return ("out byte", "default"); } if (funcName == "rocksdb_mergeoperator_create_full_merge" && arg.Name == "new_value_length") { @@ -94,7 +94,7 @@ private static string ManualEnumName(NativeEnum nativeEnum) } if (funcName == "rocksdb_mergeoperator_create_partial_merge" && arg.Name == "success") { - yield return ("out unsigned_char_ptr", "default"); + yield return ("out byte", "default"); } if (funcName == "rocksdb_mergeoperator_create_partial_merge" && arg.Name == "new_value_length") { diff --git a/csharp/src/MergeOperator.cs b/csharp/src/MergeOperator.cs index 7483ba1..31c3e00 100644 --- a/csharp/src/MergeOperator.cs +++ b/csharp/src/MergeOperator.cs @@ -8,8 +8,8 @@ namespace RocksDbSharp public interface MergeOperator { string Name { get; } - IntPtr PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength); - IntPtr FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength); + IntPtr PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out byte success, out IntPtr newValueLength); + IntPtr FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out byte success, out IntPtr newValueLength); void DeleteValue(IntPtr value, UIntPtr valueLength); } @@ -81,7 +81,7 @@ public MergeOperatorImpl(string name, PartialMergeFunc partialMerge, FullMergeFu FullMerge = fullMerge; } - unsafe IntPtr MergeOperator.PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) + unsafe IntPtr MergeOperator.PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out byte success, out IntPtr newValueLength) { var keySpan = new ReadOnlySpan((void*)key, (int)keyLength); var operandsListSpan = new ReadOnlySpan((void*)operandsList, numOperands); @@ -94,12 +94,12 @@ unsafe IntPtr MergeOperator.PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr o Marshal.Copy(value, 0, ret, value.Length); newValueLength = (IntPtr)value.Length; - success = (IntPtr)Convert.ToInt32(_success); + success = (byte)(_success ? 1 : 0); return ret; } - unsafe IntPtr MergeOperator.FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) + unsafe IntPtr MergeOperator.FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out byte success, out IntPtr newValueLength) { var keySpan = new ReadOnlySpan((void*)key, (int)keyLength); var operandsListSpan = new ReadOnlySpan((void*)operandsList, numOperands); @@ -114,7 +114,7 @@ unsafe IntPtr MergeOperator.FullMerge(IntPtr key, UIntPtr keyLength, IntPtr exis Marshal.Copy(value, 0, ret, value.Length); newValueLength = (IntPtr)value.Length; - success = (IntPtr)Convert.ToInt32(_success); + success = (byte)(_success ? 1 : 0); return ret; } diff --git a/csharp/src/Native.cs b/csharp/src/Native.cs index ca335cc..369c54e 100644 --- a/csharp/src/Native.cs +++ b/csharp/src/Native.cs @@ -145,8 +145,8 @@ namespace RocksDbSharp public delegate int CompareDelegate(void_ptr p0, const_char_ptr a, size_t alen, const_char_ptr b, size_t blen); public delegate int CompareTsDelegate(void_ptr p0, const_char_ptr a_ts, size_t a_tslen, const_char_ptr b_ts, size_t b_tslen); public delegate int CompareWithoutTsDelegate(void_ptr p0, const_char_ptr a, size_t alen, bool a_has_ts, const_char_ptr b, size_t blen, bool b_has_ts); - public delegate char_ptr FullMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr existing_value, size_t existing_value_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, out unsigned_char_ptr success, out size_t_ptr new_value_length); - public delegate char_ptr PartialMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, out unsigned_char_ptr success, out size_t_ptr new_value_length); + public delegate char_ptr FullMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr existing_value, size_t existing_value_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, out byte success, out size_t_ptr new_value_length); + public delegate char_ptr PartialMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, out byte success, out size_t_ptr new_value_length); public delegate void DeleteValueDelegate(void_ptr p0, const_char_ptr value, size_t value_length); public delegate char_ptr TransformDelegate(void_ptr p0, const_char_ptr key, size_t length, size_t_ptr dst_length); public delegate char InDomainDelegate(void_ptr p0, const_char_ptr key, size_t length); diff --git a/csharp/src/Options/ColumnFamilyOptions.cs b/csharp/src/Options/ColumnFamilyOptions.cs index a6c8acb..0a7793d 100644 --- a/csharp/src/Options/ColumnFamilyOptions.cs +++ b/csharp/src/Options/ColumnFamilyOptions.cs @@ -323,13 +323,13 @@ private static MergeOperator GetMergeOperatorFromPtr(IntPtr getMergeOperatorPtr) return getMergeOperator(); } - private unsafe static IntPtr MergeOperator_PartialMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) + private unsafe static IntPtr MergeOperator_PartialMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out byte success, out IntPtr newValueLength) { var mergeOperator = GetMergeOperatorFromPtr((*((OptionsBase.MergeOperatorState*)state)).GetMergeOperatorPtr); return mergeOperator.PartialMerge(key, keyLength, operandsList, operandsListLength, numOperands, out success, out newValueLength); } - private unsafe static IntPtr MergeOperator_FullMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) + private unsafe static IntPtr MergeOperator_FullMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out byte success, out IntPtr newValueLength) { var mergeOperator = GetMergeOperatorFromPtr((*((OptionsBase.MergeOperatorState*)state)).GetMergeOperatorPtr); return mergeOperator.FullMerge(key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, out success, out newValueLength);