From f45c0d20de1d083fc65cb446a67542f13c9f31cc Mon Sep 17 00:00:00 2001 From: moozzyk Date: Tue, 9 Oct 2018 17:11:53 -0700 Subject: [PATCH] JNI support for ReadOptions::iterate_lower_bound (#4444) Summary: Fixes: #4401 Pull Request resolved: https://github.com/facebook/rocksdb/pull/4444 Differential Revision: D10282120 Pulled By: sagar0 fbshipit-source-id: d9ddcc1b132208ae7f806fa2106add6fec1baa11 --- java/rocksjni/options.cc | 25 ++++++++ .../main/java/org/rocksdb/ReadOptions.java | 58 ++++++++++++++++++- .../java/org/rocksdb/ReadOptionsTest.java | 34 +++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/java/rocksjni/options.cc b/java/rocksjni/options.cc index 9aed80e1e..90692315d 100644 --- a/java/rocksjni/options.cc +++ b/java/rocksjni/options.cc @@ -6525,6 +6525,31 @@ jlong Java_org_rocksdb_ReadOptions_iterateUpperBound(JNIEnv* /*env*/, return reinterpret_cast(upper_bound_slice_handle); } +/* + * Class: org_rocksdb_ReadOptions + * Method: setIterateLowerBound + * Signature: (JJ)I + */ +void Java_org_rocksdb_ReadOptions_setIterateLowerBound( + JNIEnv* /*env*/, jobject /*jobj*/, jlong jhandle, + jlong jlower_bound_slice_handle) { + reinterpret_cast(jhandle)->iterate_lower_bound = + reinterpret_cast(jlower_bound_slice_handle); +} + +/* + * Class: org_rocksdb_ReadOptions + * Method: iterateLowerBound + * Signature: (J)J + */ +jlong Java_org_rocksdb_ReadOptions_iterateLowerBound(JNIEnv* /*env*/, + jobject /*jobj*/, + jlong jhandle) { + auto& lower_bound_slice_handle = + reinterpret_cast(jhandle)->iterate_lower_bound; + return reinterpret_cast(lower_bound_slice_handle); +} + ///////////////////////////////////////////////////////////////////// // rocksdb::ComparatorOptions diff --git a/java/src/main/java/org/rocksdb/ReadOptions.java b/java/src/main/java/org/rocksdb/ReadOptions.java index be8aec6b3..f176d249b 100644 --- a/java/src/main/java/org/rocksdb/ReadOptions.java +++ b/java/src/main/java/org/rocksdb/ReadOptions.java @@ -27,6 +27,7 @@ public class ReadOptions extends RocksObject { public ReadOptions(ReadOptions other) { super(copyReadOptions(other.nativeHandle_)); iterateUpperBoundSlice_ = other.iterateUpperBoundSlice_; + iterateLowerBoundSlice_ = other.iterateLowerBoundSlice_; } /** @@ -423,15 +424,65 @@ public class ReadOptions extends RocksObject { return null; } + /** + * Defines the smallest key at which the backward iterator can return an + * entry. Once the bound is passed, Valid() will be false. + * `iterate_lower_bound` is inclusive ie the bound value is a valid entry. + * + * If prefix_extractor is not null, the Seek target and `iterate_lower_bound` + * need to have the same prefix. This is because ordering is not guaranteed + * outside of prefix domain. + * + * Default: nullptr + * + * @param iterateLowerBound Slice representing the lower bound + * @return the reference to the current ReadOptions. + */ + public ReadOptions setIterateLowerBound(final Slice iterateLowerBound) { + assert(isOwningHandle()); + if (iterateLowerBound != null) { + // Hold onto a reference so it doesn't get garbaged collected out from under us. + iterateLowerBoundSlice_ = iterateLowerBound; + setIterateLowerBound(nativeHandle_, iterateLowerBoundSlice_.getNativeHandle()); + } + return this; + } + + /** + * Defines the smallest key at which the backward iterator can return an + * entry. Once the bound is passed, Valid() will be false. + * `iterate_lower_bound` is inclusive ie the bound value is a valid entry. + * + * If prefix_extractor is not null, the Seek target and `iterate_lower_bound` + * need to have the same prefix. This is because ordering is not guaranteed + * outside of prefix domain. + * + * Default: nullptr + * + * @return Slice representing current iterate_lower_bound setting, or null if + * one does not exist. + */ + public Slice iterateLowerBound() { + assert(isOwningHandle()); + long lowerBoundSliceHandle = iterateLowerBound(nativeHandle_); + if (lowerBoundSliceHandle != 0) { + // Disown the new slice - it's owned by the C++ side of the JNI boundary + // from the perspective of this method. + return new Slice(lowerBoundSliceHandle, false); + } + return null; + } + // instance variables // NOTE: If you add new member variables, please update the copy constructor above! // - // Hold a reference to any iterate upper bound that was set on this object - // until we're destroyed or it's overwritten. That way the caller can freely + // Hold a reference to any iterate upper/lower bound that was set on this object + // until we're destroyed or it's overwritten. That way the caller can freely // leave scope without us losing the Java Slice object, which during close() // would also reap its associated rocksdb::Slice native object since it's // possibly (likely) to be an owning handle. protected Slice iterateUpperBoundSlice_; + protected Slice iterateLowerBoundSlice_; private native static long newReadOptions(); private native static long copyReadOptions(long handle); @@ -465,6 +516,9 @@ public class ReadOptions extends RocksObject { private native void setIterateUpperBound(final long handle, final long upperBoundSliceHandle); private native long iterateUpperBound(final long handle); + private native void setIterateLowerBound(final long handle, + final long upperBoundSliceHandle); + private native long iterateLowerBound(final long handle); @Override protected final native void disposeInternal(final long handle); diff --git a/java/src/test/java/org/rocksdb/ReadOptionsTest.java b/java/src/test/java/org/rocksdb/ReadOptionsTest.java index f7d799909..4e860ae4c 100644 --- a/java/src/test/java/org/rocksdb/ReadOptionsTest.java +++ b/java/src/test/java/org/rocksdb/ReadOptionsTest.java @@ -144,16 +144,34 @@ public class ReadOptionsTest { } } + @Test + public void iterateLowerBound() { + try (final ReadOptions opt = new ReadOptions()) { + Slice lowerBound = buildRandomSlice(); + opt.setIterateLowerBound(lowerBound); + assertThat(Arrays.equals(lowerBound.data(), opt.iterateLowerBound().data())).isTrue(); + } + } + + @Test + public void iterateLowerBoundNull() { + try (final ReadOptions opt = new ReadOptions()) { + assertThat(opt.iterateLowerBound()).isNull(); + } + } + @Test public void copyConstructor() { try (final ReadOptions opt = new ReadOptions()) { opt.setVerifyChecksums(false); opt.setFillCache(false); opt.setIterateUpperBound(buildRandomSlice()); + opt.setIterateLowerBound(buildRandomSlice()); ReadOptions other = new ReadOptions(opt); assertThat(opt.verifyChecksums()).isEqualTo(other.verifyChecksums()); assertThat(opt.fillCache()).isEqualTo(other.fillCache()); assertThat(Arrays.equals(opt.iterateUpperBound().data(), other.iterateUpperBound().data())).isTrue(); + assertThat(Arrays.equals(opt.iterateLowerBound().data(), other.iterateLowerBound().data())).isTrue(); } } @@ -237,6 +255,22 @@ public class ReadOptionsTest { } } + @Test + public void failSetIterateLowerBoundUninitialized() { + try (final ReadOptions readOptions = + setupUninitializedReadOptions(exception)) { + readOptions.setIterateLowerBound(null); + } + } + + @Test + public void failIterateLowerBoundUninitialized() { + try (final ReadOptions readOptions = + setupUninitializedReadOptions(exception)) { + readOptions.iterateLowerBound(); + } + } + private ReadOptions setupUninitializedReadOptions( ExpectedException exception) { final ReadOptions readOptions = new ReadOptions(); -- GitLab