Skip to content

Commit 3b9721c

Browse files
committed
Bring Java bindings up to C API parity
1 parent 8fdda16 commit 3b9721c

13 files changed

Lines changed: 649 additions & 20 deletions

File tree

.github/workflows/java.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
name: Build and test
1818
strategy:
1919
matrix:
20-
runner: [ubuntu-24.04, ubuntu-24.04-arm, macos-14]
20+
runner: [ubuntu-24.04, ubuntu-24.04-arm, macos-latest]
2121
runs-on: ${{ matrix.runner }}
2222
steps:
2323
- uses: actions/checkout@v4
@@ -35,10 +35,7 @@ jobs:
3535

3636
- name: Prepare test datasets
3737
run: |
38-
git clone --depth 1 https://github.com/LadybugDB/go-ladybug ../go-ladybug
39-
mkdir -p ../../dataset/tinysnb ../../dataset/tinysnb-serial
40-
cp -R ../go-ladybug/dataset/tinysnb/. ../../dataset/tinysnb/
41-
cp ../go-ladybug/dataset/tinysnb/vMoviesSerial.csv ../../dataset/tinysnb-serial/vMovies.csv
38+
git clone --depth 1 https://github.com/ladybugdb/dataset.git ./dataset
4239
4340
- name: Build
4441
env:

build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ repositories {
3636
}
3737

3838
dependencies {
39+
implementation 'org.apache.arrow:arrow-c-data:18.2.0'
40+
implementation 'org.apache.arrow:arrow-vector:18.2.0'
41+
implementation 'org.apache.arrow:arrow-memory-core:18.2.0'
3942
testImplementation platform('org.junit:junit-bom:5.10.0')
4043
testImplementation 'org.junit.jupiter:junit-jupiter'
44+
testRuntimeOnly 'org.apache.arrow:arrow-memory-netty:18.2.0'
4145
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
4246
}
4347

src/jni/lbug_java.cpp

Lines changed: 270 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,19 @@ void bindJavaParamsToPreparedStatement(JNIEnv* env, lbug_prepared_statement* pre
524524
}
525525
}
526526

527+
void throwLastError(JNIEnv* env, const char* fallback) {
528+
if (auto* errorMessage = lbug_get_last_error()) {
529+
throwJNIException(env, errorMessage);
530+
free(errorMessage);
531+
} else {
532+
throwJNIException(env, fallback);
533+
}
534+
}
535+
536+
jobject createQueryResultObject(JNIEnv* env, lbug_query_result* queryResult) {
537+
return createJavaObject(env, queryResult, J_C_QueryResult, J_C_QueryResult_F_qr_ref);
538+
}
539+
527540
/**
528541
* All Database native functions
529542
*/
@@ -550,16 +563,20 @@ JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugNativeReloadLibrary(JNIEnv*
550563
}
551564
}
552565

