This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new 7b38cf454 feat(java/driver/jni): metadata methods, validation suite
(#3972)
7b38cf454 is described below
commit 7b38cf4543330592a40a5023b4e6b93f8f34d7ff
Author: Tornike Gurgenidze <[email protected]>
AuthorDate: Thu Feb 19 07:05:06 2026 +0400
feat(java/driver/jni): metadata methods, validation suite (#3972)
- adds bindings for: GetObjects, GetInfo, GetTableSchema, GetTableTypes
- adds jni-validation-sqlite suite, most of the stuff in there is still
disabled because of sqlite issues. I'll try later to add some of those
to quirks and make it work.
- fixes deferred bind bug that came up in validation suite. c driver
expects user to rebind if data changes, while java seems to allow a
caller to alter VectorSchemaRoot after it has been bound. Added
`exportBind` method in JniStatement that exports bound data to c before
each execution rather than during bind call.
---
java/driver/jni-validation-sqlite/pom.xml | 72 +++++++++
.../jni/JniSqliteConnectionMetadataTest.java | 70 +++++++++
.../adbc/driver/jni/JniSqliteConnectionTest.java} | 22 +--
.../arrow/adbc/driver/jni/JniSqliteQuirks.java} | 29 ++--
.../adbc/driver/jni/JniSqliteStatementTest.java | 61 ++++++++
.../adbc/driver/jni/JniSqliteTransactionTest.java} | 24 ++-
java/driver/jni/src/main/cpp/jni_wrapper.cc | 162 +++++++++++++++++++--
.../arrow/adbc/driver/jni/JniConnection.java | 37 ++++-
.../apache/arrow/adbc/driver/jni/JniStatement.java | 26 ++--
.../arrow/adbc/driver/jni/impl/JniLoader.java | 36 +++++
.../arrow/adbc/driver/jni/impl/NativeAdbc.java | 18 +++
.../adbc/driver/jni/impl/NativeQueryResult.java | 20 ++-
.../adbc/driver/jni/impl/NativeSchemaResult.java | 44 ++++++
.../arrow/adbc/driver/jni/JniDriverTest.java | 28 ++++
java/pom.xml | 6 +
15 files changed, 584 insertions(+), 71 deletions(-)
diff --git a/java/driver/jni-validation-sqlite/pom.xml
b/java/driver/jni-validation-sqlite/pom.xml
new file mode 100644
index 000000000..f50d8a111
--- /dev/null
+++ b/java/driver/jni-validation-sqlite/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>arrow-adbc-java-root</artifactId>
+ <version>0.23.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>adbc-driver-jni-validation-sqlite</artifactId>
+ <packaging>jar</packaging>
+ <name>Arrow ADBC Driver JNI Validation with SQLite</name>
+ <description>Tests validating the JNI driver against SQLite.</description>
+
+ <properties>
+ <maven.deploy.skip>true</maven.deploy.skip>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>adbc-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>adbc-driver-jni</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Testing -->
+ <dependency>
+ <groupId>org.apache.arrow</groupId>
+ <artifactId>arrow-memory-netty</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>adbc-driver-validation</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git
a/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteConnectionMetadataTest.java
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteConnectionMetadataTest.java
new file mode 100644
index 000000000..e805a1530
--- /dev/null
+++
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteConnectionMetadataTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.arrow.adbc.driver.jni;
+
+import org.apache.arrow.adbc.driver.testsuite.AbstractConnectionMetadataTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+
+public class JniSqliteConnectionMetadataTest extends
AbstractConnectionMetadataTest {
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new JniSqliteQuirks();
+ }
+
+ @Override
+ @Disabled("SQLite getInfo schema differs: map value type is List<Int32>
instead of Int32")
+ public void getInfo() {}
+
+ @Override
+ @Disabled("SQLite getInfo schema differs: map value type is List<Int32>
instead of Int32")
+ public void getInfoByCode() {}
+
+ @Override
+ @Disabled("SQLite does not support ALTER TABLE ... ALTER COLUMN ... SET NOT
NULL")
+ public void getObjectsConstraints() {}
+
+ @Override
+ @Disabled(
+ "SQLite getObjects schema has nullable catalog_name/db_schema_name, test
expects non-null")
+ public void getObjectsColumns() {}
+
+ @Override
+ @Disabled(
+ "SQLite getObjects schema has nullable catalog_name/db_schema_name, test
expects non-null")
+ public void getObjectsCatalogs() {}
+
+ @Override
+ @Disabled(
+ "SQLite getObjects schema has nullable catalog_name/db_schema_name, test
expects non-null")
+ public void getObjectsCatalogsPattern() {}
+
+ @Override
+ @Disabled(
+ "SQLite getObjects schema has nullable catalog_name/db_schema_name, test
expects non-null")
+ public void getObjectsDbSchemas() {}
+
+ @Override
+ @Disabled(
+ "SQLite getObjects schema has nullable catalog_name/db_schema_name, test
expects non-null")
+ public void getObjectsTables() {}
+
+ @Override
+ @Disabled("SQLite type affinity returns Int64 for all types, causing schema
mismatch")
+ public void getTableSchema() {}
+}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteConnectionTest.java
similarity index 66%
copy from
java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
copy to
java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteConnectionTest.java
index 526d615c3..e15182bca 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
+++
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteConnectionTest.java
@@ -15,22 +15,14 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jni.impl;
+package org.apache.arrow.adbc.driver.jni;
-public class NativeQueryResult {
- private final long rowsAffected;
- private final long cDataStream;
+import org.apache.arrow.adbc.driver.testsuite.AbstractConnectionTest;
+import org.junit.jupiter.api.BeforeAll;
- public NativeQueryResult(long rowsAffected, long cDataStream) {
- this.rowsAffected = rowsAffected;
- this.cDataStream = cDataStream;
- }
-
- public long rowsAffected() {
- return rowsAffected;
- }
-
- public long cDataStream() {
- return cDataStream;
+public class JniSqliteConnectionTest extends AbstractConnectionTest {
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new JniSqliteQuirks();
}
}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteQuirks.java
similarity index 53%
copy from
java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
copy to
java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteQuirks.java
index 526d615c3..6d6124d43 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
+++
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteQuirks.java
@@ -15,22 +15,25 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jni.impl;
+package org.apache.arrow.adbc.driver.jni;
-public class NativeQueryResult {
- private final long rowsAffected;
- private final long cDataStream;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.adbc.core.AdbcException;
+import org.apache.arrow.adbc.driver.testsuite.SqlValidationQuirks;
+import org.apache.arrow.memory.BufferAllocator;
- public NativeQueryResult(long rowsAffected, long cDataStream) {
- this.rowsAffected = rowsAffected;
- this.cDataStream = cDataStream;
+public class JniSqliteQuirks extends SqlValidationQuirks {
+ @Override
+ public AdbcDatabase initDatabase(BufferAllocator allocator) throws
AdbcException {
+ final Map<String, Object> parameters = new HashMap<>();
+ JniDriver.PARAM_DRIVER.set(parameters, "adbc_driver_sqlite");
+ return new JniDriver(allocator).open(parameters);
}
- public long rowsAffected() {
- return rowsAffected;
- }
-
- public long cDataStream() {
- return cDataStream;
+ @Override
+ public String defaultCatalog() {
+ return "main";
}
}
diff --git
a/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteStatementTest.java
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteStatementTest.java
new file mode 100644
index 000000000..0f09213f8
--- /dev/null
+++
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteStatementTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.arrow.adbc.driver.jni;
+
+import org.apache.arrow.adbc.driver.testsuite.AbstractStatementTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+
+class JniSqliteStatementTest extends AbstractStatementTest {
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new JniSqliteQuirks();
+ }
+
+ @Override
+ @Disabled("SQLite driver does not return expected ADBC status codes for
schema mismatch")
+ public void bulkIngestAppendConflict() {}
+
+ @Override
+ @Disabled("SQLite driver returns INTERNAL instead of NOT_FOUND")
+ public void bulkIngestAppendNotFound() {}
+
+ @Override
+ @Disabled("SQLite driver returns INTERNAL instead of ALREADY_EXISTS")
+ public void bulkIngestCreateConflict() {}
+
+ @Override
+ @Disabled("Not yet implemented in JNI driver")
+ public void executeSchema() {}
+
+ @Override
+ @Disabled("Not yet implemented in JNI driver")
+ public void executeSchemaPrepared() {}
+
+ @Override
+ @Disabled("Not yet implemented in JNI driver")
+ public void executeSchemaParams() {}
+
+ @Override
+ @Disabled("Not yet implemented in JNI driver")
+ public void getParameterSchema() {}
+
+ @Override
+ @Disabled("SQLite promotes INT to BIGINT, causing strict schema equality
check to fail")
+ public void prepareQueryWithParameters() {}
+}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteTransactionTest.java
similarity index 66%
copy from
java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
copy to
java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteTransactionTest.java
index 526d615c3..18a9850c2 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
+++
b/java/driver/jni-validation-sqlite/src/test/java/org/apache/arrow/adbc/driver/jni/JniSqliteTransactionTest.java
@@ -15,22 +15,16 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jni.impl;
+package org.apache.arrow.adbc.driver.jni;
-public class NativeQueryResult {
- private final long rowsAffected;
- private final long cDataStream;
+import org.apache.arrow.adbc.driver.testsuite.AbstractTransactionTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
- public NativeQueryResult(long rowsAffected, long cDataStream) {
- this.rowsAffected = rowsAffected;
- this.cDataStream = cDataStream;
- }
-
- public long rowsAffected() {
- return rowsAffected;
- }
-
- public long cDataStream() {
- return cDataStream;
+@Disabled("Transactions not yet implemented in JNI driver")
+public class JniSqliteTransactionTest extends AbstractTransactionTest {
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new JniSqliteQuirks();
}
}
diff --git a/java/driver/jni/src/main/cpp/jni_wrapper.cc
b/java/driver/jni/src/main/cpp/jni_wrapper.cc
index fdfea15b0..eb2564c5a 100644
--- a/java/driver/jni/src/main/cpp/jni_wrapper.cc
+++ b/java/driver/jni/src/main/cpp/jni_wrapper.cc
@@ -23,6 +23,7 @@
#include <optional>
#include <string>
#include <utility>
+#include <vector>
#include <arrow-adbc/adbc.h>
#include <arrow-adbc/adbc_driver_manager.h>
@@ -334,27 +335,34 @@
Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_closeStatement(
}
}
+jobject MakeNativeQueryResult(JNIEnv* env, jlong rows_affected,
+ struct ArrowArrayStream* out) {
+ jclass native_result_class = RequireImplClass(env, "NativeQueryResult");
+ jmethodID native_result_ctor =
+ RequireMethod(env, native_result_class, "<init>", "(JJ)V");
+ return env->NewObject(native_result_class, native_result_ctor, rows_affected,
+ static_cast<jlong>(reinterpret_cast<uintptr_t>(out)));
+}
+
+jobject MakeNativeSchemaResult(JNIEnv* env, struct ArrowSchema* schema) {
+ jclass native_result_class = RequireImplClass(env, "NativeSchemaResult");
+ jmethodID native_result_ctor =
+ RequireMethod(env, native_result_class, "<init>", "(J)V");
+ return env->NewObject(native_result_class, native_result_ctor,
+
static_cast<jlong>(reinterpret_cast<uintptr_t>(schema)));
+}
+
JNIEXPORT jobject JNICALL
Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_statementExecuteQuery(
JNIEnv* env, [[maybe_unused]] jclass self, jlong handle) {
try {
struct AdbcError error = ADBC_ERROR_INIT;
auto* ptr = reinterpret_cast<struct
AdbcStatement*>(static_cast<uintptr_t>(handle));
- auto out = std::make_unique<struct ArrowArrayStream>();
- std::memset(out.get(), 0, sizeof(struct ArrowArrayStream));
+ struct ArrowArrayStream out = {};
int64_t rows_affected = 0;
- CHECK_ADBC_ERROR(AdbcStatementExecuteQuery(ptr, out.get(), &rows_affected,
&error),
- error);
+ CHECK_ADBC_ERROR(AdbcStatementExecuteQuery(ptr, &out, &rows_affected,
&error), error);
- jclass native_result_class = RequireImplClass(env, "NativeQueryResult");
- jmethodID native_result_ctor =
- RequireMethod(env, native_result_class, "<init>", "(JJ)V");
- jobject object =
- env->NewObject(native_result_class, native_result_ctor, rows_affected,
-
static_cast<jlong>(reinterpret_cast<uintptr_t>(out.get())));
- // Don't release until after we've constructed the object
- out.release();
- return object;
+ return MakeNativeQueryResult(env, rows_affected, &out);
} catch (const AdbcException& e) {
e.ThrowJavaException(env);
}
@@ -445,4 +453,132 @@
Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_statementSetOption(
e.ThrowJavaException(env);
}
}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetObjects(
+ JNIEnv* env, [[maybe_unused]] jclass self, jlong handle, jint depth,
jstring catalog,
+ jstring db_schema, jstring table_name, jobjectArray table_types,
+ jstring column_name) {
+ try {
+ struct AdbcError error = ADBC_ERROR_INIT;
+ auto* conn = reinterpret_cast<struct
AdbcConnection*>(static_cast<uintptr_t>(handle));
+
+ // Nullable string parameters: null jstring → NULL for C API (meaning "no
filter")
+ auto catalog_str = MaybeGetJniString(env, catalog);
+ auto db_schema_str = MaybeGetJniString(env, db_schema);
+ auto table_name_str = MaybeGetJniString(env, table_name);
+ auto column_name_str = MaybeGetJniString(env, column_name);
+
+ // Convert String[] table_types to const char** (NULL-terminated) or NULL
+ std::vector<std::string> table_type_strings;
+ std::vector<const char*> table_type_ptrs;
+ const char** c_table_types = nullptr;
+ if (table_types != nullptr) {
+ jsize len = env->GetArrayLength(table_types);
+ table_type_strings.reserve(len);
+ table_type_ptrs.reserve(len + 1);
+ for (jsize i = 0; i < len; i++) {
+ auto element =
+ reinterpret_cast<jstring>(env->GetObjectArrayElement(table_types,
i));
+ table_type_strings.push_back(GetJniString(env, element));
+ table_type_ptrs.push_back(table_type_strings.back().c_str());
+ }
+ table_type_ptrs.push_back(nullptr); // NULL terminator
+ c_table_types = table_type_ptrs.data();
+ }
+
+ struct ArrowArrayStream out = {};
+
+ CHECK_ADBC_ERROR(
+ AdbcConnectionGetObjects(
+ conn, static_cast<int>(depth), catalog_str ? catalog_str->c_str()
: nullptr,
+ db_schema_str ? db_schema_str->c_str() : nullptr,
+ table_name_str ? table_name_str->c_str() : nullptr, c_table_types,
+ column_name_str ? column_name_str->c_str() : nullptr, &out,
&error),
+ error);
+
+ return MakeNativeQueryResult(env, -1, &out);
+ } catch (const AdbcException& e) {
+ e.ThrowJavaException(env);
+ }
+ return nullptr;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetInfo(
+ JNIEnv* env, [[maybe_unused]] jclass self, jlong handle, jintArray
info_codes) {
+ try {
+ struct AdbcError error = ADBC_ERROR_INIT;
+ auto* conn = reinterpret_cast<struct
AdbcConnection*>(static_cast<uintptr_t>(handle));
+
+ // Convert jintArray to uint32_t* + length (or NULL + 0 if array is null)
+ const uint32_t* c_info_codes = nullptr;
+ size_t info_codes_length = 0;
+ std::vector<uint32_t> info_codes_vec;
+ if (info_codes != nullptr) {
+ jsize len = env->GetArrayLength(info_codes);
+ info_codes_vec.resize(len);
+ env->GetIntArrayRegion(info_codes, 0, len,
+ reinterpret_cast<jint*>(info_codes_vec.data()));
+ c_info_codes = info_codes_vec.data();
+ info_codes_length = static_cast<size_t>(len);
+ }
+
+ struct ArrowArrayStream out = {};
+
+ CHECK_ADBC_ERROR(
+ AdbcConnectionGetInfo(conn, c_info_codes, info_codes_length, &out,
&error),
+ error);
+
+ return MakeNativeQueryResult(env, -1, &out);
+ } catch (const AdbcException& e) {
+ e.ThrowJavaException(env);
+ }
+ return nullptr;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetTableSchema(
+ JNIEnv* env, [[maybe_unused]] jclass self, jlong handle, jstring catalog,
+ jstring db_schema, jstring table_name) {
+ try {
+ struct AdbcError error = ADBC_ERROR_INIT;
+ auto* conn = reinterpret_cast<struct
AdbcConnection*>(static_cast<uintptr_t>(handle));
+
+ auto catalog_str = MaybeGetJniString(env, catalog);
+ auto db_schema_str = MaybeGetJniString(env, db_schema);
+ JniStringView table_name_str(env, table_name);
+
+ struct ArrowSchema schema = {};
+
+ CHECK_ADBC_ERROR(
+ AdbcConnectionGetTableSchema(conn, catalog_str ? catalog_str->c_str()
: nullptr,
+ db_schema_str ? db_schema_str->c_str() :
nullptr,
+ table_name_str.value, &schema, &error),
+ error);
+
+ return MakeNativeSchemaResult(env, &schema);
+ } catch (const AdbcException& e) {
+ e.ThrowJavaException(env);
+ }
+ return nullptr;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetTableTypes(
+ JNIEnv* env, [[maybe_unused]] jclass self, jlong handle) {
+ try {
+ struct AdbcError error = ADBC_ERROR_INIT;
+ auto* conn = reinterpret_cast<struct
AdbcConnection*>(static_cast<uintptr_t>(handle));
+
+ struct ArrowArrayStream out = {};
+
+ CHECK_ADBC_ERROR(AdbcConnectionGetTableTypes(conn, &out, &error), error);
+
+ return MakeNativeQueryResult(env, -1, &out);
+ } catch (const AdbcException& e) {
+ e.ThrowJavaException(env);
+ }
+ return nullptr;
+}
}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniConnection.java
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniConnection.java
index 7c1b9a4a6..ef214eea3 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniConnection.java
+++
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniConnection.java
@@ -26,6 +26,7 @@ import
org.apache.arrow.adbc.driver.jni.impl.NativeConnectionHandle;
import org.apache.arrow.adbc.driver.jni.impl.NativeStatementHandle;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.ipc.ArrowReader;
+import org.apache.arrow.vector.types.pojo.Schema;
import org.checkerframework.checker.nullness.qual.Nullable;
public class JniConnection implements AdbcConnection {
@@ -78,7 +79,41 @@ public class JniConnection implements AdbcConnection {
@Override
public ArrowReader getInfo(int @Nullable [] infoCodes) throws AdbcException {
- throw new UnsupportedOperationException();
+ return JniLoader.INSTANCE.connectionGetInfo(handle,
infoCodes).importStream(allocator);
+ }
+
+ @Override
+ public ArrowReader getObjects(
+ GetObjectsDepth depth,
+ String catalogPattern,
+ String dbSchemaPattern,
+ String tableNamePattern,
+ String[] tableTypes,
+ String columnNamePattern)
+ throws AdbcException {
+ return JniLoader.INSTANCE
+ .connectionGetObjects(
+ handle,
+ depth.ordinal(),
+ catalogPattern,
+ dbSchemaPattern,
+ tableNamePattern,
+ tableTypes,
+ columnNamePattern)
+ .importStream(allocator);
+ }
+
+ @Override
+ public Schema getTableSchema(String catalog, String dbSchema, String
tableName)
+ throws AdbcException {
+ return JniLoader.INSTANCE
+ .connectionGetTableSchema(handle, catalog, dbSchema, tableName)
+ .importSchema(allocator);
+ }
+
+ @Override
+ public ArrowReader getTableTypes() throws AdbcException {
+ return
JniLoader.INSTANCE.connectionGetTableTypes(handle).importStream(allocator);
}
@Override
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniStatement.java
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniStatement.java
index b496049e1..5a68fb466 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniStatement.java
+++
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniStatement.java
@@ -23,16 +23,15 @@ import org.apache.arrow.adbc.driver.jni.impl.JniLoader;
import org.apache.arrow.adbc.driver.jni.impl.NativeQueryResult;
import org.apache.arrow.adbc.driver.jni.impl.NativeStatementHandle;
import org.apache.arrow.c.ArrowArray;
-import org.apache.arrow.c.ArrowArrayStream;
import org.apache.arrow.c.ArrowSchema;
import org.apache.arrow.c.Data;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.VectorSchemaRoot;
-import org.apache.arrow.vector.ipc.ArrowReader;
public class JniStatement implements AdbcStatement {
private final BufferAllocator allocator;
private final NativeStatementHandle handle;
+ private VectorSchemaRoot bindRoot;
public JniStatement(BufferAllocator allocator, NativeStatementHandle handle)
{
this.allocator = allocator;
@@ -46,11 +45,21 @@ public class JniStatement implements AdbcStatement {
@Override
public void bind(VectorSchemaRoot root) throws AdbcException {
+ this.bindRoot = root;
+ }
+
+ // The C Data export takes ownership of the data at bind time and ignores
subsequent
+ // client changes to the bound root. Defer the export until execution so we
capture
+ // the final state of the VectorSchemaRoot.
+ private void exportBind() throws AdbcException {
+ if (bindRoot == null) {
+ return;
+ }
try (final ArrowArray batch = ArrowArray.allocateNew(allocator);
final ArrowSchema schema = ArrowSchema.allocateNew(allocator)) {
// TODO(lidavidm): we may need a way to separately provide a dictionary
provider
- Data.exportSchema(allocator, root.getSchema(), null, schema);
- Data.exportVectorSchemaRoot(allocator, root, null, batch);
+ Data.exportSchema(allocator, bindRoot.getSchema(), null, schema);
+ Data.exportVectorSchemaRoot(allocator, bindRoot, null, batch);
JniLoader.INSTANCE.statementBind(handle, batch, schema);
}
@@ -58,17 +67,14 @@ public class JniStatement implements AdbcStatement {
@Override
public QueryResult executeQuery() throws AdbcException {
+ exportBind();
NativeQueryResult result =
JniLoader.INSTANCE.statementExecuteQuery(handle);
- // TODO: need to handle result in such a way that we free it even if we
error here
- ArrowReader reader;
- try (final ArrowArrayStream cStream =
ArrowArrayStream.wrap(result.cDataStream())) {
- reader = org.apache.arrow.c.Data.importArrayStream(allocator, cStream);
- }
- return new QueryResult(result.rowsAffected(), reader);
+ return new QueryResult(result.rowsAffected(),
result.importStream(allocator));
}
@Override
public UpdateResult executeUpdate() throws AdbcException {
+ exportBind();
long rowsAffected = JniLoader.INSTANCE.statementExecuteUpdate(handle);
return new UpdateResult(rowsAffected);
}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
index db8142867..a885e2ae2 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
+++
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
@@ -118,4 +118,40 @@ public enum JniLoader {
throws AdbcException {
NativeAdbc.statementSetOption(statement.getStatementHandle(), key, value);
}
+
+ public NativeQueryResult connectionGetObjects(
+ NativeConnectionHandle connection,
+ int depth,
+ String catalog,
+ String dbSchema,
+ String tableName,
+ String[] tableTypes,
+ String columnName)
+ throws AdbcException {
+ return NativeAdbc.connectionGetObjects(
+ connection.getConnectionHandle(),
+ depth,
+ catalog,
+ dbSchema,
+ tableName,
+ tableTypes,
+ columnName);
+ }
+
+ public NativeQueryResult connectionGetInfo(NativeConnectionHandle
connection, int[] infoCodes)
+ throws AdbcException {
+ return NativeAdbc.connectionGetInfo(connection.getConnectionHandle(),
infoCodes);
+ }
+
+ public NativeSchemaResult connectionGetTableSchema(
+ NativeConnectionHandle connection, String catalog, String dbSchema,
String tableName)
+ throws AdbcException {
+ return NativeAdbc.connectionGetTableSchema(
+ connection.getConnectionHandle(), catalog, dbSchema, tableName);
+ }
+
+ public NativeQueryResult connectionGetTableTypes(NativeConnectionHandle
connection)
+ throws AdbcException {
+ return
NativeAdbc.connectionGetTableTypes(connection.getConnectionHandle());
+ }
}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeAdbc.java
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeAdbc.java
index 4e839c0b6..199662f15 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeAdbc.java
+++
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeAdbc.java
@@ -51,4 +51,22 @@ class NativeAdbc {
static native void statementPrepare(long handle) throws AdbcException;
static native void statementSetOption(long handle, String key, String value)
throws AdbcException;
+
+ static native NativeQueryResult connectionGetObjects(
+ long handle,
+ int depth,
+ String catalog,
+ String dbSchema,
+ String tableName,
+ String[] tableTypes,
+ String columnName)
+ throws AdbcException;
+
+ static native NativeQueryResult connectionGetInfo(long handle, int[]
infoCodes)
+ throws AdbcException;
+
+ static native NativeSchemaResult connectionGetTableSchema(
+ long handle, String catalog, String dbSchema, String tableName) throws
AdbcException;
+
+ static native NativeQueryResult connectionGetTableTypes(long handle) throws
AdbcException;
}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
index 526d615c3..6399dfbb3 100644
---
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
+++
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeQueryResult.java
@@ -17,20 +17,32 @@
package org.apache.arrow.adbc.driver.jni.impl;
+import org.apache.arrow.c.ArrowArrayStream;
+import org.apache.arrow.c.Data;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.ipc.ArrowReader;
+
public class NativeQueryResult {
private final long rowsAffected;
- private final long cDataStream;
+ private final ArrowArrayStream.Snapshot streamSnapshot;
public NativeQueryResult(long rowsAffected, long cDataStream) {
this.rowsAffected = rowsAffected;
- this.cDataStream = cDataStream;
+ // Immediately snapshot the stream to take ownership of the contents.
+ // The address may point to a stack-allocated struct that becomes invalid
+ // after the JNI call returns.
+ this.streamSnapshot = ArrowArrayStream.wrap(cDataStream).snapshot();
}
public long rowsAffected() {
return rowsAffected;
}
- public long cDataStream() {
- return cDataStream;
+ /** Import the C Data stream into a Java ArrowReader. */
+ public ArrowReader importStream(BufferAllocator allocator) {
+ try (final ArrowArrayStream cStream =
ArrowArrayStream.allocateNew(allocator)) {
+ cStream.save(streamSnapshot);
+ return Data.importArrayStream(allocator, cStream);
+ }
}
}
diff --git
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeSchemaResult.java
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeSchemaResult.java
new file mode 100644
index 000000000..a09d3894b
--- /dev/null
+++
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/NativeSchemaResult.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.arrow.adbc.driver.jni.impl;
+
+import org.apache.arrow.c.ArrowSchema;
+import org.apache.arrow.c.CDataDictionaryProvider;
+import org.apache.arrow.c.Data;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.types.pojo.Schema;
+
+public class NativeSchemaResult {
+ private final ArrowSchema.Snapshot schemaSnapshot;
+
+ public NativeSchemaResult(long cDataSchema) {
+ // Immediately snapshot the schema to take ownership of the contents.
+ // The address may point to a stack-allocated struct that becomes invalid
+ // after the JNI call returns.
+ this.schemaSnapshot = ArrowSchema.wrap(cDataSchema).snapshot();
+ }
+
+ /** Import the C Data schema into a Java Schema. */
+ public Schema importSchema(BufferAllocator allocator) {
+ try (final ArrowSchema cSchema = ArrowSchema.allocateNew(allocator);
+ final CDataDictionaryProvider provider = new
CDataDictionaryProvider()) {
+ cSchema.save(schemaSnapshot);
+ return Data.importSchema(allocator, cSchema, provider);
+ }
+ }
+}
diff --git
a/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/JniDriverTest.java
b/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/JniDriverTest.java
index 94fd9394e..a29f5de0b 100644
---
a/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/JniDriverTest.java
+++
b/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/JniDriverTest.java
@@ -38,6 +38,7 @@ import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.BigIntVector;
import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.ipc.ArrowReader;
import org.apache.arrow.vector.types.Types;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.Schema;
@@ -272,6 +273,33 @@ class JniDriverTest {
}
}
+ @Test
+ void getObjects() throws Exception {
+ try (final BufferAllocator allocator = new RootAllocator()) {
+ JniDriver driver = new JniDriver(allocator);
+ Map<String, Object> parameters = new HashMap<>();
+ JniDriver.PARAM_DRIVER.set(parameters, "adbc_driver_sqlite");
+
+ try (final AdbcDatabase db = driver.open(parameters);
+ final AdbcConnection conn = db.connect()) {
+ // Create a table so there's something to find
+ try (final AdbcStatement stmt = conn.createStatement()) {
+ stmt.setSqlQuery("CREATE TABLE get_objects_test (id INTEGER, name
TEXT)");
+ stmt.executeUpdate();
+ }
+
+ try (final ArrowReader reader =
+ conn.getObjects(AdbcConnection.GetObjectsDepth.ALL, null, null,
null, null, null)) {
+ assertThat(reader.loadNextBatch()).isTrue();
+ VectorSchemaRoot root = reader.getVectorSchemaRoot();
+ assertThat(root.getRowCount()).isGreaterThan(0);
+ // First field should be catalog_name
+
assertThat(root.getSchema().getFields().get(0).getName()).isEqualTo("catalog_name");
+ }
+ }
+ }
+ }
+
@Test
void bulkIngest() throws Exception {
final Schema schema =
diff --git a/java/pom.xml b/java/pom.xml
index 14b222c94..d5f0fed79 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -138,6 +138,11 @@
<artifactId>adbc-driver-validation</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>adbc-driver-jni</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
<artifactId>adbc-driver-manager</artifactId>
@@ -328,6 +333,7 @@
<id>jni</id>
<modules>
<module>driver/jni</module>
+ <module>driver/jni-validation-sqlite</module>
</modules>
</profile>
</profiles>