Skip to content

Commit 9adfa99

Browse files
committed
fix(jni): free QueryResult on Arrow entry-point failure
The four Arrow JNI entry points (createArrowTable, createArrowRelTable, createArrowRelTableCSR, dropArrowTable) freed only their wrapper on failure. The C API's setQueryResult (ladybug/src/c_api/connection.cpp:77) releases the C++ QueryResult into the out-param *before* checking success, so on the setQueryResult failure branch the out-param owned a QueryResult that the wrapper's delete did not touch and lbug_query_result_destroy was not called. Linear leak of ~432 bytes per failed call, verified by RSS measurement in issue #13. Call lbug_query_result_destroy(queryResult) before delete on the failure branch of all four. lbug_query_result_destroy is null-safe and a no-op when setQueryResult did not run, so this is safe for both the setQueryResult and the catch-block failure modes. Closes #13
1 parent 0111e0a commit 9adfa99

1 file changed

Lines changed: 16 additions & 0 deletions

File tree

src/jni/lbug_java.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,10 @@ JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionCreateArrowTab
853853
auto state = lbug_connection_create_arrow_table(conn, table.c_str(), schema, arrays,
854854
static_cast<uint64_t>(numArrays), queryResult);
855855
if (state != LbugSuccess) {
856+
// The C API can release a QueryResult into the out-param before
857+
// reporting failure (see Connection::setQueryResult). The wrapper
858+
// does not own it, so `delete` alone would leak.
859+
lbug_query_result_destroy(queryResult);
856860
delete queryResult;
857861
throwLastError(env, "Failed to create Arrow table");
858862
return jobject();
@@ -880,6 +884,10 @@ JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionCreateArrowRel
880884
auto state = lbug_connection_create_arrow_rel_table(conn, table.c_str(), srcTable.c_str(),
881885
dstTable.c_str(), schema, arrays, static_cast<uint64_t>(numArrays), queryResult);
882886
if (state != LbugSuccess) {
887+
// The C API can release a QueryResult into the out-param before
888+
// reporting failure (see Connection::setQueryResult). The wrapper
889+
// does not own it, so `delete` alone would leak.
890+
lbug_query_result_destroy(queryResult);
883891
delete queryResult;
884892
throwLastError(env, "Failed to create Arrow relationship table");
885893
return jobject();
@@ -919,6 +927,10 @@ JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionCreateArrowRel
919927
static_cast<uint64_t>(numIndicesArrays), indptrSchema, indptrArrays,
920928
static_cast<uint64_t>(numIndptrArrays), dstColumnPtr, queryResult);
921929
if (state != LbugSuccess) {
930+
// The C API can release a QueryResult into the out-param before
931+
// reporting failure (see Connection::setQueryResult). The wrapper
932+
// does not own it, so `delete` alone would leak.
933+
lbug_query_result_destroy(queryResult);
922934
delete queryResult;
923935
throwLastError(env, "Failed to create Arrow CSR relationship table");
924936
return jobject();
@@ -940,6 +952,10 @@ JNIEXPORT jobject JNICALL Java_com_ladybugdb_Native_lbugConnectionDropArrowTable
940952
auto* queryResult = new lbug_query_result();
941953
auto state = lbug_connection_drop_arrow_table(conn, table.c_str(), queryResult);
942954
if (state != LbugSuccess) {
955+
// The C API can release a QueryResult into the out-param before
956+
// reporting failure (see Connection::setQueryResult). The wrapper
957+
// does not own it, so `delete` alone would leak.
958+
lbug_query_result_destroy(queryResult);
943959
delete queryResult;
944960
throwLastError(env, "Failed to drop Arrow table");
945961
return jobject();

0 commit comments

Comments
 (0)