Skip to content

Commit

Permalink
Merge pull request #107 from mach-kernel/112524/jni-append-bytes
Browse files Browse the repository at this point in the history
Allow appender to accept byte[]
  • Loading branch information
Mause authored Dec 13, 2024
2 parents 9e873fa + 024abc4 commit 998d0e4
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 2 deletions.
10 changes: 10 additions & 0 deletions src/jni/duckdb_java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,16 @@ void _duckdb_jdbc_appender_append_string(JNIEnv *env, jclass, jobject appender_r
get_appender(env, appender_ref_buf)->Append(string_value.c_str());
}

void _duckdb_jdbc_appender_append_bytes(JNIEnv *env, jclass, jobject appender_ref_buf, jbyteArray value) {
if (env->IsSameObject(value, NULL)) {
get_appender(env, appender_ref_buf)->Append<std::nullptr_t>(nullptr);
return;
}

auto string_value = byte_array_to_string(env, value);
get_appender(env, appender_ref_buf)->Append(Value::BLOB_RAW(string_value));
}

void _duckdb_jdbc_appender_append_null(JNIEnv *env, jclass, jobject appender_ref_buf) {
get_appender(env, appender_ref_buf)->Append<std::nullptr_t>(nullptr);
}
Expand Down
10 changes: 10 additions & 0 deletions src/jni/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,16 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1appe
}
}

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1bytes(JNIEnv * env, jclass param0, jobject param1, jbyteArray param2) {
try {
return _duckdb_jdbc_appender_append_bytes(env, param0, param1, param2);
} catch (const std::exception &e) {
duckdb::ErrorData error(e);
ThrowJNI(env, error.Message().c_str());

}
}

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1timestamp(JNIEnv * env, jclass param0, jobject param1, jlong param2) {
try {
return _duckdb_jdbc_appender_append_timestamp(env, param0, param1, param2);
Expand Down
4 changes: 4 additions & 0 deletions src/jni/functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ void _duckdb_jdbc_appender_append_string(JNIEnv * env, jclass param0, jobject pa

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1string(JNIEnv * env, jclass param0, jobject param1, jbyteArray param2);

void _duckdb_jdbc_appender_append_bytes(JNIEnv * env, jclass param0, jobject param1, jbyteArray param2);

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1bytes(JNIEnv * env, jclass param0, jobject param1, jbyteArray param2);

void _duckdb_jdbc_appender_append_timestamp(JNIEnv * env, jclass param0, jobject param1, jlong param2);

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1timestamp(JNIEnv * env, jclass param0, jobject param1, jlong param2);
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/duckdb/DuckDBAppender.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ public void append(String value) throws SQLException {
}
}

public void append(byte[] value) throws SQLException {
if (value == null) {
DuckDBNative.duckdb_jdbc_appender_append_null(appender_ref);
} else {
DuckDBNative.duckdb_jdbc_appender_append_bytes(appender_ref, value);
}
}

protected void finalize() throws Throwable {
close();
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/duckdb/DuckDBNative.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ protected static native void duckdb_jdbc_appender_append_double(ByteBuffer appen
protected static native void duckdb_jdbc_appender_append_string(ByteBuffer appender_ref, byte[] value)
throws SQLException;

protected static native void duckdb_jdbc_appender_append_bytes(ByteBuffer appender_ref, byte[] value)
throws SQLException;

protected static native void duckdb_jdbc_appender_append_timestamp(ByteBuffer appender_ref, long value)
throws SQLException;

Expand Down
62 changes: 60 additions & 2 deletions src/test/java/org/duckdb/TestDuckDBJDBC.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.sql.*;
import java.time.Instant;
import java.time.LocalDate;
Expand Down Expand Up @@ -2692,7 +2693,11 @@ public static void test_appender_null_integer() throws Exception {
DuckDBAppender appender = conn.createAppender(DuckDBConnection.DEFAULT_SCHEMA, "data");

appender.beginRow();
appender.append(null);

// int foo = null won't compile
// Integer foo = null will compile, but NPE on cast to int
// So, use the String appender to pass an arbitrary null value
appender.append((String) null);
appender.endRow();
appender.flush();
appender.close();
Expand All @@ -2717,7 +2722,31 @@ public static void test_appender_null_varchar() throws Exception {
DuckDBAppender appender = conn.createAppender(DuckDBConnection.DEFAULT_SCHEMA, "data");

appender.beginRow();
appender.append(null);
appender.append((String) null);
appender.endRow();
appender.flush();
appender.close();

ResultSet results = stmt.executeQuery("SELECT * FROM data");
assertTrue(results.next());
assertNull(results.getString(1));
assertTrue(results.wasNull());

results.close();
stmt.close();
conn.close();
}

public static void test_appender_null_blob() throws Exception {
DuckDBConnection conn = DriverManager.getConnection(JDBC_URL).unwrap(DuckDBConnection.class);
Statement stmt = conn.createStatement();

stmt.execute("CREATE TABLE data (a BLOB)");

DuckDBAppender appender = conn.createAppender(DuckDBConnection.DEFAULT_SCHEMA, "data");

appender.beginRow();
appender.append((byte[]) null);
appender.endRow();
appender.flush();
appender.close();
Expand All @@ -2732,6 +2761,35 @@ public static void test_appender_null_varchar() throws Exception {
conn.close();
}

public static void test_appender_roundtrip_blob() throws Exception {
DuckDBConnection conn = DriverManager.getConnection(JDBC_URL).unwrap(DuckDBConnection.class);
Statement stmt = conn.createStatement();

stmt.execute("CREATE TABLE data (a BLOB)");

DuckDBAppender appender = conn.createAppender(DuckDBConnection.DEFAULT_SCHEMA, "data");
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] data = new byte[512];
random.nextBytes(data);

appender.beginRow();
appender.append(data);
appender.endRow();
appender.flush();
appender.close();

ResultSet results = stmt.executeQuery("SELECT * FROM data");
assertTrue(results.next());

Blob resultBlob = results.getBlob(1);
byte[] resultBytes = resultBlob.getBytes(1, (int) resultBlob.length());
assertTrue(Arrays.equals(resultBytes, data), "byte[] data is round tripped untouched");

results.close();
stmt.close();
conn.close();
}

public static void test_get_catalog() throws Exception {
Connection conn = DriverManager.getConnection(JDBC_URL);
ResultSet rs = conn.getMetaData().getCatalogs();
Expand Down

0 comments on commit 998d0e4

Please sign in to comment.