From 5742c0a2ec48a3a464e277c15382bd2a9978f3f9 Mon Sep 17 00:00:00 2001 From: Andrey Koshchiy Date: Thu, 31 Oct 2024 01:01:51 +0300 Subject: [PATCH 1/2] fix: blob usage after resultset move --- src/jni/duckdb_java.cpp | 12 +++++++++--- src/main/java/org/duckdb/DuckDBVector.java | 8 ++------ src/test/java/org/duckdb/TestDuckDBJDBC.java | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/jni/duckdb_java.cpp b/src/jni/duckdb_java.cpp index bd445885..d8a03309 100644 --- a/src/jni/duckdb_java.cpp +++ b/src/jni/duckdb_java.cpp @@ -105,6 +105,7 @@ static jmethodID J_Object_toString; static jclass J_DuckDBTime; + void ThrowJNI(JNIEnv *env, const char *message) { D_ASSERT(J_SQLException); env->ThrowNew(J_SQLException, message); @@ -994,15 +995,20 @@ jobject ProcessVector(JNIEnv *env, Connection *conn_ref, Vector &vec, idx_t row_ break; } case LogicalTypeId::BLOB: - varlen_data = env->NewObjectArray(row_count, J_ByteBuffer, nullptr); + varlen_data = env->NewObjectArray(row_count, J_ByteArray, nullptr); for (idx_t row_idx = 0; row_idx < row_count; row_idx++) { if (FlatVector::IsNull(vec, row_idx)) { continue; } auto &d_str = ((string_t *)FlatVector::GetData(vec))[row_idx]; - auto j_obj = env->NewDirectByteBuffer((void *)d_str.GetData(), d_str.GetSize()); - env->SetObjectArrayElement(varlen_data, row_idx, j_obj); + + auto j_arr = env->NewByteArray(d_str.GetSize()); + auto j_arr_el = env->GetByteArrayElements(j_arr, nullptr); + memcpy((void *)j_arr_el, (void *)d_str.GetData(), d_str.GetSize()); + env->ReleaseByteArrayElements(j_arr, j_arr_el, 0); + + env->SetObjectArrayElement(varlen_data, row_idx, j_arr); } break; case LogicalTypeId::UUID: diff --git a/src/main/java/org/duckdb/DuckDBVector.java b/src/main/java/org/duckdb/DuckDBVector.java index fe62174c..d6d3c240 100644 --- a/src/main/java/org/duckdb/DuckDBVector.java +++ b/src/main/java/org/duckdb/DuckDBVector.java @@ -278,7 +278,7 @@ Blob getBlob(int idx) throws SQLException { return null; } if (isType(DuckDBColumnType.BLOB)) { - return new DuckDBResultSet.DuckDBBlobResult((ByteBuffer) varlen_data[idx]); + return new DuckDBResultSet.DuckDBBlobResult(ByteBuffer.wrap((byte[]) varlen_data[idx])); } throw new SQLFeatureNotSupportedException("getBlob"); @@ -290,11 +290,7 @@ byte[] getBytes(int idx) throws SQLException { } if (isType(DuckDBColumnType.BLOB)) { - ByteBuffer bb = (ByteBuffer) varlen_data[idx]; - bb.position(0); - byte[] bytes = new byte[bb.remaining()]; - bb.get(bytes); - return bytes; + return (byte[]) varlen_data[idx]; } throw new SQLFeatureNotSupportedException("getBytes"); diff --git a/src/test/java/org/duckdb/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index 3e3fc6f3..560a3430 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -46,6 +46,7 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; +import java.util.HexFormat; import java.util.List; import java.util.ListIterator; import java.util.Locale; @@ -4604,6 +4605,22 @@ public static void test_metadata_get_index_info() throws Exception { } } + public static void test_blob_after_rs_next() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + try (Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT 'AAAA'::BLOB;")) { + Blob blob = null; + while (rs.next()) { + blob = rs.getBlob(1); + } + String expected = HexFormat.of().formatHex(new byte[]{'A', 'A', 'A', 'A'}); + String actual = HexFormat.of().formatHex(blob.getBinaryStream().readAllBytes()); + assertEquals(actual, expected); + } + } + } + } + public static void main(String[] args) throws Exception { System.exit(runTests(args, TestDuckDBJDBC.class, TestExtensionTypes.class)); } From 6ca3170916415df03ac1e7d78fc3e389afdb1a81 Mon Sep 17 00:00:00 2001 From: Andrey Koshchiy Date: Thu, 31 Oct 2024 01:28:05 +0300 Subject: [PATCH 2/2] build fix --- src/jni/duckdb_java.cpp | 1 - src/test/java/org/duckdb/TestDuckDBJDBC.java | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/jni/duckdb_java.cpp b/src/jni/duckdb_java.cpp index d8a03309..8e6a620a 100644 --- a/src/jni/duckdb_java.cpp +++ b/src/jni/duckdb_java.cpp @@ -105,7 +105,6 @@ static jmethodID J_Object_toString; static jclass J_DuckDBTime; - void ThrowJNI(JNIEnv *env, const char *message) { D_ASSERT(J_SQLException); env->ThrowNew(J_SQLException, message); diff --git a/src/test/java/org/duckdb/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index 560a3430..08be7914 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -46,7 +46,6 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; -import java.util.HexFormat; import java.util.List; import java.util.ListIterator; import java.util.Locale; @@ -4613,9 +4612,7 @@ public static void test_blob_after_rs_next() throws Exception { while (rs.next()) { blob = rs.getBlob(1); } - String expected = HexFormat.of().formatHex(new byte[]{'A', 'A', 'A', 'A'}); - String actual = HexFormat.of().formatHex(blob.getBinaryStream().readAllBytes()); - assertEquals(actual, expected); + assertEquals(blob_to_string(blob), "AAAA"); } } }