diff --git a/duckdb_java.def b/duckdb_java.def index 29ccd4720..7ff6b9388 100644 --- a/duckdb_java.def +++ b/duckdb_java.def @@ -32,6 +32,7 @@ Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1fetch_1size Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1free_1result Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1auto_1commit Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1catalog +Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1profiling_1information Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1schema Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1interrupt Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1prepare diff --git a/duckdb_java.exp b/duckdb_java.exp index 4bf7a9d48..05070358b 100644 --- a/duckdb_java.exp +++ b/duckdb_java.exp @@ -29,6 +29,7 @@ _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1fetch_1size _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1free_1result _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1auto_1commit _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1catalog +_Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1profiling_1information _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1schema _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1interrupt _Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1prepare diff --git a/duckdb_java.map b/duckdb_java.map index 7991cd98b..398b79549 100644 --- a/duckdb_java.map +++ b/duckdb_java.map @@ -31,6 +31,7 @@ DUCKDB_JAVA { Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1free_1result; Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1auto_1commit; Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1catalog; + Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1profiling_1information; Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1schema; Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1interrupt; Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1prepare; diff --git a/src/jni/duckdb_java.cpp b/src/jni/duckdb_java.cpp index e3071c92d..985ce437d 100644 --- a/src/jni/duckdb_java.cpp +++ b/src/jni/duckdb_java.cpp @@ -910,3 +910,34 @@ void _duckdb_jdbc_create_extension_type(JNIEnv *env, jclass, jobject conn_buf) { byte_test_type_type.SetAlias("byte_test_type"); ExtensionUtil::RegisterType(db_instance, "byte_test_type", byte_test_type_type); } + +static ProfilerPrintFormat GetProfilerPrintFormat(JNIEnv *env, jobject format) { + if (env->IsSameObject(format, J_ProfilerPrintFormat_QUERY_TREE)) { + return ProfilerPrintFormat::QUERY_TREE; + } + if (env->IsSameObject(format, J_ProfilerPrintFormat_JSON)) { + return ProfilerPrintFormat::JSON; + } + if (env->IsSameObject(format, J_ProfilerPrintFormat_QUERY_TREE_OPTIMIZER)) { + return ProfilerPrintFormat::QUERY_TREE_OPTIMIZER; + } + if (env->IsSameObject(format, J_ProfilerPrintFormat_NO_OUTPUT)) { + return ProfilerPrintFormat::NO_OUTPUT; + } + if (env->IsSameObject(format, J_ProfilerPrintFormat_HTML)) { + return ProfilerPrintFormat::HTML; + } + if (env->IsSameObject(format, J_ProfilerPrintFormat_GRAPHVIZ)) { + return ProfilerPrintFormat::GRAPHVIZ; + } +} + +jstring _duckdb_jdbc_get_profiling_information(JNIEnv *env, jclass, jobject conn_ref_buf, jobject j_format) { + auto connection = get_connection(env, conn_ref_buf); + if (!connection) { + throw InvalidInputException("Invalid connection"); + } + auto format = GetProfilerPrintFormat(env, j_format); + auto profiling_info = connection->GetProfilingInformation(format); + return env->NewStringUTF(profiling_info.c_str()); +} diff --git a/src/jni/functions.cpp b/src/jni/functions.cpp index e4f0826b3..4bda7b88d 100644 --- a/src/jni/functions.cpp +++ b/src/jni/functions.cpp @@ -396,3 +396,13 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1create_1extens } } + +JNIEXPORT jstring JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1profiling_1information(JNIEnv * env, jclass param0, jobject param1, jobject param2) { + try { + return _duckdb_jdbc_get_profiling_information(env, param0, param1, param2); + } catch (const std::exception &e) { + duckdb::ErrorData error(e); + ThrowJNI(env, error.Message().c_str()); + + } +} diff --git a/src/jni/functions.hpp b/src/jni/functions.hpp index 1267b3f7d..285d3ea02 100644 --- a/src/jni/functions.hpp +++ b/src/jni/functions.hpp @@ -160,3 +160,7 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1appe void _duckdb_jdbc_create_extension_type(JNIEnv * env, jclass param0, jobject param1); JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1create_1extension_1type(JNIEnv * env, jclass param0, jobject param1); + +jstring _duckdb_jdbc_get_profiling_information(JNIEnv * env, jclass param0, jobject param1, jobject param2); + +JNIEXPORT jstring JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1get_1profiling_1information(JNIEnv * env, jclass param0, jobject param1, jobject param2); diff --git a/src/jni/refs.cpp b/src/jni/refs.cpp index 3dd67c958..912eb5eb7 100644 --- a/src/jni/refs.cpp +++ b/src/jni/refs.cpp @@ -100,6 +100,14 @@ jmethodID J_Object_toString; jclass J_DuckDBTime; +jclass J_ProfilerPrintFormat; +jobject J_ProfilerPrintFormat_QUERY_TREE; +jobject J_ProfilerPrintFormat_JSON; +jobject J_ProfilerPrintFormat_QUERY_TREE_OPTIMIZER; +jobject J_ProfilerPrintFormat_NO_OUTPUT; +jobject J_ProfilerPrintFormat_HTML; +jobject J_ProfilerPrintFormat_GRAPHVIZ; + static std::vector global_refs; template @@ -252,6 +260,20 @@ void create_refs(JNIEnv *env) { J_DuckVector_varlen = get_field_id(env, J_DuckVector, "varlen_data", "[Ljava/lang/Object;"); J_ByteBuffer = make_class_ref(env, "java/nio/ByteBuffer"); + + J_ProfilerPrintFormat = make_class_ref(env, "org/duckdb/ProfilerPrintFormat"); + J_ProfilerPrintFormat_QUERY_TREE = + make_static_object_field_ref(env, J_ProfilerPrintFormat, "QUERY_TREE", "Lorg/duckdb/ProfilerPrintFormat;"); + J_ProfilerPrintFormat_JSON = + make_static_object_field_ref(env, J_ProfilerPrintFormat, "JSON", "Lorg/duckdb/ProfilerPrintFormat;"); + J_ProfilerPrintFormat_QUERY_TREE_OPTIMIZER = make_static_object_field_ref( + env, J_ProfilerPrintFormat, "QUERY_TREE_OPTIMIZER", "Lorg/duckdb/ProfilerPrintFormat;"); + J_ProfilerPrintFormat_NO_OUTPUT = + make_static_object_field_ref(env, J_ProfilerPrintFormat, "NO_OUTPUT", "Lorg/duckdb/ProfilerPrintFormat;"); + J_ProfilerPrintFormat_HTML = + make_static_object_field_ref(env, J_ProfilerPrintFormat, "HTML", "Lorg/duckdb/ProfilerPrintFormat;"); + J_ProfilerPrintFormat_GRAPHVIZ = + make_static_object_field_ref(env, J_ProfilerPrintFormat, "GRAPHVIZ", "Lorg/duckdb/ProfilerPrintFormat;"); } void delete_global_refs(JNIEnv *env) noexcept { diff --git a/src/jni/refs.hpp b/src/jni/refs.hpp index 6d317e73b..988645573 100644 --- a/src/jni/refs.hpp +++ b/src/jni/refs.hpp @@ -97,6 +97,14 @@ extern jmethodID J_Object_toString; extern jclass J_DuckDBTime; +extern jclass J_ProfilerPrintFormat; +extern jobject J_ProfilerPrintFormat_QUERY_TREE; +extern jobject J_ProfilerPrintFormat_JSON; +extern jobject J_ProfilerPrintFormat_QUERY_TREE_OPTIMIZER; +extern jobject J_ProfilerPrintFormat_NO_OUTPUT; +extern jobject J_ProfilerPrintFormat_HTML; +extern jobject J_ProfilerPrintFormat_GRAPHVIZ; + void create_refs(JNIEnv *env); void delete_global_refs(JNIEnv *env) noexcept; diff --git a/src/main/java/org/duckdb/DuckDBConnection.java b/src/main/java/org/duckdb/DuckDBConnection.java index cc41c6a49..79a37891f 100644 --- a/src/main/java/org/duckdb/DuckDBConnection.java +++ b/src/main/java/org/duckdb/DuckDBConnection.java @@ -384,4 +384,8 @@ public void registerArrowStream(String name, Object arrow_array_stream) { long array_stream_address = getArrowStreamAddress(arrow_array_stream); DuckDBNative.duckdb_jdbc_arrow_register(conn_ref, array_stream_address, name.getBytes(StandardCharsets.UTF_8)); } + + public String getProfilingInformation(ProfilerPrintFormat format) throws SQLException { + return DuckDBNative.duckdb_jdbc_get_profiling_information(conn_ref, format); + } } diff --git a/src/main/java/org/duckdb/DuckDBNative.java b/src/main/java/org/duckdb/DuckDBNative.java index d03eff588..6d4c3fb1a 100644 --- a/src/main/java/org/duckdb/DuckDBNative.java +++ b/src/main/java/org/duckdb/DuckDBNative.java @@ -167,6 +167,10 @@ protected static native void duckdb_jdbc_appender_append_decimal(ByteBuffer appe protected static native void duckdb_jdbc_create_extension_type(ByteBuffer conn_ref) throws SQLException; + protected static native String duckdb_jdbc_get_profiling_information(ByteBuffer conn_ref, + ProfilerPrintFormat format) + throws SQLException; + public static void duckdb_jdbc_create_extension_type(DuckDBConnection conn) throws SQLException { duckdb_jdbc_create_extension_type(conn.conn_ref); } diff --git a/src/main/java/org/duckdb/ProfilerPrintFormat.java b/src/main/java/org/duckdb/ProfilerPrintFormat.java new file mode 100644 index 000000000..4680af22b --- /dev/null +++ b/src/main/java/org/duckdb/ProfilerPrintFormat.java @@ -0,0 +1,11 @@ +package org.duckdb; + +public enum ProfilerPrintFormat { + + QUERY_TREE, + JSON, + QUERY_TREE_OPTIMIZER, + NO_OUTPUT, + HTML, + GRAPHVIZ +} diff --git a/src/test/java/org/duckdb/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index 07aaec586..805c0bb70 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -4876,6 +4876,16 @@ public static void test_spark_path_option_ignored() throws Exception { conn.close(); } + public static void test_get_profiling_information() throws Exception { + try (Connection conn = DriverManager.getConnection(JDBC_URL); Statement stmt = conn.createStatement()) { + stmt.execute("SET enable_profiling = 'no_output';"); + try (ResultSet rs = stmt.executeQuery("SELECT 1+1")) { + String profile = ((DuckDBConnection) conn).getProfilingInformation(ProfilerPrintFormat.JSON); + assertTrue(profile.contains("\"query_name\": \"SELECT 1+1\",")); + } + } + } + public static void main(String[] args) throws Exception { String arg1 = args.length > 0 ? args[0] : ""; final int statusCode;