553-
JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugDatabaseInit(JNIEnv* env, jclass,
554-
jstring databasePath, jlong bufferPoolSize, jboolean enableCompression, jboolean readOnly,
555-
jlong maxDbSize, jboolean autoCheckpoint, jlong checkpointThreshold,
556-
jboolean throwOnWalReplayFailure, jboolean enableChecksums) {
566+
JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugDatabaseInitExtended(JNIEnv* env, jclass,
567+
jstring databasePath, jlong bufferPoolSize, jlong maxNumThreads, jboolean enableCompression,
568+
jboolean readOnly, jlong maxDbSize, jboolean autoCheckpoint, jlong checkpointThreshold,
569+
jboolean throwOnWalReplayFailure, jboolean enableChecksums, jboolean enableMultiWrites,
570+
jboolean enableDefaultHashIndex) {
557571
try {
558572
const char* path = env->GetStringUTFChars(databasePath, JNI_FALSE);
559573
auto systemConfig = lbug_default_system_config();
560574
if (bufferPoolSize != 0) {
561575
systemConfig.buffer_pool_size = static_cast<uint64_t>(bufferPoolSize);
562576
}
577+
if (maxNumThreads != 0) {
578+
systemConfig.max_num_threads = static_cast<uint64_t>(maxNumThreads);
579+
}
563580
systemConfig.enable_compression = enableCompression;
564581
systemConfig.read_only = readOnly;
565582
if (maxDbSize != 0) {
@@ -578,6 +595,8 @@ JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugDatabaseInit(JNIEnv* env,
578595
}
579596
systemConfig.throw_on_wal_replay_failure = throwOnWalReplayFailure;
580597
systemConfig.enable_checksums = enableChecksums;
598+
systemConfig.enable_multi_writes = enableMultiWrites;
599+
systemConfig.enable_default_hash_index = enableDefaultHashIndex;
581600
try {
582601
auto* db = new lbug_database();
583602
auto state = lbug_database_init(path, systemConfig, db);
@@ -605,6 +624,15 @@ JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugDatabaseInit(JNIEnv* env,
605624
return 0;
606625
}
607626

627+
JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugDatabaseInit(JNIEnv* env, jclass clazz,
628+
jstring databasePath, jlong bufferPoolSize, jboolean enableCompression, jboolean readOnly,
629+
jlong maxDbSize, jboolean autoCheckpoint, jlong checkpointThreshold,
630+
jboolean throwOnWalReplayFailure, jboolean enableChecksums) {
631+
return Java_com_ladybugdb_Native_lbugDatabaseInitExtended(env, clazz, databasePath,
632+
bufferPoolSize, 0, enableCompression, readOnly, maxDbSize, autoCheckpoint,
633+
checkpointThreshold, throwOnWalReplayFailure, enableChecksums, false, true);
634+
}
635+
608636
JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugDatabaseDestroy(JNIEnv* env, jclass,
609637
jobject thisDB) {
610638
try {
@@ -792,6 +820,118 @@ JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugConnectionSetQueryTimeout(J
792820
}
793821
}
794822

823+
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionCreateArrowTable(JNIEnv* env,
824+
jclass, jobject thisConn, jstring tableName, jlong arrowSchemaAddress, jlong arrowArraysAddress,
825+
jlong numArrays) {
826+
try {
827+
auto* conn = getConnection(env, thisConn);
828+
std::string table = jstringToUtf8String(env, tableName);
829+
auto* schema = reinterpret_cast<ArrowSchema*>(static_cast<uintptr_t>(arrowSchemaAddress));
830+
auto* arrays = reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(arrowArraysAddress));
831+
auto* queryResult = new lbug_query_result();
832+
auto state = lbug_connection_create_arrow_table(conn, table.c_str(), schema, arrays,
833+
static_cast<uint64_t>(numArrays), queryResult);
834+
if (state != LbugSuccess) {
835+
delete queryResult;
836+
throwLastError(env, "Failed to create Arrow table");
837+
return jobject();
838+
}
839+
return createQueryResultObject(env, queryResult);
840+
} catch (const Exception& e) {
841+
throwJNIException(env, e.what());
842+
} catch (...) {
843+
throwJNIException(env, "Unknown Error");
844+
}
845+
return jobject();
846+
}
847+
848+
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionCreateArrowRelTable(JNIEnv* env,
849+
jclass, jobject thisConn, jstring tableName, jstring srcTableName, jstring dstTableName,
850+
jlong arrowSchemaAddress, jlong arrowArraysAddress, jlong numArrays) {
851+
try {
852+
auto* conn = getConnection(env, thisConn);
853+
std::string table = jstringToUtf8String(env, tableName);
854+
std::string srcTable = jstringToUtf8String(env, srcTableName);
855+
std::string dstTable = jstringToUtf8String(env, dstTableName);
856+
auto* schema = reinterpret_cast<ArrowSchema*>(static_cast<uintptr_t>(arrowSchemaAddress));
857+
auto* arrays = reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(arrowArraysAddress));
858+
auto* queryResult = new lbug_query_result();
859+
auto state = lbug_connection_create_arrow_rel_table(conn, table.c_str(), srcTable.c_str(),
860+
dstTable.c_str(), schema, arrays, static_cast<uint64_t>(numArrays), queryResult);
861+
if (state != LbugSuccess) {
862+
delete queryResult;
863+
throwLastError(env, "Failed to create Arrow relationship table");
864+
return jobject();
865+
}
866+
return createQueryResultObject(env, queryResult);
867+
} catch (const Exception& e) {
868+
throwJNIException(env, e.what());
869+
} catch (...) {
870+
throwJNIException(env, "Unknown Error");
871+
}
872+
return jobject();
873+
}
874+
875+
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionCreateArrowRelTableCSR(
876+
JNIEnv* env, jclass, jobject thisConn, jstring tableName, jstring srcTableName,
877+
jstring dstTableName, jlong indicesSchemaAddress, jlong indicesArraysAddress,
878+
jlong numIndicesArrays, jlong indptrSchemaAddress, jlong indptrArraysAddress,
879+
jlong numIndptrArrays, jstring dstColumnName) {
880+
try {
881+
auto* conn = getConnection(env, thisConn);
882+
std::string table = jstringToUtf8String(env, tableName);
883+
std::string srcTable = jstringToUtf8String(env, srcTableName);
884+
std::string dstTable = jstringToUtf8String(env, dstTableName);
885+
std::string dstColumn = jstringToUtf8String(env, dstColumnName);
886+
auto* indicesSchema =
887+
reinterpret_cast<ArrowSchema*>(static_cast<uintptr_t>(indicesSchemaAddress));
888+
auto* indicesArrays =
889+
reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(indicesArraysAddress));
890+
auto* indptrSchema =
891+
reinterpret_cast<ArrowSchema*>(static_cast<uintptr_t>(indptrSchemaAddress));
892+
auto* indptrArrays =
893+
reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(indptrArraysAddress));
894+
auto* queryResult = new lbug_query_result();
895+
auto* dstColumnPtr = dstColumn.empty() ? nullptr : dstColumn.c_str();
896+
auto state = lbug_connection_create_arrow_rel_table_csr(conn, table.c_str(),
897+
srcTable.c_str(), dstTable.c_str(), indicesSchema, indicesArrays,
898+
static_cast<uint64_t>(numIndicesArrays), indptrSchema, indptrArrays,
899+
static_cast<uint64_t>(numIndptrArrays), dstColumnPtr, queryResult);
900+
if (state != LbugSuccess) {
901+
delete queryResult;
902+
throwLastError(env, "Failed to create Arrow CSR relationship table");
903+
return jobject();
904+
}
905+
return createQueryResultObject(env, queryResult);
906+
} catch (const Exception& e) {
907+
throwJNIException(env, e.what());
908+
} catch (...) {
909+
throwJNIException(env, "Unknown Error");
910+
}
911+
return jobject();
912+
}
913+
914+
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionDropArrowTable(JNIEnv* env,
915+
jclass, jobject thisConn, jstring tableName) {
916+
try {
917+
auto* conn = getConnection(env, thisConn);
918+
std::string table = jstringToUtf8String(env, tableName);
919+
auto* queryResult = new lbug_query_result();
920+
auto state = lbug_connection_drop_arrow_table(conn, table.c_str(), queryResult);
921+
if (state != LbugSuccess) {
922+
delete queryResult;
923+
throwLastError(env, "Failed to drop Arrow table");
924+
return jobject();
925+
}
926+
return createQueryResultObject(env, queryResult);
927+
} catch (const Exception& e) {
928+
throwJNIException(env, e.what());
929+
} catch (...) {
930+
throwJNIException(env, "Unknown Error");
931+
}
932+
return jobject();
933+
}
934+
795935
/**
796936
* All PreparedStatement native functions
797937
*/
@@ -822,6 +962,19 @@ JNIEXPORT jboolean JNICALL Java_com_ladybugdb_Native_lbugPreparedStatementIsSucc
822962
return jboolean();
823963
}
824964

965+
JNIEXPORT jboolean JNICALL Java_com_ladybugdb_Native_lbugPreparedStatementIsReadOnly(JNIEnv* env,
966+
jclass, jobject thisPS) {
967+
try {
968+
auto* ps = getPreparedStatement(env, thisPS);
969+
return static_cast<jboolean>(lbug_prepared_statement_is_read_only(ps));
970+
} catch (const Exception& e) {
971+
throwJNIException(env, e.what());
972+
} catch (...) {
973+
throwJNIException(env, "Unknown Error");
974+
}
975+
return jboolean();
976+
}
977+
825978
JNIEXPORT jstring JNICALL Java_com_ladybugdb_Native_lbugPreparedStatementGetErrorMessage(
826979
JNIEnv* env, jclass, jobject thisPS) {
827980
try {
@@ -1061,6 +1214,35 @@ JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugQueryResultResetIterator(JN
10611214
}
10621215
}
10631216

1217+
JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugQueryResultGetArrowSchema(JNIEnv* env, jclass,
1218+
jobject thisQR, jlong arrowSchemaAddress) {
1219+
try {
1220+
auto* qr = getQueryResult(env, thisQR);
1221+
auto* schema = reinterpret_cast<ArrowSchema*>(static_cast<uintptr_t>(arrowSchemaAddress));
1222+
throwIfError(lbug_query_result_get_arrow_schema(qr, schema),
1223+
"Failed to get Arrow schema");
1224+
} catch (const Exception& e) {
1225+
throwJNIException(env, e.what());
1226+
} catch (...) {
1227+
throwJNIException(env, "Unknown Error");
1228+
}
1229+
}
1230+
1231+
JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugQueryResultGetNextArrowChunk(JNIEnv* env,
1232+
jclass, jobject thisQR, jlong chunkSize, jlong arrowArrayAddress) {
1233+
try {
1234+
auto* qr = getQueryResult(env, thisQR);
1235+
auto* array = reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(arrowArrayAddress));
1236+
throwIfError(lbug_query_result_get_next_arrow_chunk(qr, static_cast<int64_t>(chunkSize),
1237+
array),
1238+
"Failed to get next Arrow chunk");
1239+
} catch (const Exception& e) {
1240+
throwJNIException(env, e.what());
1241+
} catch (...) {
1242+
throwJNIException(env, "Unknown Error");
1243+
}
1244+
}
1245+
10641246
/**
10651247
* All FlatTuple native functions
10661248
*/
@@ -1605,6 +1787,46 @@ JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugValueGetMapValue(JNIEnv*
16051787
return jobject();
16061788
}
16071789

1790+
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugValueGetRecursiveRelNodeList(JNIEnv* env,
1791+
jclass, jobject thisValue) {
1792+
try {
1793+
auto* v = getValue(env, thisValue);
1794+
auto* nodes = new lbug_value();
1795+
if (lbug_value_get_recursive_rel_node_list(v, nodes) != LbugSuccess) {
1796+
delete nodes;
1797+
return nullptr;
1798+
}
1799+
jobject result = createJavaObject(env, nodes, J_C_Value, J_C_Value_F_v_ref);
1800+
env->SetBooleanField(result, J_C_Value_F_isOwnedByCPP, static_cast<jboolean>(true));
1801+
return result;
1802+
} catch (const Exception& e) {
1803+
throwJNIException(env, e.what());
1804+
} catch (...) {
1805+
throwJNIException(env, "Unknown Error");
1806+
}
1807+
return jobject();
1808+
}
1809+
1810+
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugValueGetRecursiveRelRelList(JNIEnv* env,
1811+
jclass, jobject thisValue) {
1812+
try {
1813+
auto* v = getValue(env, thisValue);
1814+
auto* rels = new lbug_value();
1815+
if (lbug_value_get_recursive_rel_rel_list(v, rels) != LbugSuccess) {
1816+
delete rels;
1817+
return nullptr;
1818+
}
1819+
jobject result = createJavaObject(env, rels, J_C_Value, J_C_Value_F_v_ref);
1820+
env->SetBooleanField(result, J_C_Value_F_isOwnedByCPP, static_cast<jboolean>(true));
1821+
return result;
1822+
} catch (const Exception& e) {
1823+
throwJNIException(env, e.what());
1824+
} catch (...) {
1825+
throwJNIException(env, "Unknown Error");
1826+
}
1827+
return jobject();
1828+
}
1829+
16081830
JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugValueGetDataType(JNIEnv* env, jclass,
16091831
jobject thisValue) {
16101832
try {
@@ -2193,6 +2415,50 @@ JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugValueGetStructIndex(JNIEnv
21932415
return jlong();
21942416
}
21952417

2418+
JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugArrowArrayAllocate(JNIEnv* env, jclass,
2419+
jlong numArrays) {
2420+
try {
2421+
if (numArrays <= 0) {
2422+
throw NotImplementedException("Number of Arrow arrays must be positive");
2423+
}
2424+
auto* arrays = new ArrowArray[static_cast<uint64_t>(numArrays)]();
2425+
return static_cast<jlong>(reinterpret_cast<uint64_t>(arrays));
2426+
} catch (const Exception& e) {
2427+
throwJNIException(env, e.what());
2428+
} catch (...) {
2429+
throwJNIException(env, "Unknown Error");
2430+
}
2431+
return 0;
2432+
}
2433+
2434+
JNIEXPORT jlong JNICALL Java_com_ladybugdb_Native_lbugArrowArrayGetAddress(JNIEnv* env, jclass,
2435+
jlong arrowArraysAddress, jlong index) {
2436+
try {
2437+
if (index < 0) {
2438+
throw NotImplementedException("Arrow array index must be non-negative");
2439+
}
2440+
auto* arrays = reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(arrowArraysAddress));
2441+
return static_cast<jlong>(reinterpret_cast<uint64_t>(&arrays[index]));
2442+
} catch (const Exception& e) {
2443+
throwJNIException(env, e.what());
2444+
} catch (...) {
2445+
throwJNIException(env, "Unknown Error");
2446+
}
2447+
return 0;
2448+
}
2449+
2450+
JNIEXPORT void JNICALL Java_com_ladybugdb_Native_lbugArrowArrayFree(JNIEnv* env, jclass,
2451+
jlong arrowArraysAddress) {
2452+
try {
2453+
auto* arrays = reinterpret_cast<ArrowArray*>(static_cast<uintptr_t>(arrowArraysAddress));
2454+
delete[] arrays;
2455+
} catch (const Exception& e) {
2456+
throwJNIException(env, e.what());
2457+
} catch (...) {
2458+
throwJNIException(env, "Unknown Error");
2459+
}
2460+
}
2461+
21962462
JNIEXPORT jstring JNICALL Java_com_ladybugdb_Native_lbugGetVersion(JNIEnv* env, jclass) {
21972463
try {
21982464
return takeOwnedCStringAsJString(env, lbug_get_version());

0 commit comments

Comments
 (0)