From 5f65dc877806f8d60d64144ddee9023b178c2d50 Mon Sep 17 00:00:00 2001 From: Matteo Merli Date: Mon, 6 Mar 2017 22:13:53 -0800 Subject: [PATCH] Expose DB::DeleteRange and WriteBath::DeleteRange in Java Summary: Added JNI wrapper from `DeleteRange` methods Closes https://github.com/facebook/rocksdb/pull/1951 Differential Revision: D4657746 Pulled By: yiwu-arbug fbshipit-source-id: 3fc7ab8 --- java/rocksjni/portal.h | 20 +++ java/rocksjni/rocksjni.cc | 129 ++++++++++++++++++ java/rocksjni/write_batch.cc | 41 ++++++ java/rocksjni/write_batch_with_index.cc | 38 ++++++ java/rocksjni/writebatchhandlerjnicallback.cc | 49 +++++++ java/rocksjni/writebatchhandlerjnicallback.h | 2 + .../java/org/rocksdb/AbstractWriteBatch.java | 17 +++ java/src/main/java/org/rocksdb/RocksDB.java | 110 +++++++++++++++ .../src/main/java/org/rocksdb/WriteBatch.java | 7 + .../java/org/rocksdb/WriteBatchInterface.java | 33 +++++ .../java/org/rocksdb/WriteBatchWithIndex.java | 6 + .../test/java/org/rocksdb/RocksDBTest.java | 20 +++ .../org/rocksdb/WriteBatchHandlerTest.java | 12 +- .../test/java/org/rocksdb/WriteBatchTest.java | 29 ++++ .../org/rocksdb/WriteBatchWithIndexTest.java | 24 ++++ 15 files changed, 531 insertions(+), 6 deletions(-) diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index e4b9f356d..87b00cc7c 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -795,6 +795,26 @@ class WriteBatchHandlerJni : public RocksDBNativeClass< return mid; } + /** + * Get the Java Method: WriteBatch.Handler#deleteRange + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ + static jmethodID getDeleteRangeMethodId(JNIEnv* env) { + jclass jclazz = getJClass(env); + if (jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "deleteRange", "([B[B)V"); + assert(mid != nullptr); + return mid; + } + /** * Get the Java Method: WriteBatch.Handler#logData * diff --git a/java/rocksjni/rocksjni.cc b/java/rocksjni/rocksjni.cc index 9ecdbb581..044cccbf7 100644 --- a/java/rocksjni/rocksjni.cc +++ b/java/rocksjni/rocksjni.cc @@ -1224,6 +1224,135 @@ void Java_org_rocksdb_RocksDB_singleDelete__JJ_3BIJ( } } +////////////////////////////////////////////////////////////////////////////// +// rocksdb::DB::DeleteRange() +/** + * @return true if the delete range succeeded, false if a Java Exception + * was thrown + */ +bool rocksdb_delete_range_helper(JNIEnv* env, rocksdb::DB* db, + const rocksdb::WriteOptions& write_options, + rocksdb::ColumnFamilyHandle* cf_handle, + jbyteArray jbegin_key, jint jbegin_key_off, + jint jbegin_key_len, jbyteArray jend_key, + jint jend_key_off, jint jend_key_len) { + jbyte* begin_key = new jbyte[jbegin_key_len]; + env->GetByteArrayRegion(jbegin_key, jbegin_key_off, jbegin_key_len, + begin_key); + if (env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete[] begin_key; + return false; + } + rocksdb::Slice begin_key_slice(reinterpret_cast(begin_key), + jbegin_key_len); + + jbyte* end_key = new jbyte[jend_key_len]; + env->GetByteArrayRegion(jend_key, jend_key_off, jend_key_len, end_key); + if (env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete[] begin_key; + delete[] end_key; + return false; + } + rocksdb::Slice end_key_slice(reinterpret_cast(end_key), jend_key_len); + + rocksdb::Status s = + db->DeleteRange(write_options, cf_handle, begin_key_slice, end_key_slice); + + // cleanup + delete[] begin_key; + delete[] end_key; + + if (s.ok()) { + return true; + } + + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + return false; +} + +/* + * Class: org_rocksdb_RocksDB + * Method: deleteRange + * Signature: (J[BII[BII)V + */ +void Java_org_rocksdb_RocksDB_deleteRange__J_3BII_3BII( + JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jbegin_key, + jint jbegin_key_off, jint jbegin_key_len, jbyteArray jend_key, + jint jend_key_off, jint jend_key_len) { + auto* db = reinterpret_cast(jdb_handle); + static const rocksdb::WriteOptions default_write_options = + rocksdb::WriteOptions(); + rocksdb_delete_range_helper(env, db, default_write_options, nullptr, + jbegin_key, jbegin_key_off, jbegin_key_len, + jend_key, jend_key_off, jend_key_len); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: deleteRange + * Signature: (J[BII[BIIJ)V + */ +void Java_org_rocksdb_RocksDB_deleteRange__J_3BII_3BIIJ( + JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jbegin_key, + jint jbegin_key_off, jint jbegin_key_len, jbyteArray jend_key, + jint jend_key_off, jint jend_key_len, jlong jcf_handle) { + auto* db = reinterpret_cast(jdb_handle); + static const rocksdb::WriteOptions default_write_options = + rocksdb::WriteOptions(); + auto* cf_handle = reinterpret_cast(jcf_handle); + if (cf_handle != nullptr) { + rocksdb_delete_range_helper(env, db, default_write_options, cf_handle, + jbegin_key, jbegin_key_off, jbegin_key_len, + jend_key, jend_key_off, jend_key_len); + } else { + rocksdb::RocksDBExceptionJni::ThrowNew( + env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); + } +} + +/* + * Class: org_rocksdb_RocksDB + * Method: deleteRange + * Signature: (JJ[BII[BII)V + */ +void Java_org_rocksdb_RocksDB_deleteRange__JJ_3BII_3BII( + JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options, + jbyteArray jbegin_key, jint jbegin_key_off, jint jbegin_key_len, + jbyteArray jend_key, jint jend_key_off, jint jend_key_len) { + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options); + rocksdb_delete_range_helper(env, db, *write_options, nullptr, jbegin_key, + jbegin_key_off, jbegin_key_len, jend_key, + jend_key_off, jend_key_len); +} + +/* + * Class: org_rocksdb_RocksDB + * Method: deleteRange + * Signature: (JJ[BII[BIIJ)V + */ +void Java_org_rocksdb_RocksDB_deleteRange__JJ_3BII_3BIIJ( + JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options, + jbyteArray jbegin_key, jint jbegin_key_off, jint jbegin_key_len, + jbyteArray jend_key, jint jend_key_off, jint jend_key_len, + jlong jcf_handle) { + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options); + auto* cf_handle = reinterpret_cast(jcf_handle); + if (cf_handle != nullptr) { + rocksdb_delete_range_helper(env, db, *write_options, cf_handle, jbegin_key, + jbegin_key_off, jbegin_key_len, jend_key, + jend_key_off, jend_key_len); + } else { + rocksdb::RocksDBExceptionJni::ThrowNew( + env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); + } +} + ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::Merge diff --git a/java/rocksjni/write_batch.cc b/java/rocksjni/write_batch.cc index 8e6ba6674..bef1e8d0c 100644 --- a/java/rocksjni/write_batch.cc +++ b/java/rocksjni/write_batch.cc @@ -201,6 +201,47 @@ void Java_org_rocksdb_WriteBatch_remove__J_3BIJ( rocksdb::JniUtil::k_op(remove, env, jobj, jkey, jkey_len); } +/* + * Class: org_rocksdb_WriteBatch + * Method: deleteRange + * Signature: (J[BI[BI)V + */ +JNIEXPORT void JNICALL Java_org_rocksdb_WriteBatch_deleteRange__J_3BI_3BI( + JNIEnv*, jobject, jlong, jbyteArray, jint, jbyteArray, jint); + +void Java_org_rocksdb_WriteBatch_deleteRange__J_3BI_3BI( + JNIEnv* env, jobject jobj, jlong jwb_handle, jbyteArray jbegin_key, + jint jbegin_key_len, jbyteArray jend_key, jint jend_key_len) { + auto* wb = reinterpret_cast(jwb_handle); + assert(wb != nullptr); + auto deleteRange = [&wb](rocksdb::Slice beginKey, rocksdb::Slice endKey) { + wb->DeleteRange(beginKey, endKey); + }; + rocksdb::JniUtil::kv_op(deleteRange, env, jobj, jbegin_key, jbegin_key_len, + jend_key, jend_key_len); +} + +/* + * Class: org_rocksdb_WriteBatch + * Method: deleteRange + * Signature: (J[BI[BIJ)V + */ +void Java_org_rocksdb_WriteBatch_deleteRange__J_3BI_3BIJ( + JNIEnv* env, jobject jobj, jlong jwb_handle, jbyteArray jbegin_key, + jint jbegin_key_len, jbyteArray jend_key, jint jend_key_len, + jlong jcf_handle) { + auto* wb = reinterpret_cast(jwb_handle); + assert(wb != nullptr); + auto* cf_handle = reinterpret_cast(jcf_handle); + assert(cf_handle != nullptr); + auto deleteRange = [&wb, &cf_handle](rocksdb::Slice beginKey, + rocksdb::Slice endKey) { + wb->DeleteRange(cf_handle, beginKey, endKey); + }; + rocksdb::JniUtil::kv_op(deleteRange, env, jobj, jbegin_key, jbegin_key_len, + jend_key, jend_key_len); +} + /* * Class: org_rocksdb_WriteBatch * Method: putLogData diff --git a/java/rocksjni/write_batch_with_index.cc b/java/rocksjni/write_batch_with_index.cc index 53cf60584..6a382f894 100644 --- a/java/rocksjni/write_batch_with_index.cc +++ b/java/rocksjni/write_batch_with_index.cc @@ -172,6 +172,44 @@ void Java_org_rocksdb_WriteBatchWithIndex_remove__J_3BIJ( rocksdb::JniUtil::k_op(remove, env, jobj, jkey, jkey_len); } +/* + * Class: org_rocksdb_WriteBatchWithIndex + * Method: deleteRange + * Signature: (J[BI[BI)V + */ +void Java_org_rocksdb_WriteBatchWithIndex_deleteRange__J_3BI_3BI( + JNIEnv* env, jobject jobj, jlong jwbwi_handle, jbyteArray jbegin_key, + jint jbegin_key_len, jbyteArray jend_key, jint jend_key_len) { + auto* wbwi = reinterpret_cast(jwbwi_handle); + assert(wbwi != nullptr); + auto deleteRange = [&wbwi](rocksdb::Slice beginKey, rocksdb::Slice endKey) { + wbwi->DeleteRange(beginKey, endKey); + }; + rocksdb::JniUtil::kv_op(deleteRange, env, jobj, jbegin_key, jbegin_key_len, + jend_key, jend_key_len); +} + +/* + * Class: org_rocksdb_WriteBatchWithIndex + * Method: deleteRange + * Signature: (J[BI[BIJ)V + */ +void Java_org_rocksdb_WriteBatchWithIndex_deleteRange__J_3BI_3BIJ( + JNIEnv* env, jobject jobj, jlong jwbwi_handle, jbyteArray jbegin_key, + jint jbegin_key_len, jbyteArray jend_key, jint jend_key_len, + jlong jcf_handle) { + auto* wbwi = reinterpret_cast(jwbwi_handle); + assert(wbwi != nullptr); + auto* cf_handle = reinterpret_cast(jcf_handle); + assert(cf_handle != nullptr); + auto deleteRange = [&wbwi, &cf_handle](rocksdb::Slice beginKey, + rocksdb::Slice endKey) { + wbwi->DeleteRange(cf_handle, beginKey, endKey); + }; + rocksdb::JniUtil::kv_op(deleteRange, env, jobj, jbegin_key, jbegin_key_len, + jend_key, jend_key_len); +} + /* * Class: org_rocksdb_WriteBatchWithIndex * Method: putLogData diff --git a/java/rocksjni/writebatchhandlerjnicallback.cc b/java/rocksjni/writebatchhandlerjnicallback.cc index d24419176..7ef45a25c 100644 --- a/java/rocksjni/writebatchhandlerjnicallback.cc +++ b/java/rocksjni/writebatchhandlerjnicallback.cc @@ -41,6 +41,12 @@ WriteBatchHandlerJniCallback::WriteBatchHandlerJniCallback( return; } + m_jDeleteRangeMethodId = WriteBatchHandlerJni::getDeleteRangeMethodId(env); + if (m_jDeleteRangeMethodId == nullptr) { + // exception thrown + return; + } + m_jLogDataMethodId = WriteBatchHandlerJni::getLogDataMethodId(env); if(m_jLogDataMethodId == nullptr) { // exception thrown @@ -176,6 +182,49 @@ void WriteBatchHandlerJniCallback::Delete(const Slice& key) { } } +void WriteBatchHandlerJniCallback::DeleteRange(const Slice& beginKey, + const Slice& endKey) { + const jbyteArray j_beginKey = sliceToJArray(beginKey); + if (j_beginKey == nullptr) { + // exception thrown + if (m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + return; + } + + const jbyteArray j_endKey = sliceToJArray(beginKey); + if (j_endKey == nullptr) { + // exception thrown + if (m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + return; + } + + m_env->CallVoidMethod(m_jWriteBatchHandler, m_jDeleteRangeMethodId, + j_beginKey, j_endKey); + if (m_env->ExceptionCheck()) { + // exception thrown + m_env->ExceptionDescribe(); + if (j_beginKey != nullptr) { + m_env->DeleteLocalRef(j_beginKey); + } + if (j_endKey != nullptr) { + m_env->DeleteLocalRef(j_endKey); + } + return; + } + + if (j_beginKey != nullptr) { + m_env->DeleteLocalRef(j_beginKey); + } + + if (j_endKey != nullptr) { + m_env->DeleteLocalRef(j_endKey); + } +} + void WriteBatchHandlerJniCallback::LogData(const Slice& blob) { const jbyteArray j_blob = sliceToJArray(blob); if(j_blob == nullptr) { diff --git a/java/rocksjni/writebatchhandlerjnicallback.h b/java/rocksjni/writebatchhandlerjnicallback.h index 1c421db03..791dd9ae9 100644 --- a/java/rocksjni/writebatchhandlerjnicallback.h +++ b/java/rocksjni/writebatchhandlerjnicallback.h @@ -28,6 +28,7 @@ class WriteBatchHandlerJniCallback : public WriteBatch::Handler { void Put(const Slice& key, const Slice& value); void Merge(const Slice& key, const Slice& value); void Delete(const Slice& key); + void DeleteRange(const Slice& beginKey, const Slice& endKey); void LogData(const Slice& blob); bool Continue(); @@ -38,6 +39,7 @@ class WriteBatchHandlerJniCallback : public WriteBatch::Handler { jmethodID m_jPutMethodId; jmethodID m_jMergeMethodId; jmethodID m_jDeleteMethodId; + jmethodID m_jDeleteRangeMethodId; jmethodID m_jLogDataMethodId; jmethodID m_jContinueMethodId; }; diff --git a/java/src/main/java/org/rocksdb/AbstractWriteBatch.java b/java/src/main/java/org/rocksdb/AbstractWriteBatch.java index af3a249fd..6c08d7e42 100644 --- a/java/src/main/java/org/rocksdb/AbstractWriteBatch.java +++ b/java/src/main/java/org/rocksdb/AbstractWriteBatch.java @@ -51,6 +51,17 @@ public abstract class AbstractWriteBatch extends RocksObject remove(nativeHandle_, key, key.length, columnFamilyHandle.nativeHandle_); } + @Override + public void deleteRange(byte[] beginKey, byte[] endKey) { + deleteRange(nativeHandle_, beginKey, beginKey.length, endKey, endKey.length); + } + + @Override + public void deleteRange(ColumnFamilyHandle columnFamilyHandle, byte[] beginKey, byte[] endKey) { + deleteRange(nativeHandle_, beginKey, beginKey.length, endKey, endKey.length, + columnFamilyHandle.nativeHandle_); + } + @Override public void putLogData(byte[] blob) { putLogData(nativeHandle_, blob, blob.length); @@ -91,6 +102,12 @@ public abstract class AbstractWriteBatch extends RocksObject abstract void remove(final long handle, final byte[] key, final int keyLen, final long cfHandle); + abstract void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen, + final byte[] endKey, final int endKeyLen); + + abstract void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen, + final byte[] endKey, final int endKeyLen, final long cfHandle); + abstract void putLogData(final long handle, final byte[] blob, final int blobLen); diff --git a/java/src/main/java/org/rocksdb/RocksDB.java b/java/src/main/java/org/rocksdb/RocksDB.java index 2a4beefd1..c7a79edb0 100644 --- a/java/src/main/java/org/rocksdb/RocksDB.java +++ b/java/src/main/java/org/rocksdb/RocksDB.java @@ -1337,6 +1337,104 @@ public class RocksDB extends RocksObject { property, property.length()); } + /** + * Removes the database entries in the range ["beginKey", "endKey"), i.e., + * including "beginKey" and excluding "endKey". a non-OK status on error. It + * is not an error if no keys exist in the range ["beginKey", "endKey"). + * + * Delete the database entry (if any) for "key". Returns OK on success, and a + * non-OK status on error. It is not an error if "key" did not exist in the + * database. + * + * @param beginKey + * First key to delete within database (included) + * @param endKey + * Last key to delete within database (excluded) + * + * @throws RocksDBException + * thrown if error happens in underlying native library. + */ + public void deleteRange(final byte[] beginKey, final byte[] endKey) throws RocksDBException { + deleteRange(nativeHandle_, beginKey, 0, beginKey.length, endKey, 0, endKey.length); + } + + /** + * Removes the database entries in the range ["beginKey", "endKey"), i.e., + * including "beginKey" and excluding "endKey". a non-OK status on error. It + * is not an error if no keys exist in the range ["beginKey", "endKey"). + * + * Delete the database entry (if any) for "key". Returns OK on success, and a + * non-OK status on error. It is not an error if "key" did not exist in the + * database. + * + * @param columnFamilyHandle + * {@link org.rocksdb.ColumnFamilyHandle} instance + * @param beginKey + * First key to delete within database (included) + * @param endKey + * Last key to delete within database (excluded) + * + * @throws RocksDBException + * thrown if error happens in underlying native library. + */ + public void deleteRange(final ColumnFamilyHandle columnFamilyHandle, final byte[] beginKey, + final byte[] endKey) throws RocksDBException { + deleteRange(nativeHandle_, beginKey, 0, beginKey.length, endKey, 0, endKey.length, + columnFamilyHandle.nativeHandle_); + } + + /** + * Removes the database entries in the range ["beginKey", "endKey"), i.e., + * including "beginKey" and excluding "endKey". a non-OK status on error. It + * is not an error if no keys exist in the range ["beginKey", "endKey"). + * + * Delete the database entry (if any) for "key". Returns OK on success, and a + * non-OK status on error. It is not an error if "key" did not exist in the + * database. + * + * @param writeOpt + * WriteOptions to be used with delete operation + * @param beginKey + * First key to delete within database (included) + * @param endKey + * Last key to delete within database (excluded) + * + * @throws RocksDBException + * thrown if error happens in underlying native library. + */ + public void deleteRange(final WriteOptions writeOpt, final byte[] beginKey, final byte[] endKey) + throws RocksDBException { + deleteRange(nativeHandle_, writeOpt.nativeHandle_, beginKey, 0, beginKey.length, endKey, 0, + endKey.length); + } + + /** + * Removes the database entries in the range ["beginKey", "endKey"), i.e., + * including "beginKey" and excluding "endKey". a non-OK status on error. It + * is not an error if no keys exist in the range ["beginKey", "endKey"). + * + * Delete the database entry (if any) for "key". Returns OK on success, and a + * non-OK status on error. It is not an error if "key" did not exist in the + * database. + * + * @param columnFamilyHandle {@link org.rocksdb.ColumnFamilyHandle} + * instance + * @param writeOpt + * WriteOptions to be used with delete operation + * @param beginKey + * First key to delete within database (included) + * @param endKey + * Last key to delete within database (excluded) + * + * @throws RocksDBException + * thrown if error happens in underlying native library. + */ + public void deleteRange(final ColumnFamilyHandle columnFamilyHandle, final WriteOptions writeOpt, + final byte[] beginKey, final byte[] endKey) throws RocksDBException { + deleteRange(nativeHandle_, writeOpt.nativeHandle_, beginKey, 0, beginKey.length, endKey, 0, + endKey.length, columnFamilyHandle.nativeHandle_); + } + /** * DB implementations can export properties about their state * via this method. If "property" is a valid property understood by this @@ -2261,6 +2359,18 @@ public class RocksDB extends RocksObject { protected native void singleDelete( long handle, long writeOptHandle, byte[] key, int keyLen, long cfHandle) throws RocksDBException; + protected native void deleteRange(long handle, byte[] beginKey, int beginKeyOffset, + int beginKeyLength, byte[] endKey, int endKeyOffset, int endKeyLength) + throws RocksDBException; + protected native void deleteRange(long handle, byte[] beginKey, int beginKeyOffset, + int beginKeyLength, byte[] endKey, int endKeyOffset, int endKeyLength, long cfHandle) + throws RocksDBException; + protected native void deleteRange(long handle, long writeOptHandle, byte[] beginKey, + int beginKeyOffset, int beginKeyLength, byte[] endKey, int endKeyOffset, int endKeyLength) + throws RocksDBException; + protected native void deleteRange(long handle, long writeOptHandle, byte[] beginKey, + int beginKeyOffset, int beginKeyLength, byte[] endKey, int endKeyOffset, int endKeyLength, + long cfHandle) throws RocksDBException; protected native String getProperty0(long nativeHandle, String property, int propertyLength) throws RocksDBException; protected native String getProperty0(long nativeHandle, long cfHandle, diff --git a/java/src/main/java/org/rocksdb/WriteBatch.java b/java/src/main/java/org/rocksdb/WriteBatch.java index bfd05a93f..fb447c92c 100644 --- a/java/src/main/java/org/rocksdb/WriteBatch.java +++ b/java/src/main/java/org/rocksdb/WriteBatch.java @@ -91,6 +91,12 @@ public class WriteBatch extends AbstractWriteBatch { final int keyLen); @Override final native void remove(final long handle, final byte[] key, final int keyLen, final long cfHandle); + @Override + final native void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen, + final byte[] endKey, final int endKeyLen); + @Override + final native void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen, + final byte[] endKey, final int endKeyLen, final long cfHandle); @Override final native void putLogData(final long handle, final byte[] blob, final int blobLen); @Override final native void clear0(final long handle); @@ -116,6 +122,7 @@ public class WriteBatch extends AbstractWriteBatch { public abstract void put(byte[] key, byte[] value); public abstract void merge(byte[] key, byte[] value); public abstract void delete(byte[] key); + public abstract void deleteRange(byte[] beginKey, byte[] endKey); public abstract void logData(byte[] blob); /** diff --git a/java/src/main/java/org/rocksdb/WriteBatchInterface.java b/java/src/main/java/org/rocksdb/WriteBatchInterface.java index a07791851..4746ba3a1 100644 --- a/java/src/main/java/org/rocksdb/WriteBatchInterface.java +++ b/java/src/main/java/org/rocksdb/WriteBatchInterface.java @@ -75,6 +75,39 @@ public interface WriteBatchInterface { */ void remove(ColumnFamilyHandle columnFamilyHandle, byte[] key); + /** + * Removes the database entries in the range ["beginKey", "endKey"), i.e., + * including "beginKey" and excluding "endKey". a non-OK status on error. It + * is not an error if no keys exist in the range ["beginKey", "endKey"). + * + * Delete the database entry (if any) for "key". Returns OK on success, and a + * non-OK status on error. It is not an error if "key" did not exist in the + * database. + * + * @param beginKey + * First key to delete within database (included) + * @param endKey + * Last key to delete within database (excluded) + */ + void deleteRange(byte[] beginKey, byte[] endKey); + + /** + * Removes the database entries in the range ["beginKey", "endKey"), i.e., + * including "beginKey" and excluding "endKey". a non-OK status on error. It + * is not an error if no keys exist in the range ["beginKey", "endKey"). + * + * Delete the database entry (if any) for "key". Returns OK on success, and a + * non-OK status on error. It is not an error if "key" did not exist in the + * database. + * + * @param columnFamilyHandle {@link ColumnFamilyHandle} instance + * @param beginKey + * First key to delete within database (included) + * @param endKey + * Last key to delete within database (excluded) + */ + void deleteRange(ColumnFamilyHandle columnFamilyHandle, byte[] beginKey, byte[] endKey); + /** * Append a blob of arbitrary size to the records in this batch. The blob will * be stored in the transaction log but not in any other file. In particular, diff --git a/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java b/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java index b84ea8583..0b55543c1 100644 --- a/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java +++ b/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java @@ -248,6 +248,12 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { final int keyLen); @Override final native void remove(final long handle, final byte[] key, final int keyLen, final long cfHandle); + @Override + final native void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen, + final byte[] endKey, final int endKeyLen); + @Override + final native void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen, + final byte[] endKey, final int endKeyLen, final long cfHandle); @Override final native void putLogData(final long handle, final byte[] blob, final int blobLen); @Override final native void clear0(final long handle); diff --git a/java/src/test/java/org/rocksdb/RocksDBTest.java b/java/src/test/java/org/rocksdb/RocksDBTest.java index ab1feb0e5..b5c1edd25 100644 --- a/java/src/test/java/org/rocksdb/RocksDBTest.java +++ b/java/src/test/java/org/rocksdb/RocksDBTest.java @@ -255,6 +255,26 @@ public class RocksDBTest { } } + @Test + public void deleteRange() throws RocksDBException { + try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath()); + final WriteOptions wOpt = new WriteOptions()) { + db.put("key1".getBytes(), "value".getBytes()); + db.put("key2".getBytes(), "12345678".getBytes()); + db.put("key3".getBytes(), "abcdefg".getBytes()); + db.put("key4".getBytes(), "xyz".getBytes()); + assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes()); + assertThat(db.get("key2".getBytes())).isEqualTo("12345678".getBytes()); + assertThat(db.get("key3".getBytes())).isEqualTo("abcdefg".getBytes()); + assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes()); + db.deleteRange("key2".getBytes(), "key4".getBytes()); + assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes()); + assertThat(db.get("key2".getBytes())).isNull(); + assertThat(db.get("key3".getBytes())).isNull(); + assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes()); + } + } + @Test public void getIntProperty() throws RocksDBException { try ( diff --git a/java/src/test/java/org/rocksdb/WriteBatchHandlerTest.java b/java/src/test/java/org/rocksdb/WriteBatchHandlerTest.java index 35c63f2af..953a638f9 100644 --- a/java/src/test/java/org/rocksdb/WriteBatchHandlerTest.java +++ b/java/src/test/java/org/rocksdb/WriteBatchHandlerTest.java @@ -119,12 +119,7 @@ public class WriteBatchHandlerTest { * Enumeration of Write Batch * event actions */ - private enum Action { - PUT, - MERGE, - DELETE, - LOG - } + private enum Action { PUT, MERGE, DELETE, DELETE_RANGE, LOG } /** * A simple WriteBatch Handler which adds a record @@ -160,6 +155,11 @@ public class WriteBatchHandlerTest { new Tuple(key, null))); } + @Override + public void deleteRange(final byte[] beginKey, final byte[] endKey) { + events.add(new Tuple<>(Action.DELETE_RANGE, new Tuple(beginKey, endKey))); + } + @Override public void logData(final byte[] blob) { events.add(new Tuple<>(Action.LOG, diff --git a/java/src/test/java/org/rocksdb/WriteBatchTest.java b/java/src/test/java/org/rocksdb/WriteBatchTest.java index ba5d00397..1e289d532 100644 --- a/java/src/test/java/org/rocksdb/WriteBatchTest.java +++ b/java/src/test/java/org/rocksdb/WriteBatchTest.java @@ -163,6 +163,30 @@ public class WriteBatchTest { } } + @Test + public void deleteRange() throws RocksDBException { + try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath()); + final WriteOptions wOpt = new WriteOptions()) { + db.put("key1".getBytes(), "value".getBytes()); + db.put("key2".getBytes(), "12345678".getBytes()); + db.put("key3".getBytes(), "abcdefg".getBytes()); + db.put("key4".getBytes(), "xyz".getBytes()); + assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes()); + assertThat(db.get("key2".getBytes())).isEqualTo("12345678".getBytes()); + assertThat(db.get("key3".getBytes())).isEqualTo("abcdefg".getBytes()); + assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes()); + + WriteBatch batch = new WriteBatch(); + batch.deleteRange("key2".getBytes(), "key4".getBytes()); + db.write(new WriteOptions(), batch); + + assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes()); + assertThat(db.get("key2".getBytes())).isNull(); + assertThat(db.get("key3".getBytes())).isNull(); + assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes()); + } + } + @Test(expected = RocksDBException.class) public void restorePoints_withoutSavePoints() throws RocksDBException { try (final WriteBatch batch = new WriteBatch()) { @@ -234,6 +258,11 @@ public class WriteBatchTest { } } + @Override + public void deleteRange(final byte[] beginKey, final byte[] endKey) { + throw new UnsupportedOperationException(); + } + @Override public void logData(final byte[] blob) { } diff --git a/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java b/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java index b2283480a..61e7176ae 100644 --- a/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java +++ b/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java @@ -383,4 +383,28 @@ public class WriteBatchWithIndexTest { buf.get(ary); return ary; } + + @Test + public void deleteRange() throws RocksDBException { + try (final RocksDB db = RocksDB.open(dbFolder.getRoot().getAbsolutePath()); + final WriteOptions wOpt = new WriteOptions()) { + db.put("key1".getBytes(), "value".getBytes()); + db.put("key2".getBytes(), "12345678".getBytes()); + db.put("key3".getBytes(), "abcdefg".getBytes()); + db.put("key4".getBytes(), "xyz".getBytes()); + assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes()); + assertThat(db.get("key2".getBytes())).isEqualTo("12345678".getBytes()); + assertThat(db.get("key3".getBytes())).isEqualTo("abcdefg".getBytes()); + assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes()); + + WriteBatch batch = new WriteBatch(); + batch.deleteRange("key2".getBytes(), "key4".getBytes()); + db.write(new WriteOptions(), batch); + + assertThat(db.get("key1".getBytes())).isEqualTo("value".getBytes()); + assertThat(db.get("key2".getBytes())).isNull(); + assertThat(db.get("key3".getBytes())).isNull(); + assertThat(db.get("key4".getBytes())).isEqualTo("xyz".getBytes()); + } + } }