From bdd88dad0dba0ff63eea550bbeb5646e621e1017 Mon Sep 17 00:00:00 2001 From: Christopher Wirt Date: Wed, 4 Nov 2026 14:07:23 +0000 Subject: [PATCH] feat(jni): list the editions a session knows Session.knownEditions() returns the name of every edition the session knows, each one a name enableEdition accepts. Callers can check an edition without enabling it on a spare session and matching the error. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Christopher Wirt --- .../src/main/java/dev/vortex/api/Session.java | 9 ++++ .../java/dev/vortex/jni/NativeSession.java | 3 ++ .../test/java/dev/vortex/api/SessionTest.java | 42 +++++++++++++++++++ vortex-jni/src/session.rs | 29 +++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 java/vortex-jni/src/test/java/dev/vortex/api/SessionTest.java diff --git a/java/vortex-jni/src/main/java/dev/vortex/api/Session.java b/java/vortex-jni/src/main/java/dev/vortex/api/Session.java index 399498f030f..073ab2c92e1 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/api/Session.java +++ b/java/vortex-jni/src/main/java/dev/vortex/api/Session.java @@ -6,6 +6,7 @@ import com.google.common.base.Preconditions; import dev.vortex.VortexCleaner; import dev.vortex.jni.NativeSession; +import java.util.Set; /** * Handle to a native Vortex session. The session owns a current-thread async runtime and is the entry point for opening @@ -40,6 +41,14 @@ public void enableEdition(String edition) { NativeSession.enableEdition(pointer, edition); } + /** + * The name of every edition this session knows, for example {@code core2025.10.0}. Each one is a name + * {@link #enableEdition} accepts. + */ + public Set knownEditions() { + return Set.of(NativeSession.knownEditions(pointer)); + } + /** Internal: returns the native pointer. Do not free directly. */ public long nativePointer() { return pointer; diff --git a/java/vortex-jni/src/main/java/dev/vortex/jni/NativeSession.java b/java/vortex-jni/src/main/java/dev/vortex/jni/NativeSession.java index 3133a12336c..b2113ae82d1 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/jni/NativeSession.java +++ b/java/vortex-jni/src/main/java/dev/vortex/jni/NativeSession.java @@ -22,4 +22,7 @@ private NativeSession() {} * this call. Throws a {@link RuntimeException} if the edition is not known. */ public static native void enableEdition(long pointer, String edition); + + /** The name of every edition the session knows, each one a name {@link #enableEdition} accepts. */ + public static native String[] knownEditions(long pointer); } diff --git a/java/vortex-jni/src/test/java/dev/vortex/api/SessionTest.java b/java/vortex-jni/src/test/java/dev/vortex/api/SessionTest.java new file mode 100644 index 00000000000..9420350c7ea --- /dev/null +++ b/java/vortex-jni/src/test/java/dev/vortex/api/SessionTest.java @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +package dev.vortex.api; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; + +public final class SessionTest { + + @Test + public void knownEditionsIncludeTheCoreEditionsAndTheListLayoutEdition() { + Set known = Session.create().knownEditions(); + + assertTrue(known.contains("core2025.10.0"), known::toString); + assertTrue(known.contains("core2026.08.3"), known::toString); + assertTrue(known.contains("qc2026.11.0"), known::toString); + assertFalse(known.contains("core2099.01.0"), known::toString); + } + + @Test + public void everyKnownEditionCanBeEnabled() { + Session session = Session.create(); + + for (String edition : session.knownEditions()) { + assertDoesNotThrow(() -> session.enableEdition(edition), edition); + } + } + + @Test + public void anEditionThatIsntKnownCantBeEnabled() { + Session session = Session.create(); + + assertFalse(session.knownEditions().contains("core2099.01.0")); + assertThrows(RuntimeException.class, () -> session.enableEdition("core2099.01.0")); + } +} diff --git a/vortex-jni/src/session.rs b/vortex-jni/src/session.rs index 5bd27e89990..be65755daf7 100644 --- a/vortex-jni/src/session.rs +++ b/vortex-jni/src/session.rs @@ -5,8 +5,10 @@ use jni::EnvUnowned; use jni::objects::JClass; +use jni::objects::JObject; use jni::objects::JString; use jni::sys::jlong; +use jni::sys::jobjectArray; use vortex::VortexSessionDefault; use vortex::editions::CORE_2026_08_3; use vortex::editions::EditionSessionExt; @@ -87,3 +89,30 @@ pub extern "system" fn Java_dev_vortex_jni_NativeSession_enableEdition( Ok(()) }) } + +/// Returns the name of every edition the session knows, for example `core2025.10.0`, as a +/// `String[]`. Each one is a name `enableEdition` accepts. +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_vortex_jni_NativeSession_knownEditions( + mut env: EnvUnowned, + _class: JClass, + pointer: jlong, +) -> jobjectArray { + try_or_throw(&mut env, |env| { + // SAFETY: pointer must have been created by `newSession` and not yet freed. + let session = unsafe { session_ref(pointer) }; + let editions = session.editions().editions(); + let names = env.new_object_array( + i32::try_from(editions.len()) + .map_err(|_| vortex_err!("edition count exceeds Java array range"))?, + jni::jni_str!("java/lang/String"), + JObject::null(), + )?; + for (index, edition) in editions.iter().enumerate() { + let name = env.new_string(edition.id.to_string())?; + names.set_element(env, index, &name)?; + env.delete_local_ref(name); + } + Ok(names.into_raw()) + }) +}