diff --git a/.gitignore b/.gitignore index 4391680c3d..4f1b9b8d99 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,23 @@ tools/nodejs_api/testDb/ scripts/pre-compiled-bins/headers scripts/pre-compiled-bins/lib* scripts/pre-compiled-bins/kuzu* + +# Java API +tools/java_api/CMakeFiles/ +tools/java_api/Testing/ +tools/java_api/testdb/ +tools/java_api/testdb/ +tools/java_api_falied/ +tools/test_java_api/ +tools/java_api/cmake_install.cmake +tools/java_api/Notes.txt +tools/java_api/CMakeCache.txt +tools/java_api/CTestTestfile.cmake +tools/java_api/*.log +tools/java_api/*.jar +tools/java_api/*.dylib +tools/java_api/test.java +tools/java_api/*.class +tools/java_api/KuzuNative.h +tools/java_api/error.txt +tools/java_api/Makefile \ No newline at end of file diff --git a/tools/java_api/CMakeLists.txt b/tools/java_api/CMakeLists.txt new file mode 100644 index 0000000000..1d2a9df0e3 --- /dev/null +++ b/tools/java_api/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required (VERSION 3.11) + +set(CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS TRUE) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +project (kuzu_java_api) + +find_package(Java REQUIRED) +find_package(JNI REQUIRED) +if (JNI_FOUND) + message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}") + message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}") +endif() +include(UseJava) + +enable_testing() + +file(GLOB JAVA_SRC_FILES *.java) + +set(CMAKE_JAVA_COMPILE_FLAGS -source 1.8 -target 1.8 -encoding utf-8) +add_jar(kuzu_java ${JAVA_SRC_FILES}) +get_target_property(_jarFile kuzu_java JAR_FILE) +get_target_property(_classDir kuzu_java CLASSDIR) + +set (_stubDir "${CMAKE_CURRENT_BINARY_DIR}") +add_custom_command( + OUTPUT KuzuNative.h + COMMAND ${Java_JAVAC_EXECUTABLE} + -h . + ${JAVA_SRC_FILES} +) + +# generate libfoo.jnilib +include_directories(${JNI_INCLUDE_DIRS} ${_classDir} ${_stubDir} ../../src/include) +include_directories(../../third_party/antlr4_cypher/include) +include_directories(../../third_party/antlr4_runtime/src) +include_directories(../../third_party/spdlog) +include_directories(../../third_party/nlohmann_json) +include_directories(../../third_party/pyparse) +include_directories(../../third_party/utf8proc/include) +include_directories(../../third_party/pybind11/include) +include_directories(../../third_party/re2/include) +include_directories(../../third_party/concurrentqueue) + +find_library(KUZU NAMES kuzu PATHS ../../build/release/src) + +add_library(kuzu_java_native MODULE kuzu_java.cpp KuzuNative.h) +set_target_properties(kuzu_java_native PROPERTIES SUFFIX ".dylib") +target_link_libraries(kuzu_java_native ${JNI_LIBRARIES} ${KUZU}) + +# add test to run JNIFoo +add_test(NAME Test + COMMAND ${Java_JAVA_EXECUTABLE} + -ea + -Djava.library.path=${CMAKE_CURRENT_BINARY_DIR} + -cp ${_jarFile} tools.java_api.test) \ No newline at end of file diff --git a/tools/java_api/KuzuConnection.java b/tools/java_api/KuzuConnection.java new file mode 100644 index 0000000000..2ced41ac65 --- /dev/null +++ b/tools/java_api/KuzuConnection.java @@ -0,0 +1,104 @@ +package tools.java_api; +import java.util.Map; + +public class KuzuConnection { + + long conn_ref; + boolean destroyed = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuConnection has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public KuzuConnection(KuzuDatabase db) { + assert db != null: "Cannot create connection, database is null."; + conn_ref = KuzuNative.kuzu_connection_init(db); + } + + public void destroy() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_destroy(this); + destroyed = true; + } + + public void beginReadOnlyTransaction() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_begin_read_only_transaction(this); + } + + public void beginWriteTransaction() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_begin_write_transaction(this); + } + + public void commit() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_commit(this); + } + + public void rollback() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_rollback(this); + } + + public void setMaxNumThreadForExec(long num_threads) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_set_max_num_thread_for_exec(this, num_threads); + } + + public long getMaxNumThreadForExec () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_get_max_num_thread_for_exec(this); + } + + public KuzuQueryResult query (String queryStr) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_query(this, queryStr); + } + + public KuzuPreparedStatement prepare (String queryStr) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_prepare(this, queryStr); + } + + public KuzuQueryResult execute (KuzuPreparedStatement ps, Map m) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_execute(this, ps, m); + } + + public String getNodeTableNames () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_get_node_table_names(this); + } + + public String getRelTableNames () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_get_rel_table_names(this); + } + + public String getNodePropertyNames (String table_name) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_get_node_property_names(this, table_name); + } + + public String getRelPropertyNames (String table_name) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_connection_get_rel_property_names(this, table_name); + } + + public void interrupt () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_interrupt(this); + } + + public void setQueryTimeout (long timeout_in_ms) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_connection_set_query_timeout(this, timeout_in_ms); + } +} diff --git a/tools/java_api/KuzuDataType.java b/tools/java_api/KuzuDataType.java new file mode 100644 index 0000000000..d7846e7755 --- /dev/null +++ b/tools/java_api/KuzuDataType.java @@ -0,0 +1,59 @@ +package tools.java_api; + +public class KuzuDataType { + long dt_ref; + boolean destroyed = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuDatabase has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public void destroy() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_data_type_destroy(this); + destroyed = true; + } + + public KuzuDataType (KuzuDataTypeID id) { + dt_ref = KuzuNative.kuzu_data_type_create(id, null, 0); + } + + public KuzuDataType + (KuzuDataTypeID id, KuzuDataType child_type, long fixed_num_elements_in_list) { + dt_ref = KuzuNative.kuzu_data_type_create(id, child_type, fixed_num_elements_in_list); + } + + public KuzuDataType clone() { + if(destroyed) + return null; + else + return KuzuNative.kuzu_data_type_clone(this); + } + + public boolean equals (KuzuDataType other) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_data_type_equals(this, other); + } + + public KuzuDataTypeID getID () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_data_type_get_id(this); + } + + public KuzuDataType getChildType () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_data_type_get_child_type(this); + } + + public long getFixedNumElementsInList () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_data_type_get_fixed_num_elements_in_list(this); + } + +} diff --git a/tools/java_api/KuzuDataTypeID.java b/tools/java_api/KuzuDataTypeID.java new file mode 100644 index 0000000000..b7b7bebea0 --- /dev/null +++ b/tools/java_api/KuzuDataTypeID.java @@ -0,0 +1,27 @@ +package tools.java_api; + +public enum KuzuDataTypeID { + ANY(0), + NODE(10), + REL(11), + BOOL(22), + INT64(23), + INT32(24), + INT16(25), + DOUBLE(26), + FLOAT(27), + DATE(28), + TIMESTAMP(29), + INTERVAL(30), + FIXED_LIST(31), + INTERNAL_ID(40), + STRING(50), + VAR_LIST(52), + STRUCT(53); + + public final int value; + + private KuzuDataTypeID(int v) { + this.value = v; + } +} diff --git a/tools/java_api/KuzuDatabase.java b/tools/java_api/KuzuDatabase.java new file mode 100644 index 0000000000..29b7a32fd3 --- /dev/null +++ b/tools/java_api/KuzuDatabase.java @@ -0,0 +1,35 @@ +package tools.java_api; + +public class KuzuDatabase { + + long db_ref; + String db_path; + long buffer_size; + boolean destroyed = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuDatabase has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public KuzuDatabase (String database_path, long buffer_pool_size) { + this.db_path = database_path; + this.buffer_size = buffer_pool_size; + db_ref = KuzuNative.kuzu_database_init(database_path, buffer_pool_size); + } + + public void destroy() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_database_destroy(this); + destroyed = true; + } + + public static void setLoggingLevel(String logging_level) { + KuzuNative.kuzu_database_set_logging_level(logging_level); + } +} diff --git a/tools/java_api/KuzuFlatTuple.java b/tools/java_api/KuzuFlatTuple.java new file mode 100644 index 0000000000..247e743d99 --- /dev/null +++ b/tools/java_api/KuzuFlatTuple.java @@ -0,0 +1,34 @@ +package tools.java_api; + +public class KuzuFlatTuple { + long ft_ref; + boolean destroyed = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuFlatTuple has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public void destroy () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_flat_tuple_destroy(this); + destroyed = true; + } + + public KuzuValue getValue (long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_flat_tuple_get_value(this, index); + } + + public String toString () { + if (destroyed) + return "KuzuFlatTuple has been destroyed."; + else + return KuzuNative.kuzu_flat_tuple_to_string(this); + } +} diff --git a/tools/java_api/KuzuInternalID.java b/tools/java_api/KuzuInternalID.java new file mode 100644 index 0000000000..33841943ce --- /dev/null +++ b/tools/java_api/KuzuInternalID.java @@ -0,0 +1,11 @@ +package tools.java_api; + +public class KuzuInternalID { + public long table_id; + public long offset; + + public KuzuInternalID (long table_id, long offset) { + this.table_id = table_id; + this.offset = offset; + } +} diff --git a/tools/java_api/KuzuNative.java b/tools/java_api/KuzuNative.java new file mode 100644 index 0000000000..11a9043264 --- /dev/null +++ b/tools/java_api/KuzuNative.java @@ -0,0 +1,127 @@ +package tools.java_api; + +import java.util.Map; +public class KuzuNative { + static { + System.loadLibrary("kuzu_java_native"); + } + + // Database + protected static native long kuzu_database_init(String database_path, long buffer_pool_size); + protected static native void kuzu_database_destroy(KuzuDatabase db); + protected static native void kuzu_database_set_logging_level(String logging_level); + + // Connection + protected static native long kuzu_connection_init(KuzuDatabase database); + protected static native void kuzu_connection_destroy(KuzuConnection connection); + protected static native void kuzu_connection_begin_read_only_transaction(KuzuConnection connection); + protected static native void kuzu_connection_begin_write_transaction(KuzuConnection connection); + protected static native void kuzu_connection_commit(KuzuConnection connection); + protected static native void kuzu_connection_rollback(KuzuConnection connection); + protected static native void kuzu_connection_set_max_num_thread_for_exec( + KuzuConnection connection, long num_threads); + protected static native long kuzu_connection_get_max_num_thread_for_exec(KuzuConnection connection); + protected static native KuzuQueryResult kuzu_connection_query(KuzuConnection connection, String query); + protected static native KuzuPreparedStatement kuzu_connection_prepare( + KuzuConnection connection, String query); + protected static native KuzuQueryResult kuzu_connection_execute( + KuzuConnection connection, KuzuPreparedStatement prepared_statement, Map param); + protected static native String kuzu_connection_get_node_table_names(KuzuConnection connection); + protected static native String kuzu_connection_get_rel_table_names(KuzuConnection connection); + protected static native String kuzu_connection_get_node_property_names( + KuzuConnection connection, String table_name); + protected static native String kuzu_connection_get_rel_property_names( + KuzuConnection connection, String table_name); + protected static native void kuzu_connection_interrupt(KuzuConnection connection); + protected static native void kuzu_connection_set_query_timeout( + KuzuConnection connection, long timeout_in_ms); + + // PreparedStatement + protected static native void kuzu_prepared_statement_destroy(KuzuPreparedStatement prepared_statement); + protected static native boolean kuzu_prepared_statement_allow_active_transaction( + KuzuPreparedStatement prepared_statement); + protected static native boolean kuzu_prepared_statement_is_success(KuzuPreparedStatement prepared_statement); + protected static native String kuzu_prepared_statement_get_error_message( + KuzuPreparedStatement prepared_statement); + + // QueryResult + protected static native void kuzu_query_result_destroy(KuzuQueryResult query_result); + protected static native boolean kuzu_query_result_is_success(KuzuQueryResult query_result); + protected static native String kuzu_query_result_get_error_message(KuzuQueryResult query_result); + protected static native long kuzu_query_result_get_num_columns(KuzuQueryResult query_result); + protected static native String kuzu_query_result_get_column_name(KuzuQueryResult query_result, long index); + protected static native KuzuDataType kuzu_query_result_get_column_data_type( + KuzuQueryResult query_result, long index); + protected static native long kuzu_query_result_get_num_tuples(KuzuQueryResult query_result); + protected static native KuzuQuerySummary kuzu_query_result_get_query_summary(KuzuQueryResult query_result); + protected static native boolean kuzu_query_result_has_next(KuzuQueryResult query_result); + protected static native KuzuFlatTuple kuzu_query_result_get_next(KuzuQueryResult query_result); + protected static native String kuzu_query_result_to_string(KuzuQueryResult query_result); + protected static native void kuzu_query_result_write_to_csv(KuzuQueryResult query_result, + String file_path, char delimiter, char escape_char, char new_line); + protected static native void kuzu_query_result_reset_iterator(KuzuQueryResult query_result); + + // FlatTuple + protected static native void kuzu_flat_tuple_destroy(KuzuFlatTuple flat_tuple); + protected static native KuzuValue kuzu_flat_tuple_get_value(KuzuFlatTuple flat_tuple, long index); + protected static native String kuzu_flat_tuple_to_string(KuzuFlatTuple flat_tuple); + + // DataType + protected static native long kuzu_data_type_create( + KuzuDataTypeID id, KuzuDataType child_type, long fixed_num_elements_in_list); + protected static native KuzuDataType kuzu_data_type_clone(KuzuDataType data_type); + protected static native void kuzu_data_type_destroy(KuzuDataType data_type); + protected static native boolean kuzu_data_type_equals(KuzuDataType data_type1, KuzuDataType data_type2); + protected static native KuzuDataTypeID kuzu_data_type_get_id(KuzuDataType data_type); + protected static native KuzuDataType kuzu_data_type_get_child_type(KuzuDataType data_type); + protected static native long kuzu_data_type_get_fixed_num_elements_in_list(KuzuDataType data_type); + + // Value + protected static native KuzuValue kuzu_value_create_null(); + protected static native KuzuValue kuzu_value_create_null_with_data_type(KuzuDataType data_type); + protected static native boolean kuzu_value_is_null(KuzuValue value); + protected static native void kuzu_value_set_null(KuzuValue value, boolean is_null); + protected static native KuzuValue kuzu_value_create_default(KuzuDataType data_type); + protected static native long kuzu_value_create_value(T val); + + protected static native KuzuValue kuzu_value_clone(KuzuValue value); + protected static native void kuzu_value_copy(KuzuValue value, KuzuValue other); + protected static native void kuzu_value_destroy(KuzuValue value); + protected static native long kuzu_value_get_list_size(KuzuValue value); + protected static native KuzuValue kuzu_value_get_list_element(KuzuValue value, long index); + protected static native KuzuDataType kuzu_value_get_data_type(KuzuValue value); + + protected static native T kuzu_value_get_value(KuzuValue value); + + protected static native String kuzu_value_to_string(KuzuValue value); + + protected static native long kuzu_node_val_create(KuzuInternalID id, String label); + protected static native KuzuNodeValue kuzu_node_val_clone(KuzuNodeValue node_val); + protected static native void kuzu_node_val_destroy(KuzuNodeValue node_val); + protected static native KuzuValue kuzu_node_val_get_id_val(KuzuNodeValue node_val); + protected static native KuzuValue kuzu_node_val_get_label_val(KuzuNodeValue node_val); + protected static native KuzuInternalID kuzu_node_val_get_id(KuzuNodeValue node_val); + protected static native String kuzu_node_val_get_label_name(KuzuNodeValue node_val); + protected static native long kuzu_node_val_get_property_size(KuzuNodeValue node_val); + protected static native String kuzu_node_val_get_property_name_at(KuzuNodeValue node_val, long index); + protected static native KuzuValue kuzu_node_val_get_property_value_at(KuzuNodeValue node_val, long index); + protected static native void kuzu_node_val_add_property( + KuzuNodeValue node_val, String key, KuzuValue value); + protected static native String kuzu_node_val_to_string(KuzuNodeValue node_val); + + protected static native long kuzu_rel_val_create( + KuzuInternalID src_id, KuzuInternalID dst_id, String label); + protected static native KuzuRelValue kuzu_rel_val_clone(KuzuRelValue rel_val); + protected static native void kuzu_rel_val_destroy(KuzuRelValue rel_val); + protected static native KuzuValue kuzu_rel_val_get_src_id_val(KuzuRelValue rel_val); + protected static native KuzuValue kuzu_rel_val_get_dst_id_val(KuzuRelValue rel_val); + protected static native KuzuInternalID kuzu_rel_val_get_src_id(KuzuRelValue rel_val); + protected static native KuzuInternalID kuzu_rel_val_get_dst_id(KuzuRelValue rel_val); + protected static native String kuzu_rel_val_get_label_name(KuzuRelValue rel_val); + protected static native long kuzu_rel_val_get_property_size(KuzuRelValue rel_val); + protected static native String kuzu_rel_val_get_property_name_at(KuzuRelValue rel_val, long index); + protected static native KuzuValue kuzu_rel_val_get_property_value_at(KuzuRelValue rel_val, long index); + protected static native void kuzu_rel_val_add_property(KuzuRelValue rel_val, String key, KuzuValue value); + protected static native String kuzu_rel_val_to_string(KuzuRelValue rel_val); + +} diff --git a/tools/java_api/KuzuNodeValue.java b/tools/java_api/KuzuNodeValue.java new file mode 100644 index 0000000000..6aababa4cb --- /dev/null +++ b/tools/java_api/KuzuNodeValue.java @@ -0,0 +1,83 @@ +package tools.java_api; + +public class KuzuNodeValue { + long nv_ref; + boolean destroyed = false; + boolean isOwnedByCPP = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuNodeValue has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public KuzuNodeValue(KuzuInternalID id, String label) { + nv_ref = KuzuNative.kuzu_node_val_create(id, label); + } + + public KuzuNodeValue clone() { + if (destroyed) + return null; + else + return KuzuNative.kuzu_node_val_clone(this); + } + + public void destroy() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + if (!isOwnedByCPP) { + KuzuNative.kuzu_node_val_destroy(this); + destroyed = true; + } + } + + public KuzuValue getIDVal() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_id_val(this); + } + + public KuzuValue getLabelVal() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_label_val(this); + } + + public KuzuInternalID getID() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_id(this); + } + + public String getLabelName() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_label_name(this); + } + + public long getPropertySize() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_property_size(this); + } + + public String getPropertyNameAt(long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_property_name_at(this, index); + } + + public KuzuValue getPropertyValueAt(long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_node_val_get_property_value_at(this, index); + } + + public void addProperty(String key, KuzuValue value) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_node_val_add_property(this, key, value); + } + + public String toString() { + if (destroyed) + return "KuzuNodeValue has been destroyed."; + else + return KuzuNative.kuzu_node_val_to_string(this); + } +} diff --git a/tools/java_api/KuzuObjectRefDestroyedException.java b/tools/java_api/KuzuObjectRefDestroyedException.java new file mode 100644 index 0000000000..d32d1e6871 --- /dev/null +++ b/tools/java_api/KuzuObjectRefDestroyedException.java @@ -0,0 +1,7 @@ +package tools.java_api; + +public class KuzuObjectRefDestroyedException extends Exception { + public KuzuObjectRefDestroyedException(String errorMessage) { + super(errorMessage); + } +} diff --git a/tools/java_api/KuzuPreparedStatement.java b/tools/java_api/KuzuPreparedStatement.java new file mode 100644 index 0000000000..375a8f3ad1 --- /dev/null +++ b/tools/java_api/KuzuPreparedStatement.java @@ -0,0 +1,38 @@ +package tools.java_api; + +public class KuzuPreparedStatement { + long ps_ref; + boolean destroyed = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuPreparedStatement has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public void destroy() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_prepared_statement_destroy(this); + destroyed = true; + } + + public boolean allowActiveTransaction() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_prepared_statement_allow_active_transaction(this); + } + + public boolean isSuccess() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_prepared_statement_is_success(this); + } + + public String getErrorMessage() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_prepared_statement_get_error_message(this); + } + +} diff --git a/tools/java_api/KuzuQueryResult.java b/tools/java_api/KuzuQueryResult.java new file mode 100644 index 0000000000..7f18ab4caa --- /dev/null +++ b/tools/java_api/KuzuQueryResult.java @@ -0,0 +1,84 @@ +package tools.java_api; + +public class KuzuQueryResult { + long qr_ref; + boolean destroyed = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuQueryResult has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public void destroy () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_query_result_destroy(this); + destroyed = true; + } + + public boolean isSuccess () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_is_success(this); + } + + public String getErrorMessage () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_error_message(this); + } + + public long getNumColumns () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_num_columns(this); + } + + public String getColumnName (long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_column_name(this, index); + } + + public KuzuDataType getColumnDataType (long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_column_data_type(this, index); + } + + public long getNumTuples () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_num_tuples(this); + } + + public KuzuQuerySummary getQuerySummary () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_query_summary(this); + } + + public boolean hasNext () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_has_next(this); + } + + public KuzuFlatTuple getNext () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_query_result_get_next(this); + } + + public String toString () { + if (destroyed) + return "KuzuQueryResult has been destroyed."; + else + return KuzuNative.kuzu_query_result_to_string(this); + } + + public void writeToCsv (String file_path, char delimiter, char escape_char, char new_line) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_query_result_write_to_csv(this, file_path, delimiter, escape_char, new_line); + } + + public void resetIterator () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_query_result_reset_iterator(this); + } +} diff --git a/tools/java_api/KuzuQuerySummary.java b/tools/java_api/KuzuQuerySummary.java new file mode 100644 index 0000000000..3db7be98c1 --- /dev/null +++ b/tools/java_api/KuzuQuerySummary.java @@ -0,0 +1,20 @@ +package tools.java_api; + +public class KuzuQuerySummary { + + double cmpTime; + double exeTime; + + public KuzuQuerySummary(double cmpTime, double exeTime) { + this.cmpTime = cmpTime; + this.exeTime = exeTime; + } + + public double getCompilingTime() { + return cmpTime; + } + + public double getExecutionTime() { + return exeTime; + } +} diff --git a/tools/java_api/KuzuRelValue.java b/tools/java_api/KuzuRelValue.java new file mode 100644 index 0000000000..a7809cf765 --- /dev/null +++ b/tools/java_api/KuzuRelValue.java @@ -0,0 +1,88 @@ +package tools.java_api; + +public class KuzuRelValue { + long rv_ref; + boolean destroyed = false; + boolean isOwnedByCPP = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuRelValue has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public KuzuRelValue(KuzuInternalID src_id, KuzuInternalID dst_id, String label) { + rv_ref = KuzuNative.kuzu_rel_val_create(src_id, dst_id, label); + } + + public KuzuRelValue clone() { + if (destroyed) + return null; + else + return KuzuNative.kuzu_rel_val_clone(this); + } + + public void destroy() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + if (!isOwnedByCPP) { + KuzuNative.kuzu_rel_val_destroy(this); + destroyed = true; + } + } + + public KuzuValue getSrcIDVal() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_src_id_val(this); + } + + public KuzuValue getDstIDVal() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_dst_id_val(this); + } + + public KuzuInternalID getSrcID() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_src_id(this); + } + + public KuzuInternalID getDstID() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_dst_id(this); + } + + public String getLabelName() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_label_name(this); + } + + public long getPropertySize() throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_property_size(this); + } + + public String getPropertyNameAt(long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_property_name_at(this, index); + } + + public KuzuValue getPropertyValueAt(long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_rel_val_get_property_value_at(this, index); + } + + public void addProperty(String key, KuzuValue value) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_rel_val_add_property(this, key, value); + } + + public String toString() { + if (destroyed) + return "KuzuRelValue has been destroyed."; + else + return KuzuNative.kuzu_rel_val_to_string(this); + } +} diff --git a/tools/java_api/KuzuValue.java b/tools/java_api/KuzuValue.java new file mode 100644 index 0000000000..6c21498eb5 --- /dev/null +++ b/tools/java_api/KuzuValue.java @@ -0,0 +1,95 @@ +package tools.java_api; + +public class KuzuValue { + long v_ref; + boolean destroyed = false; + boolean isOwnedByCPP = false; + + private void checkNotDestroyed () throws KuzuObjectRefDestroyedException { + if (destroyed) + throw new KuzuObjectRefDestroyedException("KuzuValue has been destroyed."); + } + + @Override + protected void finalize() throws KuzuObjectRefDestroyedException { + destroy(); + } + + public boolean isOwnedByCPP() { + return isOwnedByCPP; + } + + public KuzuValue (T val) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + v_ref = KuzuNative.kuzu_value_create_value(val); + } + + public void destroy () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + if (!isOwnedByCPP) { + KuzuNative.kuzu_value_destroy(this); + destroyed = true; + } + } + + public static KuzuValue createNull() { + return KuzuNative.kuzu_value_create_null(); + } + + public static KuzuValue createNullWithDataType(KuzuDataType data_type) { + return KuzuNative.kuzu_value_create_null_with_data_type(data_type); + } + + public static KuzuValue createDefault(KuzuDataType data_type) { + return KuzuNative.kuzu_value_create_default(data_type); + } + + public boolean isNull () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_value_is_null(this); + } + + public void setNull (boolean flag) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_value_set_null(this, flag); + } + + public void copy (KuzuValue other) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + KuzuNative.kuzu_value_copy(this, other); + } + + public KuzuValue clone () { + if (destroyed) + return null; + else + return KuzuNative.kuzu_value_clone(this); + } + + public T getValue () throws KuzuObjectRefDestroyedException{ + checkNotDestroyed(); + return KuzuNative.kuzu_value_get_value(this); + } + + public long getListSize () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_value_get_list_size(this); + } + + public KuzuValue getListElement (long index) throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_value_get_list_element(this, index); + } + + public KuzuDataType getDataType () throws KuzuObjectRefDestroyedException { + checkNotDestroyed(); + return KuzuNative.kuzu_value_get_data_type(this); + } + + public String toString () { + if (destroyed) + return "KuzuValue has been destroyed."; + else + return KuzuNative.kuzu_value_to_string(this); + } +} diff --git a/tools/java_api/java_test/ConnectionTest.java b/tools/java_api/java_test/ConnectionTest.java new file mode 100644 index 0000000000..fbcd39b01f --- /dev/null +++ b/tools/java_api/java_test/ConnectionTest.java @@ -0,0 +1,370 @@ +package tools.java_api.java_test; + +import tools.java_api.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.MethodOrderer; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Map; +import java.util.HashMap; +import java.time.LocalDate; +import java.time.Instant; +import java.time.Duration; + + +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ConnectionTest extends TestBase { + + @Test + void ConnCreationAndDestroy() { + try{ + KuzuConnection conn = new KuzuConnection(db); + conn.destroy(); + }catch(AssertionError e) { + fail("ConnCreationAndDestroy failed: "); + System.out.println(e.toString()); + }catch(KuzuObjectRefDestroyedException e) { + fail("ConnCreationAndDestroy failed: "); + System.out.println(e.toString()); + } + System.out.println("ConnCreationAndDestroy passed"); + } + + @Test + void ConnInvalidDB() { + try{ + KuzuConnection conn = new KuzuConnection(null); + fail("DBInvalidPath did not throw KuzuObjectRefDestroyedException as expected."); + }catch(AssertionError e) { + } + System.out.println("ConnInvalidDB passed"); + } + + @Test + void ConnQuery() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) RETURN a.fName;"; + KuzuQueryResult result = conn.query(query); + assertNotNull(result); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 8); + assertEquals(result.getNumColumns(), 1); + assertTrue(result.getColumnName(0).equals("a.fName")); + result.destroy(); + System.out.println("ConnQuery passed"); + } + + @Test + void ConnSetGetMaxNumThreadForExec() throws KuzuObjectRefDestroyedException { + conn.setMaxNumThreadForExec(4); + assertEquals(conn.getMaxNumThreadForExec(), 4); + conn.setMaxNumThreadForExec(8); + assertEquals(conn.getMaxNumThreadForExec(), 8); + System.out.println("ConnSetGetMaxNumThreadForExec passed"); + } + + @Test + void ConnPrepareBool() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.isStudent = $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue(true)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 3); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepare passed"); + } + + @Test + void ConnPrepareInt64() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.age > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + + m.put("1", new KuzuValue((long)30)); + + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 4); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareInt64 passed"); + } + + @Test + void ConnPrepareInt32() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:movies) WHERE a.length > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue((int)200)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 2); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareInt32 passed"); + } + + @Test + void ConnPrepareInt16() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) -[s:studyAt]-> (b:organisation) WHERE s.length > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue((short)10)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 2); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareInt16 passed"); + } + + @Test + void ConnPrepareDouble() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.eyeSight > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue((double)4.5)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 7); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareDouble passed"); + } + + @Test + void ConnPrepareFloat() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.height < $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue((float)1.0)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 1); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareFloat passed"); + } + + @Test + void ConnPrepareString() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.fName = $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue("Alice")); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 1); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareString passed"); + } + + @Test + void ConnPrepareDate() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.birthdate > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue(LocalDate.EPOCH)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 4); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareDate passed"); + } + + @Test + void ConnPrepareTimeStamp() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.registerTime > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue(Instant.EPOCH)); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 7); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareTimeStamp passed"); + } + + @Test + void ConnPrepareInterval() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.lastJobDuration > $1 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue(Duration.ofDays(3650))); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 3); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareInterval passed"); + } + + @Test + void ConnPrepareMultiParam() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.lastJobDuration > $1 AND a.fName = $2 RETURN COUNT(*)"; + Map m = new HashMap(); + m.put("1", new KuzuValue(Duration.ofDays(730))); + m.put("2", new KuzuValue("Alice")); + KuzuPreparedStatement statement = conn.prepare(query); + assertNotNull(statement); + KuzuQueryResult result = conn.execute(statement, m); + System.out.println(result.getErrorMessage()); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + assertTrue(result.getErrorMessage().equals("")); + assertEquals(result.getNumTuples(), 1); + assertEquals(result.getNumColumns(), 1); + KuzuFlatTuple tuple = result.getNext(); + assertEquals(((long)tuple.getValue(0).getValue()), 1); + statement.destroy(); + result.destroy(); + System.out.println("ConnPrepareMultiParam passed"); + } + + @Test + void ConnGetNodeTableNames() throws KuzuObjectRefDestroyedException { + String result = conn.getNodeTableNames(); + assertNotNull(result); + assertTrue(result.equals("Node tables: \n" + + "\torganisation\n" + + "\tperson\n" + + "\tmovies\n") || + result.equals("Node tables: \n" + + "\tmovies\n" + + "\tperson\n" + + "\torganisation\n")); + System.out.println("ConnGetNodeTableNames passed"); + } + + @Test + void ConnGetRelTableNames() throws KuzuObjectRefDestroyedException { + String result = conn.getRelTableNames(); + assertNotNull(result); + assertTrue(result.equals("Rel tables: \n" + + "\tmeets\n" + + "\tstudyAt\n" + + "\tknows\n" + + "\tworkAt\n" + + "\tmarries\n") || + result.equals("Rel tables: \n" + + "\tmarries\n" + + "\tworkAt\n" + + "\tknows\n" + + "\tstudyAt\n" + + "\tmeets\n")); + System.out.println("ConnGetRelTableNames passed"); + } + + @Test + void ConnGetNodePropertyNames() throws KuzuObjectRefDestroyedException { + String result = conn.getNodePropertyNames("movies"); + assertNotNull(result); + assertTrue(result.equals("movies properties: \n" + + "\tname STRING(PRIMARY KEY)\n" + + "\tlength INT32\n" + + "\tnote STRING\n" + + "\tdescription STRUCT(DOUBLE,INT64,TIMESTAMP,DATE)\n")); + System.out.println("ConnGetNodePropertyNames passed"); + } + + @Test + void ConnGetRelPropertyNames() throws KuzuObjectRefDestroyedException { + String result = conn.getRelPropertyNames("meets"); + assertNotNull(result); + assertTrue(result.equals("meets src node: person\n" + + "meets dst node: person\n" + + "meets properties: \n" + + "\tlocation FLOAT[2]\n" + + "\ttimes INT32\n")); + System.out.println("ConnGetRelPropertyNames passed"); + } + + @Test + void ConnQueryTimeout() throws KuzuObjectRefDestroyedException { + conn.setQueryTimeout(1); + KuzuQueryResult result = conn.query("MATCH (a:person)-[:knows*1..28]->(b:person) RETURN COUNT(*);"); + assertNotNull(result); + assertFalse(result.isSuccess()); + assertTrue(result.getErrorMessage().equals("Interrupted.")); + result.destroy(); + System.out.println("ConnQueryTimeout passed"); + } + +} diff --git a/tools/java_api/java_test/DataTypeTest.java b/tools/java_api/java_test/DataTypeTest.java new file mode 100644 index 0000000000..1263040d70 --- /dev/null +++ b/tools/java_api/java_test/DataTypeTest.java @@ -0,0 +1,142 @@ +package tools.java_api.java_test; + +import tools.java_api.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + + +public class DataTypeTest extends TestBase { + + @Test + void DataTypeClone() throws KuzuObjectRefDestroyedException { + KuzuDataType dataType = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + assertNotNull(dataType); + KuzuDataType dataTypeClone = dataType.clone(); + assertNotNull(dataTypeClone); + assertEquals(dataTypeClone.getID(), KuzuDataTypeID.INT64); + + KuzuDataType dataType2 = new KuzuDataType(KuzuDataTypeID.VAR_LIST, dataType, 0); + assertNotNull(dataType2); + KuzuDataType dataTypeClone2 = dataType2.clone(); + assertNotNull(dataTypeClone2); + assertEquals(dataTypeClone2.getID(), KuzuDataTypeID.VAR_LIST); + assertEquals(dataTypeClone2.getChildType().getID(), KuzuDataTypeID.INT64); + + KuzuDataType dataType3 = new KuzuDataType(KuzuDataTypeID.FIXED_LIST, dataType, 100); + assertNotNull(dataType3); + KuzuDataType dataTypeClone3 = dataType3.clone(); + assertNotNull(dataTypeClone3); + assertEquals(dataTypeClone3.getID(), KuzuDataTypeID.FIXED_LIST); + assertEquals(dataTypeClone3.getChildType().getID(), KuzuDataTypeID.INT64); + assertEquals(dataTypeClone3.getFixedNumElementsInList(), 100); + + assertFalse(dataTypeClone2.equals(dataTypeClone3)); + + dataType.destroy(); + dataType2.destroy(); + dataType3.destroy(); + dataTypeClone.destroy(); + dataTypeClone2.destroy(); + dataTypeClone3.destroy(); + + System.out.println("DataTypeClone passed"); + } + + @Test + void DataTypeEquals() throws KuzuObjectRefDestroyedException { + KuzuDataType dataType = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + assertNotNull(dataType); + KuzuDataType dataTypeClone = dataType.clone(); + assertNotNull(dataTypeClone); + assertTrue(dataType.equals(dataTypeClone)); + + KuzuDataType dataType2 = new KuzuDataType(KuzuDataTypeID.VAR_LIST, dataType, 0); + assertNotNull(dataType2); + KuzuDataType dataTypeClone2 = dataType2.clone(); + assertNotNull(dataTypeClone2); + assertTrue(dataType2.equals(dataTypeClone2)); + + KuzuDataType dataType3 = new KuzuDataType(KuzuDataTypeID.FIXED_LIST, dataType, 100); + assertNotNull(dataType3); + KuzuDataType dataTypeClone3 = dataType3.clone(); + assertNotNull(dataTypeClone3); + assertTrue(dataType3.equals(dataTypeClone3)); + + assertFalse(dataType.equals(dataType2)); + assertFalse(dataType.equals(dataType3)); + assertFalse(dataType2.equals(dataType3)); + assertFalse(dataTypeClone.equals(dataTypeClone3)); + + dataType.destroy(); + dataType2.destroy(); + dataType3.destroy(); + dataTypeClone.destroy(); + dataTypeClone2.destroy(); + dataTypeClone3.destroy(); + + System.out.println("DataTypeEquals passed"); + } + + @Test + void DataTypeGetID() throws KuzuObjectRefDestroyedException { + KuzuDataType dataType = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + assertNotNull(dataType); + assertEquals(dataType.getID(), KuzuDataTypeID.INT64); + + KuzuDataType dataType2 = new KuzuDataType(KuzuDataTypeID.VAR_LIST, dataType, 0); + assertNotNull(dataType2); + assertEquals(dataType2.getID(), KuzuDataTypeID.VAR_LIST); + + KuzuDataType dataType3 = new KuzuDataType(KuzuDataTypeID.FIXED_LIST, dataType, 100); + assertNotNull(dataType3); + assertEquals(dataType3.getID(), KuzuDataTypeID.FIXED_LIST); + + dataType.destroy(); + dataType2.destroy(); + dataType3.destroy(); + + System.out.println("DataTypeGetID passed"); + } + + @Test + void DataTypeGetChildType() throws KuzuObjectRefDestroyedException { + KuzuDataType dataType = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + assertNotNull(dataType); + + KuzuDataType dataType2 = new KuzuDataType(KuzuDataTypeID.VAR_LIST, dataType, 0); + assertNotNull(dataType2); + assertEquals(dataType2.getChildType().getID(), KuzuDataTypeID.INT64); + + KuzuDataType dataType3 = new KuzuDataType(KuzuDataTypeID.FIXED_LIST, dataType, 100); + assertNotNull(dataType3); + assertEquals(dataType3.getChildType().getID(), KuzuDataTypeID.INT64); + + dataType.destroy(); + dataType2.destroy(); + dataType3.destroy(); + + System.out.println("DataTypeGetChildType passed"); + } + + @Test + void DataTypeGetFixedNumElementsInList() throws KuzuObjectRefDestroyedException { + KuzuDataType dataType = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + assertNotNull(dataType); + assertEquals(dataType.getFixedNumElementsInList(), 0); + + KuzuDataType dataType2 = new KuzuDataType(KuzuDataTypeID.VAR_LIST, dataType, 0); + assertNotNull(dataType2); + assertEquals(dataType.getFixedNumElementsInList(), 0); + + KuzuDataType dataType3 = new KuzuDataType(KuzuDataTypeID.FIXED_LIST, dataType, 100); + assertNotNull(dataType3); + assertEquals(dataType3.getFixedNumElementsInList(), 100); + + dataType.destroy(); + dataType2.destroy(); + dataType3.destroy(); + + System.out.println("DataTypeGetFixedNumElementsInList passed"); + + } +} diff --git a/tools/java_api/java_test/DatabaseTest.java b/tools/java_api/java_test/DatabaseTest.java new file mode 100644 index 0000000000..a8f25c3d19 --- /dev/null +++ b/tools/java_api/java_test/DatabaseTest.java @@ -0,0 +1,53 @@ +package tools.java_api.java_test; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import tools.java_api.*; + +public class DatabaseTest extends TestBase { + @Test + void DBCreationAndDestroy() { + String databasePath = System.getProperty("user.dir") + "dbtest"; + try{ + KuzuDatabase database = new KuzuDatabase(databasePath, 0); + database.destroy(); + }catch(Exception e) { + fail("DBCreationAndDestroy failed: "); + System.out.println(e.toString()); + } + + System.out.println("DBCreationAndDestroy passed"); + } + + @Test + void DBInvalidPath() { + try{ + KuzuDatabase database = new KuzuDatabase("", 0); + database.destroy(); + fail("DBInvalidPath did not throw exception as expected."); + }catch(Exception e) { + System.out.println("DBInvalidPath passed"); + } + } + + @Test + void DBSetLoggingLevel() { + try{ + KuzuDatabase.setLoggingLevel("debug"); + KuzuDatabase.setLoggingLevel("info"); + KuzuDatabase.setLoggingLevel("err"); + }catch(Exception e){ + fail("DBSetLoggingLevel failed: "); + System.out.println(e.toString()); + } + + try{ + KuzuDatabase.setLoggingLevel("unsupported"); + fail("DBSetLoggingLevel did not throw exception as expected."); + }catch(Exception e){ + } + + System.out.println("DBSetLoggingLevel passed"); + } +} diff --git a/tools/java_api/java_test/FlatTupleTest.java b/tools/java_api/java_test/FlatTupleTest.java new file mode 100644 index 0000000000..295ded556b --- /dev/null +++ b/tools/java_api/java_test/FlatTupleTest.java @@ -0,0 +1,59 @@ +package tools.java_api.java_test; + +import tools.java_api.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FlatTupleTest extends TestBase { + + @Test + void FlatTupleGetValue() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age, a.height ORDER BY a.fName LIMIT 1"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + assertNotNull(flatTuple); + + KuzuValue value = flatTuple.getValue(0); + assertNotNull(value); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.STRING); + assertTrue(value.getValue().equals("Alice")); + value.destroy(); + + value = flatTuple.getValue(1); + assertNotNull(value); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INT64); + assertTrue(value.getValue().equals(35L)); + value.destroy(); + + value = flatTuple.getValue(2); + assertNotNull(value); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.FLOAT); + assertTrue(value.getValue().equals((float)1.731)); + value.destroy(); + + value = flatTuple.getValue(222); + assertNull(value); + + result.destroy(); + System.out.println("FlatTupleGetValue passed"); + } + + @Test + void FlatTupleToString() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age, a.height ORDER BY a.fName LIMIT 1"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + assertNotNull(flatTuple); + + String str = flatTuple.toString(); + assertTrue(str.equals("Alice|35|1.731000\n")); + + flatTuple.destroy(); + result.destroy(); + + System.out.println("FlatTupleToString passed"); + } + +} diff --git a/tools/java_api/java_test/PreparedStatementTest.java b/tools/java_api/java_test/PreparedStatementTest.java new file mode 100644 index 0000000000..cfc035682b --- /dev/null +++ b/tools/java_api/java_test/PreparedStatementTest.java @@ -0,0 +1,65 @@ +package tools.java_api.java_test; + +import tools.java_api.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class PreparedStatementTest extends TestBase { + + @Test + void PrepStmtIsSuccess() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.isStudent = $1 RETURN COUNT(*)"; + KuzuPreparedStatement preparedStatement1 = conn.prepare(query); + assertNotNull(preparedStatement1); + assertTrue(preparedStatement1.isSuccess()); + preparedStatement1.destroy(); + + query = "MATCH (a:personnnn) WHERE a.isStudent = $1 RETURN COUNT(*)"; + KuzuPreparedStatement preparedStatement2 = conn.prepare(query); + assertNotNull(preparedStatement2); + assertFalse(preparedStatement2.isSuccess()); + preparedStatement2.destroy(); + + System.out.println("PrepStmtIsSuccess passed"); + } + + @Test + void PrepStmtGetErrorMessage() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.isStudent = $1 RETURN COUNT(*)"; + KuzuPreparedStatement preparedStatement1 = conn.prepare(query); + assertNotNull(preparedStatement1); + String message = preparedStatement1.getErrorMessage(); + assertTrue(message.equals("")); + preparedStatement1.destroy(); + + query = "MATCH (a:personnnn) WHERE a.isStudent = $1 RETURN COUNT(*)"; + KuzuPreparedStatement preparedStatement2 = conn.prepare(query); + assertNotNull(preparedStatement2); + message = preparedStatement2.getErrorMessage(); + assertTrue(message.equals("Binder exception: Node table personnnn does not exist.")); + preparedStatement2.destroy(); + + System.out.println("PrepStmtGetErrorMessage passed"); + } + + @Test + void PrepStmtAllowActiveTransaction() throws KuzuObjectRefDestroyedException { + String query = "MATCH (a:person) WHERE a.isStudent = $1 RETURN COUNT(*)"; + KuzuPreparedStatement preparedStatement1 = conn.prepare(query); + assertNotNull(preparedStatement1); + assertTrue(preparedStatement1.isSuccess()); + assertTrue(preparedStatement1.allowActiveTransaction()); + preparedStatement1.destroy(); + + query = "create node table npytable (id INT64,i64 INT64[12],PRIMARY KEY(id));"; + KuzuPreparedStatement preparedStatement2 = conn.prepare(query); + assertNotNull(preparedStatement2); + assertTrue(preparedStatement2.isSuccess()); + assertFalse(preparedStatement2.allowActiveTransaction()); + preparedStatement2.destroy(); + + System.out.println("PrepStmtAllowActiveTransaction passed"); + } + + +} diff --git a/tools/java_api/java_test/QueryResultTest.java b/tools/java_api/java_test/QueryResultTest.java new file mode 100644 index 0000000000..6912479d56 --- /dev/null +++ b/tools/java_api/java_test/QueryResultTest.java @@ -0,0 +1,178 @@ +package tools.java_api.java_test; + +import tools.java_api.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Scanner; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Files; + +public class QueryResultTest extends TestBase { + + @Test + void QueryResultGetErrorMessage() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN COUNT(*)"); + assertTrue(result.isSuccess()); + String errorMessage = result.getErrorMessage(); + assertTrue(errorMessage.equals("")); + result.destroy(); + + result = conn.query("MATCH (a:personnnn) RETURN COUNT(*)"); + assertFalse(result.isSuccess()); + errorMessage = result.getErrorMessage(); + assertTrue(errorMessage.equals("Binder exception: Node table personnnn does not exist.")); + result.destroy(); + + System.out.println("QueryResultGetErrorMessage passed"); + } + + @Test + void QueryResultGetNumColumns() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age, a.height"); + assertTrue(result.isSuccess()); + assertEquals(result.getNumColumns(), 3); + result.destroy(); + + System.out.println("QueryResultGetNumColumns passed"); + } + + @Test + void QueryResultGetColumnName() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age, a.height"); + assertTrue(result.isSuccess()); + String columnName = result.getColumnName(0); + assertTrue(columnName.equals("a.fName")); + + columnName = result.getColumnName(1); + assertTrue(columnName.equals("a.age")); + + columnName = result.getColumnName(2); + assertTrue(columnName.equals("a.height")); + + columnName = result.getColumnName(222); + assertNull(columnName); + + result.destroy(); + + System.out.println("QueryResultGetColumnName passed"); + } + + @Test + void QueryResultGetColumnDataType() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age, a.height"); + assertTrue(result.isSuccess()); + KuzuDataType type = result.getColumnDataType(0); + assertEquals(type.getID(), KuzuDataTypeID.STRING); + type.destroy(); + + type = result.getColumnDataType(1); + assertEquals(type.getID(), KuzuDataTypeID.INT64); + type.destroy(); + type = result.getColumnDataType(2); + assertEquals(type.getID(), KuzuDataTypeID.FLOAT); + type.destroy(); + + type = result.getColumnDataType(222); + assertNull(type); + + System.out.println("QueryResultGetColumnDataType passed"); + } + + @Test + void QueryResultGetQuerySummary() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age, a.height"); + assertTrue(result.isSuccess()); + KuzuQuerySummary summary = result.getQuerySummary(); + assertNotNull(summary); + double compilingTime = summary.getCompilingTime(); + assertTrue(compilingTime > 0); + double executionTime = summary.getExecutionTime(); + assertTrue(executionTime > 0); + result.destroy(); + + System.out.println("QueryResultGetQuerySummary passed"); + } + + @Test + void QueryResultGetNext() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age ORDER BY a.fName"); + assertTrue(result.isSuccess()); + + assertTrue(result.hasNext()); + KuzuFlatTuple row = result.getNext(); + assertNotNull(row); + assertTrue(row.getValue(0).getValue().equals("Alice")); + assertTrue(row.getValue(1).getValue().equals(35L)); + row.destroy(); + + assertTrue(result.hasNext()); + row = result.getNext(); + assertNotNull(row); + assertTrue(row.getValue(0).getValue().equals("Bob")); + assertTrue(row.getValue(1).getValue().equals(30L)); + row.destroy(); + + result.destroy(); + + System.out.println("QueryResultGetNext passed"); + } + + @Test + void QueryResultWriteToCSV() throws IOException, KuzuObjectRefDestroyedException{ + String newline = "\n"; + String basicOutput = + "Carol,1,5.000000,1940-06-22,1911-08-20 02:32:21,CsWork" + newline + + "Dan,2,4.800000,1950-07-23,2031-11-30 12:25:30,DEsWork" + newline + + "Elizabeth,1,4.700000,1980-10-26,1976-12-23 11:21:42,DEsWork" + newline; + String query = "MATCH (a:person)-[:workAt]->(o:organisation) RETURN a.fName, a.gender," + + "a.eyeSight, a.birthdate, a.registerTime, o.name"; + KuzuQueryResult result = conn.query(query); + assertTrue(result.isSuccess()); + + Path tempDir = Files.createTempFile("output_CSV_CAPI", "csv"); + tempDir.toFile().deleteOnExit(); + + String outputPath = "output_CSV_CAPI.csv"; + result.writeToCsv(outputPath, ',', '"', '\n'); + + try { + File csv = new File(outputPath); + Scanner s = new Scanner(csv); + String content = s.useDelimiter("\\z").next(); + assertTrue(basicOutput.equals(content)); + s.close(); + System.out.println("QueryResultWriteToCSV passed"); + } catch(FileNotFoundException e) { + fail("QueryResultWriteToCSV failed, csv file not found"); + } + } + + @Test + void QueryResultResetIterator() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age ORDER BY a.fName"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + + KuzuFlatTuple row = result.getNext(); + assertTrue(row.getValue(0).getValue().equals("Alice")); + assertTrue(row.getValue(1).getValue().equals(35L)); + row.destroy(); + + result.resetIterator(); + + assertTrue(result.hasNext()); + row = result.getNext(); + assertNotNull(row); + assertTrue(row.getValue(0).getValue().equals("Alice")); + assertTrue(row.getValue(1).getValue().equals(35L)); + row.destroy(); + + result.destroy(); + + System.out.println("QueryResultResetIterator passed"); + } +} diff --git a/tools/java_api/java_test/TestBase.java b/tools/java_api/java_test/TestBase.java new file mode 100644 index 0000000000..198a35ed01 --- /dev/null +++ b/tools/java_api/java_test/TestBase.java @@ -0,0 +1,41 @@ +package tools.java_api.java_test; + +import tools.java_api.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import java.nio.file.Path; + +import java.io.IOException; + +public class TestBase { + + @TempDir + static Path tempDir; + protected static KuzuDatabase db; + protected static KuzuConnection conn; + + @BeforeAll + static void getDBandConn() throws IOException, KuzuObjectRefDestroyedException{ + System.out.println("Kuzu test starting, loading data..."); + TestHelper.loadData(tempDir.toFile().getAbsolutePath()); + db = TestHelper.getDatabase(); + conn = TestHelper.getConnection(); + System.out.println("Test data loaded"); + } + + @AfterAll + static void destroyDBandConn() throws KuzuObjectRefDestroyedException { + System.out.println("Kuzu test finished, cleaning up data..."); + try{ + db.destroy(); + conn.destroy(); + }catch(AssertionError e) { + fail("destroyDBandConn failed: "); + System.out.println(e.toString()); + } + System.out.println("Data cleaned up"); + } + +} diff --git a/tools/java_api/java_test/TestHelper.java b/tools/java_api/java_test/TestHelper.java new file mode 100644 index 0000000000..958014426e --- /dev/null +++ b/tools/java_api/java_test/TestHelper.java @@ -0,0 +1,55 @@ +package tools.java_api.java_test; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Files; + + +import tools.java_api.*; + +public class TestHelper { + private static KuzuDatabase db; + private static KuzuConnection conn; + + public static KuzuDatabase getDatabase() { + return db; + } + + public static KuzuConnection getConnection() { + return conn; + } + + public static void loadData(String dbPath) throws IOException, KuzuObjectRefDestroyedException { + Path tempDir = Files.createTempDirectory("java_api_test_db"); + tempDir.toFile().deleteOnExit(); + + BufferedReader reader; + db = new KuzuDatabase(dbPath, 0); + conn = new KuzuConnection(db); + try { + reader = new BufferedReader(new FileReader("./../../dataset/tinysnb/schema.cypher")); + String line = reader.readLine(); + + while (line != null) { + conn.query(line); + line = reader.readLine(); + } + + reader.close(); + + reader = new BufferedReader(new FileReader("./../../dataset/tinysnb/copy.cypher")); + line = reader.readLine(); + + while (line != null) { + conn.query(line); + line = reader.readLine(); + } + + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/tools/java_api/java_test/ValueTest.java b/tools/java_api/java_test/ValueTest.java new file mode 100644 index 0000000000..2dfe2e4ecd --- /dev/null +++ b/tools/java_api/java_test/ValueTest.java @@ -0,0 +1,895 @@ +package tools.java_api.java_test; + + +import tools.java_api.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.time.LocalDate; +import java.time.Duration; +import java.time.Instant; + +public class ValueTest extends TestBase { + + @Test + void ValueCreateNull() throws KuzuObjectRefDestroyedException { + KuzuValue value = KuzuValue.createNull(); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.ANY); + value.destroy(); + + System.out.println("ValueCreateNull passed"); + } + + @Test + void ValueCreateNullWithDatatype() throws KuzuObjectRefDestroyedException { + KuzuDataType type = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + KuzuValue value = KuzuValue.createNullWithDataType(type); + assertFalse(value.isOwnedByCPP()); + type.destroy(); + + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INT64); + value.destroy(); + + System.out.println("ValueCreateNullWithDatatype passed"); + } + + @Test + void ValueIsNull() throws KuzuObjectRefDestroyedException { + KuzuValue value = new KuzuValue(123L); + assertFalse(value.isOwnedByCPP()); + assertFalse(value.isNull()); + value.destroy(); + + value = KuzuValue.createNull(); + assertTrue(value.isNull()); + value.destroy(); + + KuzuDataType type = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + value = KuzuValue.createNullWithDataType(type); + assertTrue(value.isNull()); + type.destroy(); + value.destroy(); + + System.out.println("ValueIsNull passed"); + } + + @Test + void ValueSetNull() throws KuzuObjectRefDestroyedException { + KuzuValue value = new KuzuValue(123L); + assertFalse(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + value.setNull(true); + assertTrue(value.isNull()); + + value.setNull(false); + assertFalse(value.isNull()); + value.destroy(); + + System.out.println("ValueSetNull passed"); + } + + @Test + void ValueCreateDefault() throws KuzuObjectRefDestroyedException { + KuzuDataType type = new KuzuDataType(KuzuDataTypeID.INT64, null, 0); + KuzuValue value = KuzuValue.createDefault(type); + assertFalse(value.isOwnedByCPP()); + type.destroy(); + + assertFalse(value.isNull()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INT64); + assertTrue(value.getValue().equals(0L)); + value.destroy(); + + type = new KuzuDataType(KuzuDataTypeID.STRING, null, 0); + value = KuzuValue.createDefault(type); + assertFalse(value.isOwnedByCPP()); + type.destroy(); + + assertFalse(value.isNull()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.STRING); + assertTrue(value.getValue().equals("")); + value.destroy(); + + System.out.println("ValueCreateDefault passed"); + } + + @Test + void ValueCreateBool() throws KuzuObjectRefDestroyedException { + // bool + KuzuValue value = new KuzuValue(true); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.BOOL); + assertTrue(value.getValue().equals(true)); + value.destroy(); + + value = new KuzuValue(false); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.BOOL); + assertTrue(value.getValue().equals(false)); + value.destroy(); + } + + @Test + void ValueCreateINT16() throws KuzuObjectRefDestroyedException { + // INT16 + KuzuValue value = new KuzuValue((short)123); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INT16); + assertTrue(value.getValue().equals((short)123)); + value.destroy(); + } + + @Test + void ValueCreateINT32() throws KuzuObjectRefDestroyedException { + // INT32 + KuzuValue value = new KuzuValue(123); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INT32); + assertTrue(value.getValue().equals(123)); + value.destroy(); + } + + @Test + void ValueCreateINT64() throws KuzuObjectRefDestroyedException { + // INT64 + KuzuValue value = new KuzuValue(123L); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INT64); + assertTrue(value.getValue().equals(123L)); + value.destroy(); + } + + @Test + void ValueCreateFloat() throws KuzuObjectRefDestroyedException { + // float + KuzuValue value = new KuzuValue((float)123.456); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.FLOAT); + assertTrue(value.getValue().equals((float)123.456)); + value.destroy(); + } + + @Test + void ValueCreateDouble() throws KuzuObjectRefDestroyedException { + // double + KuzuValue value = new KuzuValue((float)123.456); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.FLOAT); + assertTrue(value.getValue().equals((float)123.456)); + value.destroy(); + } + + @Test + void ValueCreateInternalID() throws KuzuObjectRefDestroyedException { + // InternalID + KuzuValue value = new KuzuValue(new KuzuInternalID(1, 123)); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INTERNAL_ID); + KuzuInternalID id = value.getValue(); + assertEquals(id.table_id, 1); + assertEquals(id.offset, 123); + value.destroy(); + } + + @Test + void ValueCreateNodeVal() throws KuzuObjectRefDestroyedException { + // NodeVal + KuzuInternalID id = new KuzuInternalID(1, 123); + KuzuNodeValue nodeval = new KuzuNodeValue(id, "person"); + KuzuValue value = new KuzuValue(nodeval); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.NODE); + KuzuNodeValue nv = value.getValue(); + assertEquals(nv.getID().table_id, 1); + assertEquals(nv.getID().offset, 123); + assertTrue(nv.getLabelName().equals("person")); + assertEquals(nv.getPropertySize(), 0); + nv.destroy(); + nodeval.destroy(); + value.destroy(); + } + + @Test + void ValueCreateRelVal() throws KuzuObjectRefDestroyedException { + // RelVal + KuzuInternalID srcID = new KuzuInternalID(1, 123); + KuzuInternalID dstID = new KuzuInternalID(2, 456); + KuzuRelValue relval = new KuzuRelValue(srcID, dstID, "knows"); + KuzuValue value = new KuzuValue(relval); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.REL); + KuzuRelValue rv = value.getValue(); + assertEquals(rv.getSrcID().table_id, 1); + assertEquals(rv.getSrcID().offset, 123); + assertEquals(rv.getDstID().table_id, 2); + assertEquals(rv.getDstID().offset, 456); + assertTrue(rv.getLabelName().equals("knows")); + assertEquals(rv.getPropertySize(), 0); + value.destroy(); + relval.destroy(); + rv.destroy(); + } + + @Test + void ValueCreateDate() throws KuzuObjectRefDestroyedException { + // date + KuzuValue value = new KuzuValue(LocalDate.ofEpochDay(123)); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.DATE); + LocalDate date = value.getValue(); + assertEquals(date.toEpochDay(), 123); + value.destroy(); + } + + @Test + void ValueCreateTimeStamp() throws KuzuObjectRefDestroyedException { + // timestamp + KuzuValue value = new KuzuValue(Instant.ofEpochSecond(123/1000000L, 123 % 1000000 * 1000)); // 123 microseconds + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.TIMESTAMP); + Instant stamp = value.getValue(); + assertEquals(stamp.getEpochSecond(), 0); + assertEquals(stamp.getNano(), 123000); + value.destroy(); + + value = new KuzuValue(Instant.ofEpochSecond(123123123L/1000000L, 123123123L % 1000000 * 1000)); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.TIMESTAMP); + stamp = value.getValue(); + assertEquals(stamp.getEpochSecond(), 123); + assertEquals(stamp.getNano(), 123123000); + value.destroy(); + } + + @Test + void ValueCreateInterval() throws KuzuObjectRefDestroyedException { + // interval + KuzuValue value = new KuzuValue(Duration.ofMillis(31795200003L)); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.INTERVAL); + Duration interval = value.getValue(); + long month = (long) (interval.toDays() / 30.4167); + long day = interval.toDays() - (long) (month * 30.4167); + long micros = interval.minusDays(interval.toDays()).toMillis() * 1000; + assertEquals(month, 12); + assertEquals(day, 3); + assertEquals(micros, 3000); + value.destroy(); + } + + @Test + void ValueCreateString() throws KuzuObjectRefDestroyedException { + // String + KuzuValue value = new KuzuValue("abcdefg"); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.STRING); + String str = value.getValue(); + assertTrue(str.equals("abcdefg")); + value.destroy(); + } + + @Test + void ValueClone() throws KuzuObjectRefDestroyedException { + KuzuValue value = new KuzuValue("abcdefg"); + assertFalse(value.isOwnedByCPP()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.STRING); + String str = value.getValue(); + assertTrue(str.equals("abcdefg")); + + KuzuValue clone = value.clone(); + value.destroy(); + + assertFalse(clone.isOwnedByCPP()); + assertEquals(clone.getDataType().getID(), KuzuDataTypeID.STRING); + str = clone.getValue(); + assertTrue(str.equals("abcdefg")); + clone.destroy(); + + System.out.println("ValueClone passed"); + } + + @Test + void ValueCopy() throws KuzuObjectRefDestroyedException { + KuzuValue value = new KuzuValue("abc"); + KuzuValue value2 = new KuzuValue("abcdefg"); + + value.copy(value2); + value2.destroy(); + + assertFalse(value.isNull()); + assertEquals(value.getDataType().getID(), KuzuDataTypeID.STRING); + String str = value.getValue(); + assertTrue(str.equals("abcdefg")); + value.destroy(); + + System.out.println("ValueCopy passed"); + } + + @Test + void ValueGetListSize() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.workedHours ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + assertEquals(value.getListSize(), 2); + + value.destroy(); + flatTuple.destroy(); + result.destroy(); + + System.out.println("ValueGetListSize passed"); + } + + @Test + void ValueGetListElement() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.workedHours ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + assertEquals(value.getListSize(), 2); + + KuzuValue listElement = value.getListElement(0); + assertTrue(listElement.isOwnedByCPP()); + assertTrue(listElement.getValue().equals(10L)); + listElement.destroy(); + + listElement = value.getListElement(1); + assertTrue(listElement.isOwnedByCPP()); + assertTrue(listElement.getValue().equals(5L)); + listElement.destroy(); + + listElement = value.getListElement(222); + assertNull(listElement); + + value.destroy(); + flatTuple.destroy(); + result.destroy(); + + System.out.println("ValueGetListElement passed"); + } + + @Test + void ValueGetDatatype() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.isStudent, a.workedHours"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + + KuzuDataType dataType = value.getDataType(); + assertEquals(dataType.getID(), KuzuDataTypeID.STRING); + dataType.destroy(); + value.destroy(); + + value = flatTuple.getValue(1); + dataType = value.getDataType(); + assertEquals(dataType.getID(), KuzuDataTypeID.BOOL); + dataType.destroy(); + value.destroy(); + + value = flatTuple.getValue(2); + dataType = value.getDataType(); + assertEquals(dataType.getID(), KuzuDataTypeID.VAR_LIST); + KuzuDataType childDataType = dataType.getChildType(); + assertEquals(childDataType.getID(), KuzuDataTypeID.INT64); + childDataType.destroy(); + dataType.destroy(); + value.destroy(); + + flatTuple.destroy(); + result.destroy(); + + System.out.println("ValueGetDatatype passed"); + } + + @Test + void ValueGetBool() throws KuzuObjectRefDestroyedException { + // bool + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.isStudent ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + assertTrue(value.getValue().equals(true)); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetINT16() throws KuzuObjectRefDestroyedException { + // INT16 + KuzuQueryResult result = conn.query("MATCH (a:person) -[r:studyAt]-> (b:organisation) RETURN r.length ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + assertTrue(value.getValue().equals((short) 5)); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetINT32() throws KuzuObjectRefDestroyedException { + // INT32 + KuzuQueryResult result = conn.query("MATCH (m:movies) RETURN m.length ORDER BY m.name"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + assertTrue(value.getValue().equals(298)); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetINT64() throws KuzuObjectRefDestroyedException { + // INT64 + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.ID ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + assertTrue(value.getValue().equals(0L)); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetFloat() throws KuzuObjectRefDestroyedException { + // FLOAT + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.height ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + assertTrue(value.getValue().equals((float)1.731)); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetDouble() throws KuzuObjectRefDestroyedException { + // Double + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.eyeSight ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + assertTrue(value.getValue().equals((double)5.0)); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetInternalID() throws KuzuObjectRefDestroyedException { + // InternalID + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + KuzuNodeValue nv = value.getValue(); + KuzuInternalID id = nv.getIDVal().getValue(); + assertEquals(id.table_id, 0); + assertEquals(id.offset, 0); + nv.destroy(); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetRelVal() throws KuzuObjectRefDestroyedException { + // RelVal + KuzuQueryResult result = conn.query("MATCH (a:person) -[r:knows]-> (b:person) RETURN r ORDER BY a.ID, b.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + KuzuRelValue rel = value.getValue(); + KuzuInternalID srcId = rel.getSrcID(); + assertEquals(srcId.table_id, 0); + assertEquals(srcId.offset, 0); + + KuzuInternalID dstId = rel.getDstID(); + assertEquals(dstId.table_id, 0); + assertEquals(dstId.offset, 1); + + String label = rel.getLabelName(); + assertTrue(label.equals("knows")); + long size = rel.getPropertySize(); + assertEquals(size, 5); + + rel.destroy(); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetDate() throws KuzuObjectRefDestroyedException { + // Date + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.birthdate ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + LocalDate date = value.getValue(); + assertEquals((long)date.toEpochDay(), -25567L); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetTimeStamp() throws KuzuObjectRefDestroyedException { + // timestamp + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.registerTime ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + Instant stamp = value.getValue(); + assertEquals(stamp.toEpochMilli(), 1313839530000L); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetInterval() throws KuzuObjectRefDestroyedException { + // Interval + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.lastJobDuration ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + Duration interval = value.getValue(); + long month = (long) (interval.toDays() / 30); + long day = interval.toDays() - (long) (month * 30); + long micros = interval.minusDays(interval.toDays()).toMillis() * 1000; + assertEquals(month, 36); + assertEquals(day, 2); + assertEquals(micros, 46920000000L); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueGetString() throws KuzuObjectRefDestroyedException { + // String + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + assertFalse(value.isNull()); + + String str = value.getValue(); + assertTrue(str.equals("Alice")); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + } + + @Test + void ValueToString() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.isStudent, a.workedHours"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + String str = value.toString(); + assertTrue(str.equals("Alice")); + value.destroy(); + + value = flatTuple.getValue(1); + str = value.toString(); + assertTrue(str.equals("True")); + value.destroy(); + + value = flatTuple.getValue(2); + str = value.toString(); + assertTrue(str.equals("[10,5]")); + value.destroy(); + + flatTuple.destroy(); + result.destroy(); + + System.out.println("ValueToString passed"); + } + + @Test + void NodeValClone() throws KuzuObjectRefDestroyedException { + KuzuInternalID internalID = new KuzuInternalID(1, 123); + KuzuNodeValue nodeVal = new KuzuNodeValue(internalID, "person"); + KuzuNodeValue nodeValClone = nodeVal.clone(); + nodeVal.destroy(); + + assertEquals(nodeValClone.getID().table_id, 1); + assertEquals(nodeValClone.getID().offset, 123); + assertTrue(nodeValClone.getLabelName().equals("person")); + assertEquals(nodeValClone.getPropertySize(), 0); + + nodeValClone.destroy(); + System.out.println("NodeValClone passed"); + } + + @Test + void NodeValGetLabelVal() throws KuzuObjectRefDestroyedException { + KuzuInternalID internalID = new KuzuInternalID(1, 123); + KuzuNodeValue nodeVal = new KuzuNodeValue(internalID, "person"); + KuzuNodeValue nodeValClone = nodeVal.clone(); + nodeVal.destroy(); + + assertEquals(nodeValClone.getID().table_id, 1); + assertEquals(nodeValClone.getID().offset, 123); + assertTrue(nodeValClone.getLabelName().equals("person")); + assertEquals(nodeValClone.getPropertySize(), 0); + + nodeValClone.destroy(); + System.out.println("NodeValGetLabelVal passed"); + } + + @Test + void NodeValNodeValGetID() throws KuzuObjectRefDestroyedException { + KuzuInternalID internalID = new KuzuInternalID(1, 123); + KuzuNodeValue nodeVal = new KuzuNodeValue(internalID, "person"); + KuzuValue value = new KuzuValue(nodeVal); + KuzuInternalID nodeID = nodeVal.getID(); + assertEquals(nodeID.table_id, 1); + assertEquals(nodeID.offset, 123); + value.destroy(); + nodeVal.destroy(); + System.out.println("NodeValNodeValGetID passed"); + } + + @Test + void NodeValGetLabelName() throws KuzuObjectRefDestroyedException { + KuzuInternalID internalID = new KuzuInternalID(1, 123); + KuzuNodeValue nodeVal = new KuzuNodeValue(internalID, "person"); + KuzuValue value = new KuzuValue(nodeVal); + String labelName = nodeVal.getLabelName(); + assertTrue(labelName.equals("person")); + value.destroy(); + nodeVal.destroy(); + System.out.println("NodeValGetLabelName passed"); + } + + @Test + void NodeValAddProperty() throws KuzuObjectRefDestroyedException { + KuzuInternalID internalID = new KuzuInternalID(1, 123); + KuzuNodeValue nodeVal = new KuzuNodeValue(internalID, "person"); + long propertySize = nodeVal.getPropertySize(); + assertEquals(propertySize, 0); + + String propertyKey = "fName"; + KuzuValue propertyValue = new KuzuValue("Alice"); + nodeVal.addProperty(propertyKey, propertyValue); + propertySize = nodeVal.getPropertySize(); + assertEquals(propertySize, 1); + propertyValue.destroy(); + + propertyKey = "age"; + propertyValue = new KuzuValue(10); + nodeVal.addProperty(propertyKey, propertyValue); + propertySize = nodeVal.getPropertySize(); + assertEquals(propertySize, 2); + + propertyValue.destroy(); + nodeVal.destroy(); + + System.out.println("NodeValAddProperty passed"); + } + + @Test + void NodeValGetProperty() throws KuzuObjectRefDestroyedException { + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a ORDER BY a.ID"); + assertTrue(result.isSuccess()); + assertTrue(result.hasNext()); + KuzuFlatTuple flatTuple = result.getNext(); + KuzuValue value = flatTuple.getValue(0); + assertTrue(value.isOwnedByCPP()); + KuzuNodeValue node = value.getValue(); + String propertyName = node.getPropertyNameAt(0); + assertTrue(propertyName.equals("ID")); + propertyName = node.getPropertyNameAt(1); + assertTrue(propertyName.equals("fName")); + propertyName = node.getPropertyNameAt(2); + assertTrue(propertyName.equals("gender")); + propertyName = node.getPropertyNameAt(3); + assertTrue(propertyName.equals("isStudent")); + + KuzuValue propertyValue = node.getPropertyValueAt(0); + long propertyValueID = propertyValue.getValue(); + assertEquals(propertyValueID, 0); + propertyValue.destroy(); + propertyValue = node.getPropertyValueAt(1); + String propertyValuefName = propertyValue.getValue(); + assertTrue(propertyValuefName.equals("Alice")); + propertyValue.destroy(); + propertyValue = node.getPropertyValueAt(2); + long propertyValueGender = propertyValue.getValue(); + assertEquals(propertyValueGender, 1); + propertyValue.destroy(); + propertyValue = node.getPropertyValueAt(3); + boolean propertyValueIsStudent = propertyValue.getValue(); + assertEquals(propertyValueIsStudent, true); + propertyValue.destroy(); + + node.destroy(); + value.destroy(); + flatTuple.destroy(); + result.destroy(); + + System.out.println("NodeValGetProperty passed"); + } + + @Test + void NodeValToString() throws KuzuObjectRefDestroyedException { + KuzuInternalID internalID = new KuzuInternalID(1, 123); + KuzuNodeValue nodeVal = new KuzuNodeValue(internalID, "person"); + + String propertyKey = "fName"; + KuzuValue propertyValue = new KuzuValue("Smith"); + nodeVal.addProperty(propertyKey, propertyValue); + propertyValue.destroy(); + + propertyKey = "age"; + propertyValue = new KuzuValue(10L); + nodeVal.addProperty(propertyKey, propertyValue); + propertyValue.destroy(); + + String str = nodeVal.toString(); + System.out.println(str); + assertTrue(str.equals("(label:person, 1:123, {fName:Smith, age:10})")); + nodeVal.destroy(); + + System.out.println("NodeValToString passed"); + } + + @Test + void RelValClone() throws KuzuObjectRefDestroyedException { + KuzuInternalID srcID = new KuzuInternalID(1, 123); + KuzuInternalID dstID = new KuzuInternalID(2, 456); + KuzuRelValue relVal = new KuzuRelValue(srcID, dstID, "knows"); + KuzuRelValue relValClone = relVal.clone(); + relVal.destroy(); + KuzuValue srcIDVal = relValClone.getSrcIDVal(); + KuzuValue dstIDVal = relValClone.getDstIDVal(); + KuzuInternalID srcIDClone = srcIDVal.getValue(); + KuzuInternalID dstIDClone = dstIDVal.getValue(); + assertEquals(srcIDClone.table_id, srcID.table_id); + assertEquals(srcIDClone.offset, srcID.offset); + assertEquals(dstIDClone.table_id, dstID.table_id); + assertEquals(dstIDClone.offset, dstID.offset); + String labelName = relValClone.getLabelName(); + assertTrue(labelName.equals("knows")); + long propertySize = relValClone.getPropertySize(); + assertEquals(propertySize, 0); + srcIDVal.destroy(); + dstIDVal.destroy(); + relValClone.destroy(); + + System.out.println("RelValClone passed"); + } + + @Test + void RelValAddAndGetProperty() throws KuzuObjectRefDestroyedException { + KuzuInternalID srcID = new KuzuInternalID(1, 123); + KuzuInternalID dstID = new KuzuInternalID(2, 456); + KuzuRelValue relVal = new KuzuRelValue(srcID, dstID, "knows"); + long propertySize = relVal.getPropertySize(); + assertEquals(propertySize, 0); + + String propertyKey = "fName"; + KuzuValue propertyValue = new KuzuValue("Alice"); + relVal.addProperty(propertyKey, propertyValue); + propertySize = relVal.getPropertySize(); + assertEquals(propertySize, 1); + propertyValue.destroy(); + + propertyKey = "age"; + propertyValue = new KuzuValue(10L); + relVal.addProperty(propertyKey, propertyValue); + propertySize = relVal.getPropertySize(); + assertEquals(propertySize, 2); + propertyValue.destroy(); + + propertyKey = relVal.getPropertyNameAt(0); + assertTrue(propertyKey.equals("fName")); + + propertyKey = relVal.getPropertyNameAt(1); + assertTrue(propertyKey.equals("age")); + + propertyValue = relVal.getPropertyValueAt(0); + String propertyValuefName = propertyValue.getValue(); + assertTrue(propertyValuefName.equals("Alice")); + propertyValue.destroy(); + + propertyValue = relVal.getPropertyValueAt(1); + long propertyValueAge = propertyValue.getValue(); + assertEquals(propertyValueAge, 10L); + propertyValue.destroy(); + + relVal.destroy(); + System.out.println("RelValAddAndGetProperty passed"); + } + + @Test + void RelValToString() throws KuzuObjectRefDestroyedException { + KuzuInternalID srcID = new KuzuInternalID(1, 123); + KuzuInternalID dstID = new KuzuInternalID(2, 456); + KuzuRelValue relVal = new KuzuRelValue(srcID, dstID, "knows"); + + String propertyKey = "fName"; + KuzuValue propertyValue = new KuzuValue("Alice"); + relVal.addProperty(propertyKey, propertyValue); + propertyValue.destroy(); + + propertyKey = "age"; + propertyValue = new KuzuValue(10L); + relVal.addProperty(propertyKey, propertyValue); + propertyValue.destroy(); + + String str = relVal.toString(); + assertTrue(str.equals("(1:123)-[label:knows, {fName:Alice, age:10}]->(2:456)")); + + relVal.destroy(); + System.out.println("RelValToString passed"); + } +} diff --git a/tools/java_api/kuzu_java.cpp b/tools/java_api/kuzu_java.cpp new file mode 100644 index 0000000000..5255423b6f --- /dev/null +++ b/tools/java_api/kuzu_java.cpp @@ -0,0 +1,1210 @@ +#include +#include +#include "KuzuNative.h" +#include "main/kuzu.h" +#include "binder/bound_statement_result.h" +#include "planner/logical_plan/logical_plan.h" +#include "common/exception.h" +#include "common/types/value.h" +#include "main/query_summary.h" +#include "json.hpp" + +using namespace kuzu::main; +using namespace kuzu::common; +using namespace kuzu::processor; + + +jobject createJavaObject (JNIEnv * env, void * memAddress, std::string classPath, std::string refFieldName) { + uint64_t address = reinterpret_cast(memAddress); + jlong ref = static_cast(address); + + jclass javaClass = env->FindClass(classPath.c_str()); + jobject newObject = env->AllocObject(javaClass); + jfieldID refID = env->GetFieldID(javaClass , refFieldName.c_str(), "J"); + env->SetLongField(newObject, refID, ref); + return newObject; +} + +Database* getDatabase (JNIEnv * env, jobject thisDB) { + jclass javaDBClass = env->GetObjectClass(thisDB); + jfieldID fieldID = env->GetFieldID(javaDBClass, "db_ref", "J"); + jlong fieldValue = env->GetLongField(thisDB, fieldID); + + uint64_t address = static_cast(fieldValue); + Database* db = reinterpret_cast(address); + return db; +} + +Connection* getConnection (JNIEnv * env, jobject thisConn) { + jclass javaConnClass = env->GetObjectClass(thisConn); + jfieldID fieldID = env->GetFieldID(javaConnClass, "conn_ref", "J"); + jlong fieldValue = env->GetLongField(thisConn, fieldID); + + uint64_t address = static_cast(fieldValue); + Connection* conn = reinterpret_cast(address); + return conn; +} + +PreparedStatement* getPreparedStatement(JNIEnv * env, jobject thisPS) { + jclass javaPSClass = env->GetObjectClass(thisPS); + jfieldID fieldID = env->GetFieldID(javaPSClass, "ps_ref", "J"); + jlong fieldValue = env->GetLongField(thisPS, fieldID); + + uint64_t address = static_cast(fieldValue); + PreparedStatement* ps = reinterpret_cast(address); + return ps; +} + + +QueryResult* getQueryResult(JNIEnv * env, jobject thisQR) { + jclass javaQRClass = env->GetObjectClass(thisQR); + jfieldID fieldID = env->GetFieldID(javaQRClass, "qr_ref", "J"); + jlong fieldValue = env->GetLongField(thisQR, fieldID); + + uint64_t address = static_cast(fieldValue); + QueryResult* qr = reinterpret_cast(address); + return qr; +} + + +FlatTuple* getFlatTuple(JNIEnv * env, jobject thisFT) { + jclass javaFTClass = env->GetObjectClass(thisFT); + jfieldID fieldID = env->GetFieldID(javaFTClass, "ft_ref", "J"); + jlong fieldValue = env->GetLongField(thisFT, fieldID); + + uint64_t address = static_cast(fieldValue); + auto ft = reinterpret_cast*>(address); + return ft->get(); +} + +DataType* getDataType (JNIEnv * env, jobject thisDT) { + jclass javaDTClass = env->GetObjectClass(thisDT); + jfieldID fieldID = env->GetFieldID(javaDTClass, "dt_ref", "J"); + jlong fieldValue = env->GetLongField(thisDT, fieldID); + + uint64_t address = static_cast(fieldValue); + DataType* dt = reinterpret_cast(address); + return dt; +} + +Value* getValue (JNIEnv * env, jobject thisValue) { + jclass javaValueClass = env->GetObjectClass(thisValue); + jfieldID fieldID = env->GetFieldID(javaValueClass, "v_ref", "J"); + jlong fieldValue = env->GetLongField(thisValue, fieldID); + + uint64_t address = static_cast(fieldValue); + Value* v = reinterpret_cast(address); + return v; +} + +NodeVal* getNodeVal (JNIEnv * env, jobject thisNodeVal) { + jclass javaValueClass = env->GetObjectClass(thisNodeVal); + jfieldID fieldID = env->GetFieldID(javaValueClass, "nv_ref", "J"); + jlong fieldValue = env->GetLongField(thisNodeVal, fieldID); + + uint64_t address = static_cast(fieldValue); + NodeVal* nv = reinterpret_cast(address); + return nv; +} + +RelVal* getRelVal (JNIEnv * env, jobject thisRelVal) { + jclass javaValueClass = env->GetObjectClass(thisRelVal); + jfieldID fieldID = env->GetFieldID(javaValueClass, "rv_ref", "J"); + jlong fieldValue = env->GetLongField(thisRelVal, fieldID); + + uint64_t address = static_cast(fieldValue); + RelVal* rv = reinterpret_cast(address); + return rv; +} + +internalID_t getInternalID (JNIEnv * env, jobject id) { + jclass javaIDClass = env->GetObjectClass(id); + jfieldID fieldID = env->GetFieldID(javaIDClass, "table_id", "J"); + long table_id = static_cast(env->GetLongField(id, fieldID)); + fieldID = env->GetFieldID(javaIDClass, "offset", "J"); + long offset = static_cast(env->GetLongField(id, fieldID)); + return internalID_t(offset, table_id); +} + +std::string dataTypeIDToString (uint8_t id) { + switch (id) + { + case DataTypeID::ANY: return "ANY"; + case DataTypeID::NODE: return "NODE"; + case DataTypeID::REL: return "REL"; + case DataTypeID::SERIAL: return "SERIAL"; + case DataTypeID::BOOL: return "BOOL"; + case DataTypeID::INT64: return "INT64"; + case DataTypeID::INT32: return "INT32"; + case DataTypeID::INT16: return "INT16"; + case DataTypeID::DOUBLE: return "DOUBLE"; + case DataTypeID::FLOAT: return "FLOAT"; + case DataTypeID::DATE: return "DATE"; + case DataTypeID::TIMESTAMP: return "TIMESTAMP"; + case DataTypeID::INTERVAL: return "INTERVAL"; + case DataTypeID::FIXED_LIST: return "FIXED_LIST"; + case DataTypeID::INTERNAL_ID: return "INTERNAL_ID"; + case DataTypeID::STRING: return "STRING"; + case DataTypeID::VAR_LIST: return "VAR_LIST"; + case DataTypeID::STRUCT: return "STRUCT"; + default: throw std::invalid_argument("Unimplemented item"); + } +} + +void javaMapToCPPMap (JNIEnv * env, jobject javaMap, std::unordered_map>& cppMap) { + + jclass mapClass = env->FindClass("java/util/Map"); + jmethodID entrySet = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); + jobject set = env->CallObjectMethod(javaMap, entrySet); + jclass setClass = env->FindClass("java/util/Set"); + jmethodID iterator = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); + jobject iter = env->CallObjectMethod(set, iterator); + jclass iteratorClass = env->FindClass("java/util/Iterator"); + jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); + jmethodID next = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); + jclass entryClass = env->FindClass("java/util/Map$Entry"); + jmethodID entryGetKey = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); + jmethodID entryGetValue = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); + + while (env->CallBooleanMethod(iter, hasNext)) { + jobject entry = env->CallObjectMethod(iter, next); + jstring key = (jstring) env->CallObjectMethod(entry, entryGetKey); + jobject value = env->CallObjectMethod(entry, entryGetValue); + const char* keyStr = env->GetStringUTFChars(key, NULL); + Value* v = getValue(env, value); + std::shared_ptr value_ptr(v); + cppMap.insert({keyStr, value_ptr}); + + env->DeleteLocalRef(entry); + env->ReleaseStringUTFChars(key, keyStr); + env->DeleteLocalRef(key); + env->DeleteLocalRef(value); + } +} + +/** + * All Database native functions +*/ + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1database_1init + (JNIEnv * env, jclass, jstring database_path, jlong buffer_pool_size) { + + const char* path = env->GetStringUTFChars(database_path, JNI_FALSE); + uint64_t buffer = static_cast(buffer_pool_size); + try { + Database* db = buffer == 0 ? new Database(path) : + new Database(path, SystemConfig(buffer)); + uint64_t address = reinterpret_cast(db); + + env->ReleaseStringUTFChars(database_path, path); + return static_cast(address); + } catch (Exception& e) { + env->ReleaseStringUTFChars(database_path, path); + jclass Exception = env->FindClass("java/lang/Exception"); + env->ThrowNew(Exception, e.what()); + } +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1database_1destroy + (JNIEnv * env, jclass, jobject thisDB) { + Database* db = getDatabase(env, thisDB); + delete db; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1database_1set_1logging_1level + (JNIEnv * env, jclass, jstring logging_level) { + const char * lvl = env->GetStringUTFChars(logging_level, JNI_FALSE); + try{ + Database::setLoggingLevel(lvl); + env->ReleaseStringUTFChars(logging_level, lvl); + } catch (ConversionException e){ + env->ReleaseStringUTFChars(logging_level, lvl); + jclass Exception = env->FindClass("java/lang/Exception"); + env->ThrowNew(Exception,e.what()); + } +} + +/** + * All Connection native functions +*/ + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1init + (JNIEnv *env , jclass, jobject db) { + + try { + Database* conn_db = getDatabase(env, db); + + Connection* conn = new Connection(conn_db); + uint64_t connAddress = reinterpret_cast(conn); + + return static_cast(connAddress); + } catch (Exception& e) { + jclass Exception = env->FindClass("java/lang/Exception"); + env->ThrowNew(Exception, e.what()); + } +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1destroy + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + delete conn; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1begin_1read_1only_1transaction + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + conn->beginReadOnlyTransaction(); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1begin_1write_1transaction + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + conn->beginWriteTransaction(); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1commit + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + conn->commit(); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1rollback + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + conn->rollback(); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1set_1max_1num_1thread_1for_1exec + (JNIEnv * env, jclass, jobject thisConn, jlong num_threads) { + Connection* conn = getConnection(env, thisConn); + uint64_t threads = static_cast(num_threads); + conn->setMaxNumThreadForExec(threads); +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1get_1max_1num_1thread_1for_1exec + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + uint64_t threads = conn->getMaxNumThreadForExec(); + jlong num_threads = static_cast(threads); + return num_threads; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1query + (JNIEnv * env, jclass, jobject thisConn, jstring query) { + Connection* conn = getConnection(env, thisConn); + const char * CPPQuery = env->GetStringUTFChars(query, JNI_FALSE); + auto query_result = conn->query(CPPQuery).release(); + env->ReleaseStringUTFChars(query, CPPQuery); + + uint64_t qrAddress = reinterpret_cast(query_result); + jlong qr_ref = static_cast(qrAddress); + + jclass qrClass = env->FindClass("tools/java_api/KuzuQueryResult"); + jobject newQRObject = env->AllocObject(qrClass); + jfieldID refID = env->GetFieldID(qrClass , "qr_ref", "J"); + env->SetLongField(newQRObject, refID, qr_ref); + return newQRObject; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1prepare + (JNIEnv * env, jclass, jobject thisConn, jstring query) { + Connection* conn = getConnection(env, thisConn); + const char * cppquery = env->GetStringUTFChars(query, JNI_FALSE); + + PreparedStatement* prepared_statement = conn->prepare(cppquery).release(); + env->ReleaseStringUTFChars(query, cppquery); + if (prepared_statement == nullptr) { + return nullptr; + } + + jobject ret = createJavaObject(env, prepared_statement, "tools/java_api/KuzuPreparedStatement", "ps_ref"); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1execute + (JNIEnv * env, jclass, jobject thisConn, jobject preStm, jobject param_map) { + Connection* conn = getConnection(env, thisConn); + PreparedStatement* ps = getPreparedStatement(env, preStm); + + std::unordered_map> param; + javaMapToCPPMap(env, param_map, param); + + for (auto const &pair: param) { + std::cout << "{" << pair.first << ": " << pair.second.get()->toString() << "}\n"; + } + + auto query_result = conn->executeWithParams(ps, param).release(); + if (query_result == nullptr) { + return nullptr; + } + + jobject ret = createJavaObject(env, query_result, "tools/java_api/KuzuQueryResult", "qr_ref"); + return ret; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1get_1node_1table_1names + (JNIEnv * env , jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + jstring result = env->NewStringUTF(conn->getNodeTableNames().c_str()); + return result; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1get_1rel_1table_1names + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + jstring result = env->NewStringUTF(conn->getRelTableNames().c_str()); + return result; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1get_1node_1property_1names + (JNIEnv * env, jclass, jobject thisConn, jstring table_name) { + Connection* conn = getConnection(env, thisConn); + const char * name = env->GetStringUTFChars(table_name, JNI_FALSE); + jstring result = env->NewStringUTF(conn->getNodePropertyNames(name).c_str()); + env->ReleaseStringUTFChars(table_name, name); + return result; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1get_1rel_1property_1names + (JNIEnv * env, jclass, jobject thisConn, jstring table_name) { + Connection* conn = getConnection(env, thisConn); + const char * name = env->GetStringUTFChars(table_name, JNI_FALSE); + jstring result = env->NewStringUTF(conn->getRelPropertyNames(name).c_str()); + env->ReleaseStringUTFChars(table_name, name); + return result; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1interrupt + (JNIEnv * env, jclass, jobject thisConn) { + Connection* conn = getConnection(env, thisConn); + conn->interrupt(); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1connection_1set_1query_1timeout + (JNIEnv * env, jclass, jobject thisConn, jlong timeout_in_ms) { + Connection* conn = getConnection(env, thisConn); + uint64_t timeout = static_cast(timeout_in_ms); + conn->setQueryTimeOut(timeout); +} + +/** + * All PreparedStatement native functions +*/ + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1prepared_1statement_1destroy + (JNIEnv * env, jclass, jobject thisPS) { + PreparedStatement* ps = getPreparedStatement(env, thisPS); + delete ps; +} + +JNIEXPORT jboolean JNICALL Java_tools_java_1api_KuzuNative_kuzu_1prepared_1statement_1allow_1active_1transaction + (JNIEnv * env, jclass, jobject thisPS) { + PreparedStatement* ps = getPreparedStatement(env, thisPS); + return static_cast(ps->allowActiveTransaction()); +} + +JNIEXPORT jboolean JNICALL Java_tools_java_1api_KuzuNative_kuzu_1prepared_1statement_1is_1success + (JNIEnv * env, jclass, jobject thisPS) { + PreparedStatement* ps = getPreparedStatement(env, thisPS); + return static_cast(ps->isSuccess()); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1prepared_1statement_1get_1error_1message + (JNIEnv * env, jclass, jobject thisPS) { + PreparedStatement* ps = getPreparedStatement(env, thisPS); + std::string errorMessage = ps->getErrorMessage(); + jstring msg = env->NewStringUTF(errorMessage.c_str()); + return msg; +} + +/** + * All QueryResult native functions +*/ + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1destroy + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + delete qr; +} + +JNIEXPORT jboolean JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1is_1success + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + return static_cast(qr->isSuccess()); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1error_1message + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + std::string errorMessage = qr->getErrorMessage(); + jstring msg = env->NewStringUTF(errorMessage.c_str()); + return msg; +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1num_1columns + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + return static_cast(qr->getNumColumns()); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1column_1name + (JNIEnv * env, jclass, jobject thisQR, jlong index) { + QueryResult* qr = getQueryResult(env, thisQR); + auto column_names = qr->getColumnNames(); + uint64_t idx = static_cast(index); + if (idx >= column_names.size()) { + return nullptr; + } + std::string column_name = column_names[idx]; + jstring name = env->NewStringUTF(column_name.c_str()); + return name; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1column_1data_1type + (JNIEnv * env, jclass, jobject thisQR, jlong index) { + QueryResult* qr = getQueryResult(env, thisQR); + auto column_datatypes = qr->getColumnDataTypes(); + uint64_t idx = static_cast(index); + if (idx >= column_datatypes.size()) { + return nullptr; + } + DataType column_datatype = column_datatypes[idx]; + DataType* cdt_copy = new DataType(column_datatype); + + uint64_t dtAddress = reinterpret_cast(cdt_copy); + jlong dt_ref = static_cast(dtAddress); + + jclass dtClass = env->FindClass("tools/java_api/KuzuDataType"); + jobject newDTObject = env->AllocObject(dtClass); + jfieldID refID = env->GetFieldID(dtClass , "dt_ref", "J"); + env->SetLongField(newDTObject, refID, dt_ref); + return newDTObject; +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1num_1tuples + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + return static_cast(qr->getNumTuples()); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1query_1summary + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + auto query_summary = qr->getQuerySummary(); + + jdouble cmpTime = static_cast(query_summary->getCompilingTime()); + jdouble exeTime = static_cast(query_summary->getExecutionTime()); + + jclass qsClass = env->FindClass("tools/java_api/KuzuQuerySummary"); + jmethodID ctor = env->GetMethodID(qsClass, "", "(DD)V"); + jobject newQSObject = env->NewObject(qsClass, ctor, cmpTime, exeTime); + return newQSObject; +} + +JNIEXPORT jboolean JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1has_1next + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + return static_cast(qr->hasNext()); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1get_1next + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + auto flat_tuple = qr->getNext(); + + auto newFT = new std::shared_ptr(flat_tuple); + uint64_t ftAddress = reinterpret_cast(newFT); + jlong ft_ref = static_cast(ftAddress); + + jclass ftClass = env->FindClass("tools/java_api/KuzuFlatTuple"); + jobject newFTObject = env->AllocObject(ftClass); + jfieldID refID = env->GetFieldID(ftClass , "ft_ref", "J"); + env->SetLongField(newFTObject, refID, ft_ref); + return newFTObject; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1to_1string + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + std::string result_string = qr->toString(); + jstring ret = env->NewStringUTF(result_string.c_str()); + return ret; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1write_1to_1csv + (JNIEnv * env, jclass, jobject thisQR, jstring file_path, jchar delimiter, jchar escape_char, jchar new_line) { + QueryResult* qr = getQueryResult(env, thisQR); + const char * cpp_file_path = env->GetStringUTFChars(file_path, JNI_FALSE); + + // TODO: confirm this convertion is ok to do. + // jchar is 16-bit unicode character so converting to char will lose the higher oreder-bits + char cpp_delimiter = static_cast(delimiter); + char cpp_escape_char = static_cast(escape_char); + char cpp_new_line = static_cast(new_line); + + qr->writeToCSV(cpp_file_path, cpp_delimiter, cpp_escape_char, cpp_new_line); + env->ReleaseStringUTFChars(file_path, cpp_file_path); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1query_1result_1reset_1iterator + (JNIEnv * env, jclass, jobject thisQR) { + QueryResult* qr = getQueryResult(env, thisQR); + qr->resetIterator(); +} + +/** + * All FlatTuple native functions +*/ + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1flat_1tuple_1destroy + (JNIEnv * env, jclass, jobject thisFT) { + jclass javaFTClass = env->GetObjectClass(thisFT); + jfieldID fieldID = env->GetFieldID(javaFTClass, "ft_ref", "J"); + jlong fieldValue = env->GetLongField(thisFT, fieldID); + + uint64_t address = static_cast(fieldValue); + auto flat_tuple_shared_ptr = reinterpret_cast*>(address); + + flat_tuple_shared_ptr->reset(); + delete flat_tuple_shared_ptr; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1flat_1tuple_1get_1value + (JNIEnv * env, jclass, jobject thisFT, jlong index) { + FlatTuple* ft = getFlatTuple(env, thisFT); + uint32_t idx = static_cast(index); + Value* value; + try { + value = ft->getValue(index); + } catch (Exception& e) { return nullptr; } + + jobject v = createJavaObject(env, value, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(v); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(v, fieldID, static_cast(true)); + + return v; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1flat_1tuple_1to_1string + (JNIEnv * env, jclass, jobject thisFT) { + FlatTuple* ft = getFlatTuple(env, thisFT); + std::string result_string = ft->toString(); + jstring ret = env->NewStringUTF(result_string.c_str()); + return ret; +} + +/** + * All DataType native functions +*/ + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1create + (JNIEnv * env, jclass, jobject id, jobject child_type, jlong fixed_num_elements_in_list) { + jclass javaIDClass = env->GetObjectClass(id); + jfieldID fieldID = env->GetFieldID(javaIDClass, "value", "I"); + jint fieldValue = env->GetIntField(id, fieldID); + + uint8_t data_type_id_u8 = static_cast(fieldValue); + DataType* data_type; + if (child_type == nullptr) { + data_type = new DataType(static_cast(data_type_id_u8)); + } else { + auto child_type_pty = std::make_unique(*getDataType(env, child_type)); + uint64_t num = static_cast(fixed_num_elements_in_list); + data_type = num > 0 ? + new DataType(std::move(child_type_pty), num) : + new DataType(std::move(child_type_pty)); + } + uint64_t address = reinterpret_cast(data_type); + return static_cast(address); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1clone + (JNIEnv * env, jclass, jobject thisDT) { + DataType* oldDT = getDataType(env, thisDT); + DataType* newDT = new DataType(*oldDT); + + jobject dt = createJavaObject(env, newDT, "tools/java_api/KuzuDataType", "dt_ref"); + return dt; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1destroy + (JNIEnv * env, jclass, jobject thisDT) { + DataType* dt = getDataType(env, thisDT); + delete dt; +} + +JNIEXPORT jboolean JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1equals + (JNIEnv * env, jclass, jobject dt1, jobject dt2) { + DataType* cppdt1 = getDataType(env, dt1); + DataType* cppdt2 = getDataType(env, dt2); + + return static_cast(*cppdt1 == *cppdt2); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1get_1id + (JNIEnv * env, jclass, jobject thisDT) { + + DataType* dt = getDataType(env, thisDT); + uint8_t id_u8 = static_cast(dt->getTypeID()); + std::string id_str = dataTypeIDToString(id_u8); + jclass idClass = env->FindClass("tools/java_api/KuzuDataTypeID"); + jfieldID idField = env->GetStaticFieldID(idClass, id_str.c_str(), "Ltools/java_api/KuzuDataTypeID;"); + jobject id = env->GetStaticObjectField(idClass, idField); + return id; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1get_1child_1type + (JNIEnv * env, jclass, jobject thisDT) { + DataType* parent_type = getDataType(env, thisDT); + if (parent_type->getTypeID() != DataTypeID::FIXED_LIST && + parent_type->getTypeID() != DataTypeID::VAR_LIST) { + return nullptr; + } + DataType* child_type = parent_type->getChildType(); + if (child_type == nullptr) { + return nullptr; + } + + DataType* new_child_type = new DataType(*child_type); + jobject ret = createJavaObject(env, new_child_type, "tools/java_api/KuzuDataType", "dt_ref"); + return ret; +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1data_1type_1get_1fixed_1num_1elements_1in_1list + (JNIEnv * env, jclass, jobject thisDT) { + DataType* dt = getDataType(env, thisDT); + if (dt->getTypeID() != DataTypeID::FIXED_LIST) { + return 0; + } + auto extra_info = dt->getExtraTypeInfo(); + if (extra_info == nullptr) { + return 0; + } + auto fixed_list_info = dynamic_cast(extra_info); + return static_cast(fixed_list_info->getFixedNumElementsInList()); +} + +/** + * All Value native functions +*/ + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1create_1null + (JNIEnv * env, jclass) { + Value* v = new Value(Value::createNullValue()); + jobject ret = createJavaObject(env, v, "tools/java_api/KuzuValue", "v_ref"); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1create_1null_1with_1data_1type + (JNIEnv * env, jclass, jobject data_type) { + DataType* dt = getDataType(env, data_type); + Value* v = new Value(Value::createNullValue(*dt)); + jobject ret = createJavaObject(env, v, "tools/java_api/KuzuValue", "v_ref"); + return ret; +} + +JNIEXPORT jboolean JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1is_1null + (JNIEnv * env, jclass, jobject thisV) { + Value* v = getValue(env, thisV); + return static_cast(v->isNull()); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1set_1null + (JNIEnv * env, jclass, jobject thisV, jboolean is_null) { + Value* v = getValue(env, thisV); + v->setNull(static_cast(is_null)); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1create_1default + (JNIEnv * env, jclass, jobject data_type) { + DataType* dt = getDataType(env, data_type); + Value* v = new Value(Value::createDefaultValue(*dt)); + jobject ret = createJavaObject(env, v, "tools/java_api/KuzuValue", "v_ref"); + return ret; +} + + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1create_1value + (JNIEnv * env, jclass, jobject val) { + Value* v; + jclass val_class = env->GetObjectClass(val); + if (env->IsInstanceOf(val, env->FindClass("java/lang/Boolean"))) { + jboolean value = env->CallBooleanMethod(val, env->GetMethodID(val_class, "booleanValue", "()Z")); + v = new Value(static_cast(value)); + } else if (env->IsInstanceOf(val, env->FindClass("java/lang/Short"))) { + jshort value = env->CallShortMethod(val, env->GetMethodID(val_class, "shortValue", "()S")); + v = new Value(static_cast(value)); + } else if (env->IsInstanceOf(val, env->FindClass("java/lang/Integer"))) { + jint value = env->CallIntMethod(val, env->GetMethodID(val_class, "intValue", "()I")); + v = new Value(static_cast(value)); + } else if (env->IsInstanceOf(val, env->FindClass("java/lang/Long"))) { + jlong value = env->CallLongMethod(val, env->GetMethodID(val_class, "longValue", "()J")); + v = new Value(static_cast(value)); + } else if (env->IsInstanceOf(val, env->FindClass("java/lang/Float"))) { + jfloat value = env->CallFloatMethod(val, env->GetMethodID(val_class, "floatValue", "()F")); + v = new Value(static_cast(value)); + } else if (env->IsInstanceOf(val, env->FindClass("java/lang/Double"))) { + jdouble value = env->CallDoubleMethod(val, env->GetMethodID(val_class, "doubleValue", "()D")); + v = new Value(static_cast(value)); + } else if (env->IsInstanceOf(val, env->FindClass("java/lang/String"))) { + jstring value = static_cast(val); + const char * str = env->GetStringUTFChars(value, NULL); + v = new Value(str); + env->ReleaseStringUTFChars(value, str); + } else if (env->IsInstanceOf(val, env->FindClass("tools/java_api/KuzuInternalID"))) { + jfieldID fieldID = env->GetFieldID(val_class, "table_id", "J"); + long table_id = static_cast(env->GetLongField(val, fieldID)); + fieldID = env->GetFieldID(val_class, "offset", "J"); + long offset = static_cast(env->GetLongField(val, fieldID)); + internalID_t id(offset, table_id); + v = new Value(id); + } else if (env->IsInstanceOf(val, env->FindClass("tools/java_api/KuzuNodeValue"))) { + auto node_val = std::make_unique(*getNodeVal(env, val)); + v = new Value(std::move(node_val)); + } else if (env->IsInstanceOf(val, env->FindClass("tools/java_api/KuzuRelValue"))) { + auto rel_val = std::make_unique(*getRelVal(env, val)); + v = new Value(std::move(rel_val)); + } else if (env->IsInstanceOf(val, env->FindClass("java/time/LocalDate"))) { + jmethodID toEpochDay = env->GetMethodID(val_class, "toEpochDay", "()J"); + long days = static_cast(env->CallLongMethod(val, toEpochDay)); + v = new Value(date_t(days)); + } else if (env->IsInstanceOf(val, env->FindClass("java/time/Instant"))) { + // TODO: Need to review this for overflow + jmethodID getEpochSecond = env->GetMethodID(val_class, "getEpochSecond", "()J"); + jmethodID getNano = env->GetMethodID(val_class, "getNano", "()I"); + long seconds = static_cast(env->CallLongMethod(val, getEpochSecond)); + long nano = static_cast(env->CallLongMethod(val, getNano)); + + long micro = (seconds * 1000000L) + (nano / 1000L); + v = new Value(timestamp_t(micro)); + } else if (env->IsInstanceOf(val, env->FindClass("java/time/Duration"))) { + jmethodID toMillis = env->GetMethodID(val_class, "toMillis", "()J"); + long milis = static_cast(env->CallLongMethod(val, toMillis)); + v = new Value(interval_t(0, 0, milis * 1000L)); + } else { + // Throw exception here + return -1; + } + uint64_t address = reinterpret_cast(v); + return static_cast(address); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1clone + (JNIEnv * env, jclass, jobject thisValue) { + Value* v = getValue(env, thisValue); + Value* copy = new Value(*v); + return createJavaObject(env, copy, "tools/java_api/KuzuValue", "v_ref"); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1copy + (JNIEnv * env, jclass, jobject thisValue, jobject otherValue) { + Value* thisV = getValue(env, thisValue); + Value* otherV = getValue(env, otherValue); + thisV->copyValueFrom(*otherV); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1destroy + (JNIEnv * env, jclass, jobject thisValue) { + Value* v = getValue(env, thisValue); + delete v; +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1get_1list_1size + (JNIEnv * env, jclass, jobject thisValue) { + Value* v = getValue(env, thisValue); + return static_cast(v->getListValReference().size()); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1get_1list_1element + (JNIEnv * env, jclass, jobject thisValue, jlong index) { + Value* v = getValue(env, thisValue); + uint64_t idx = static_cast(index); + + auto& list_val = v->getListValReference(); + + if (idx >= list_val.size()) { + return nullptr; + } + + auto& list_element = list_val[index]; + auto val = list_element.get(); + + jobject element = createJavaObject(env, val, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(element); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(element, fieldID, static_cast(true)); + return element; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1get_1data_1type + (JNIEnv * env, jclass, jobject thisValue) { + Value* v = getValue(env, thisValue); + DataType* dt = new DataType(v->getDataType()); + return createJavaObject(env, dt, "tools/java_api/KuzuDataType", "dt_ref"); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1get_1value + (JNIEnv * env, jclass, jobject thisValue) { + Value* v = getValue(env, thisValue); + DataType dt = v->getDataType(); + + switch(dt.typeID) { + case DataTypeID::BOOL: + { + jclass retClass = env->FindClass("java/lang/Boolean"); + jmethodID ctor = env->GetMethodID(retClass, "", "(Z)V"); + jboolean val = static_cast(v->getValue()); + jobject ret = env->NewObject(retClass, ctor, val); + return ret; + } + case DataTypeID::INT64: + { + jclass retClass = env->FindClass("java/lang/Long"); + jmethodID ctor = env->GetMethodID(retClass, "", "(J)V"); + jlong val = static_cast(v->getValue()); + jobject ret = env->NewObject(retClass, ctor, val); + return ret; + } + case DataTypeID::INT32: + { + jclass retClass = env->FindClass("java/lang/Integer"); + jmethodID ctor = env->GetMethodID(retClass, "", "(I)V"); + jint val = static_cast(v->getValue()); + jobject ret = env->NewObject(retClass, ctor, val); + return ret; + } + case DataTypeID::INT16: + { + jclass retClass = env->FindClass("java/lang/Short"); + jmethodID ctor = env->GetMethodID(retClass, "", "(S)V"); + jshort val = static_cast(v->getValue()); + jobject ret = env->NewObject(retClass, ctor, val); + return ret; + } + case DataTypeID::DOUBLE: + { + jclass retClass = env->FindClass("java/lang/Double"); + jmethodID ctor = env->GetMethodID(retClass, "", "(D)V"); + jdouble val = static_cast(v->getValue()); + jobject ret = env->NewObject(retClass, ctor, val); + return ret; + } + case DataTypeID::FLOAT: + { + jclass retClass = env->FindClass("java/lang/Float"); + jmethodID ctor = env->GetMethodID(retClass, "", "(F)V"); + jfloat val = static_cast(v->getValue()); + jobject ret = env->NewObject(retClass, ctor, val); + return ret; + } + case DataTypeID::DATE: + { + jclass retClass = env->FindClass("java/time/LocalDate"); + date_t date = v->getValue(); + jclass ldClass = env->FindClass("java/time/LocalDate"); + jmethodID ofEpochDay = env->GetStaticMethodID(ldClass, "ofEpochDay", "(J)Ljava/time/LocalDate;"); + jobject ret = env->CallStaticObjectMethod(ldClass, ofEpochDay, static_cast(date.days)); + return ret; + } + case DataTypeID::TIMESTAMP: + { + timestamp_t ts = v->getValue(); + int64_t seconds = ts.value / 1000000L; + int64_t nano = ts.value % 1000000L * 1000L; + jclass retClass = env->FindClass("java/time/Instant"); + jmethodID ofEpochSecond = env->GetStaticMethodID(retClass, "ofEpochSecond", "(JJ)Ljava/time/Instant;"); + jobject ret = env->CallStaticObjectMethod(retClass, ofEpochSecond, seconds, nano); + return ret; + } + case DataTypeID::INTERVAL: + { + jclass retClass = env->FindClass("java/time/Duration"); + jmethodID ofMillis = env->GetStaticMethodID(retClass, "ofMillis", "(J)Ljava/time/Duration;"); + interval_t in = v->getValue(); + long millis = Interval::getMicro(in) / 1000; + jobject ret = env->CallStaticObjectMethod(retClass, ofMillis, millis); + return ret; + } + case DataTypeID::INTERNAL_ID: + { + jclass retClass = env->FindClass("tools/java_api/KuzuInternalID"); + jmethodID ctor = env->GetMethodID(retClass, "", "(JJ)V"); + internalID_t iid = v->getValue(); + jobject ret = env->NewObject(retClass, ctor, iid.tableID, iid.offset); + return ret; + } + case DataTypeID::STRING: + { + std::string str = v->getValue(); + jstring ret = env->NewStringUTF(str.c_str()); + return ret; + } + case DataTypeID::NODE: + { + auto node_val = v->getValue(); + NodeVal* nv = new NodeVal(node_val); + return createJavaObject(env, nv, "tools/java_api/KuzuNodeValue", "nv_ref"); + } + case DataTypeID::REL: + { + auto rel_val = v->getValue(); + RelVal* nv = new RelVal(rel_val); + return createJavaObject(env, nv, "tools/java_api/KuzuRelValue", "rv_ref"); + } + default: + { + //Throw exception here? + return nullptr; + } + + return nullptr; + } +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1value_1to_1string + (JNIEnv * env, jclass, jobject thisValue) { + Value* v = getValue(env, thisValue); + std::string result_string = v->toString(); + jstring ret = env->NewStringUTF(result_string.c_str()); + return ret; +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1create + (JNIEnv * env, jclass, jobject id, jstring label) { + jclass idClass = env->FindClass("tools/java_api/KuzuInternalID"); + jfieldID fieldID = env->GetFieldID(idClass, "table_id", "J"); + long table_id = static_cast(env->GetLongField(id, fieldID)); + fieldID = env->GetFieldID(idClass, "offset", "J"); + long offset = static_cast(env->GetLongField(id, fieldID)); + + auto id_val = std::make_unique(internalID_t(offset, table_id)); + const char * labelstr = env->GetStringUTFChars(label, JNI_FALSE); + auto label_val = std::make_unique(labelstr); + + NodeVal* node_val = new NodeVal(std::move(id_val), std::move(label_val)); + uint64_t address = reinterpret_cast(node_val); + env->ReleaseStringUTFChars(label, labelstr); + return static_cast(address); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1clone + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + NodeVal* newNV = new NodeVal(*nv); + return createJavaObject(env, newNV, "tools/java_api/KuzuNodeValue", "nv_ref"); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1destroy + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + delete nv; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1id_1val + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + auto idVal = nv->getNodeIDVal(); + + jobject ret = createJavaObject(env, idVal, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(ret); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(ret, fieldID, static_cast(true)); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1label_1val + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + auto labelVal = nv->getNodeIDVal(); + + jobject ret = createJavaObject(env, labelVal, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(ret); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(ret, fieldID, static_cast(true)); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1id + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + auto id = nv->getNodeID(); + + jclass retClass = env->FindClass("tools/java_api/KuzuInternalID"); + jmethodID ctor = env->GetMethodID(retClass, "", "(JJ)V"); + jobject ret = env->NewObject(retClass, ctor, id.tableID, id.offset); + return ret; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1label_1name + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + return env->NewStringUTF(nv->getLabelName().c_str()); +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1property_1size + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + return static_cast(nv->getProperties().size()); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1property_1name_1at + (JNIEnv * env, jclass, jobject thisNV, jlong index) { + NodeVal* nv = getNodeVal(env, thisNV); + uint64_t idx = static_cast(index); + return env->NewStringUTF(nv->getProperties().at(idx).first.c_str()); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1get_1property_1value_1at + (JNIEnv * env, jclass, jobject thisNV, jlong index) { + NodeVal* nv = getNodeVal(env, thisNV); + uint64_t idx = static_cast(index); + Value* val = nv->getProperties().at(idx).second.get(); + + jobject ret = createJavaObject(env, val, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(ret); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(ret, fieldID, static_cast(true)); + return ret; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1add_1property + (JNIEnv * env, jclass, jobject thisNV, jstring key, jobject value) { + NodeVal* nv = getNodeVal(env, thisNV); + const char * k = env->GetStringUTFChars(key, JNI_FALSE); + auto v = std::make_unique(*getValue(env, value)); + nv->addProperty(k, std::move(v)); + env->ReleaseStringUTFChars(key, k); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1node_1val_1to_1string + (JNIEnv * env, jclass, jobject thisNV) { + NodeVal* nv = getNodeVal(env, thisNV); + std::string result_string = nv->toString(); + jstring ret = env->NewStringUTF(result_string.c_str()); + return ret; +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1create + (JNIEnv * env, jclass, jobject src_id, jobject dst_id, jstring label) { + internalID_t cpp_src_id = getInternalID(env, src_id); + internalID_t cpp_dst_id = getInternalID(env, dst_id); + auto src_id_val = std::make_unique(internalID_t(cpp_src_id.offset, cpp_src_id.tableID)); + auto dst_id_val = std::make_unique(internalID_t(cpp_dst_id.offset, cpp_dst_id.tableID)); + const char * lablestr = env->GetStringUTFChars(label, JNI_FALSE); + auto label_val = std::make_unique(lablestr); + RelVal* rv = new RelVal(std::move(src_id_val), std::move(dst_id_val), std::move(label_val)); + + uint64_t address = reinterpret_cast(rv); + env->ReleaseStringUTFChars(label, lablestr); + return static_cast(address); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1clone + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + RelVal* newRV = new RelVal(*rv); + return createJavaObject(env, newRV, "tools/java_api/KuzuRelValue", "rv_ref"); +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1destroy + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + delete rv; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1src_1id_1val + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + auto idVal = rv->getSrcNodeIDVal(); + + jobject ret = createJavaObject(env, idVal, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(ret); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(ret, fieldID, static_cast(true)); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1dst_1id_1val + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + auto idVal = rv->getDstNodeIDVal(); + + jobject ret = createJavaObject(env, idVal, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(ret); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(ret, fieldID, static_cast(true)); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1src_1id + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + internalID_t id = rv->getSrcNodeID(); + + jclass retClass = env->FindClass("tools/java_api/KuzuInternalID"); + jmethodID ctor = env->GetMethodID(retClass, "", "(JJ)V"); + jobject ret = env->NewObject(retClass, ctor, id.tableID, id.offset); + return ret; +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1dst_1id + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + internalID_t id = rv->getDstNodeID(); + + jclass retClass = env->FindClass("tools/java_api/KuzuInternalID"); + jmethodID ctor = env->GetMethodID(retClass, "", "(JJ)V"); + jobject ret = env->NewObject(retClass, ctor, id.tableID, id.offset); + return ret; +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1label_1name + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + return env->NewStringUTF(rv->getLabelName().c_str()); +} + +JNIEXPORT jlong JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1property_1size + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + return static_cast(rv->getProperties().size()); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1property_1name_1at + (JNIEnv * env, jclass, jobject thisRV, jlong index) { + RelVal* rv = getRelVal(env, thisRV); + auto& name = rv->getProperties().at(index).first; + return env->NewStringUTF(name.c_str()); +} + +JNIEXPORT jobject JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1get_1property_1value_1at + (JNIEnv * env, jclass, jobject thisRV, jlong index) { + RelVal* rv = getRelVal(env, thisRV); + uint64_t idx = static_cast(index); + Value* val = rv->getProperties().at(idx).second.get(); + + jobject ret = createJavaObject(env, val, "tools/java_api/KuzuValue", "v_ref"); + jclass clazz = env->GetObjectClass(ret); + jfieldID fieldID = env->GetFieldID(clazz, "isOwnedByCPP", "Z"); + env->SetBooleanField(ret, fieldID, static_cast(true)); + return ret; +} + +JNIEXPORT void JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1add_1property + (JNIEnv * env, jclass, jobject thisRV, jstring key, jobject value) { + RelVal* rv = getRelVal(env, thisRV); + const char * k = env->GetStringUTFChars(key, JNI_FALSE); + auto v = std::make_unique(*getValue(env, value)); + rv->addProperty(k, std::move(v)); + + env->ReleaseStringUTFChars(key, k); +} + +JNIEXPORT jstring JNICALL Java_tools_java_1api_KuzuNative_kuzu_1rel_1val_1to_1string + (JNIEnv * env, jclass, jobject thisRV) { + RelVal* rv = getRelVal(env, thisRV); + std::string result_string = rv->toString(); + jstring ret = env->NewStringUTF(result_string.c_str()); + return ret; +} diff --git a/tools/java_api/test.java b/tools/java_api/test.java new file mode 100644 index 0000000000..1edcac8adf --- /dev/null +++ b/tools/java_api/test.java @@ -0,0 +1,74 @@ +package tools.java_api; +import java.time.*; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.File; + +public class test { + + public static void deleteFolder(File folder) { + if (folder.exists()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteFolder(file); + } else { + file.delete(); + } + } + } + folder.delete(); + System.out.println("Folder deleted: " + folder.getAbsolutePath()); + } + } + + public static void main(String[] args) throws KuzuObjectRefDestroyedException { + + BufferedReader reader; + KuzuDatabase db = new KuzuDatabase("./nope/java_api_test_db", 0); + KuzuConnection conn = new KuzuConnection(db); + try { + reader = new BufferedReader(new FileReader("./../../dataset/tinysnb/schema.cypher")); + String line = reader.readLine(); + + while (line != null) { + conn.query(line); + line = reader.readLine(); + } + + reader.close(); + + reader = new BufferedReader(new FileReader("./../../dataset/tinysnb/copy.cypher")); + line = reader.readLine(); + + while (line != null) { + conn.query(line); + line = reader.readLine(); + } + + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + KuzuQueryResult result = conn.query("MATCH (a:person) RETURN a.fName, a.age ORDER BY a.fName"); + + KuzuFlatTuple row = result.getNext(); + System.out.println(row); + row.destroy(); + + row = result.getNext(); + row.destroy(); + + result.destroy(); + + + KuzuValue value = new KuzuValue(Duration.ofMillis(31800000003L)); + Duration interval = value.getValue(); + + + } +}