You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by li...@apache.org on 2022/08/02 18:38:50 UTC
[arrow-adbc] branch main updated: [C][Java][Python] Add GetInfo (#45)
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 97c032d [C][Java][Python] Add GetInfo (#45)
97c032d is described below
commit 97c032dc131b562851051087ee68d301780e9d0b
Author: David Li <li...@gmail.com>
AuthorDate: Tue Aug 2 14:38:46 2022 -0400
[C][Java][Python] Add GetInfo (#45)
---
.gitignore | 3 +
adbc.h | 57 ++++++++++
c/driver_manager/adbc_driver_manager.cc | 12 ++
c/driver_manager/adbc_driver_manager_test.cc | 45 ++++++++
c/drivers/sqlite/sqlite.cc | 116 ++++++++++++++++++++
c/drivers/sqlite/sqlite_test.cc | 45 ++++++++
.../org/apache/arrow/adbc/core/AdbcConnection.java | 14 +++
.../org/apache/arrow/adbc/core/AdbcInfoCode.java | 45 ++++++++
.../apache/arrow/adbc/core/StandardSchemas.java | 58 ++++++----
.../adbc/driver/jdbc/InfoMetadataBuilder.java | 121 +++++++++++++++++++++
.../arrow/adbc/driver/jdbc/JdbcConnection.java | 13 ++-
...dataBuilder.java => ObjectMetadataBuilder.java} | 6 +-
.../testsuite/AbstractConnectionMetadataTest.java | 36 ++++++
python/adbc_driver_manager/.gitignore | 1 +
.../adbc_driver_manager/__init__.py | 1 +
.../adbc_driver_manager/_lib.pyx | 58 +++++++++-
.../adbc_driver_manager/tests/test_lowlevel.py | 22 ++++
python/adbc_driver_manager/setup.py | 5 +-
18 files changed, 632 insertions(+), 26 deletions(-)
diff --git a/.gitignore b/.gitignore
index 0bd7eef..ad32dd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,6 +70,9 @@ docker_cache
site/
+# Python
+dist/
+
# R files
**/.Rproj.user
**/*.Rcheck/
diff --git a/adbc.h b/adbc.h
index b201a40..26f66b3 100644
--- a/adbc.h
+++ b/adbc.h
@@ -358,6 +358,61 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
///
/// @{
+/// \brief Get metadata about the database/driver.
+///
+/// The result is an Arrow dataset with the following schema:
+///
+/// Field Name | Field Type
+/// ----------------------------|------------------------
+/// info_name | uint32 not null
+/// info_value | INFO_SCHEMA
+///
+/// INFO_SCHEMA is a dense union with members:
+///
+/// Field Name (Type Code) | Field Type
+/// ----------------------------|------------------------
+/// string_value (0) | utf8
+/// bool_value (1) | bool
+/// int64_value (2) | int64
+/// int32_bitmask (3) | int32
+/// string_list (4) | list<utf8>
+/// int32_to_int32_list_map (5) | map<int32, list<int32>>
+///
+/// Each metadatum is identified by an integer code. The recognized
+/// codes are defined as constants. Codes [0, 10_000) are reserved
+/// for ADBC usage. Drivers/vendors will ignore requests for
+/// unrecognized codes (the row will be omitted from the result).
+///
+/// \param[in] connection The connection to query.
+/// \param[in] info_codes A list of metadata codes to fetch, or NULL
+/// to fetch all.
+/// \param[in] info_codes_length The length of the info_codes
+/// parameter. Ignored if info_codes is NULL.
+/// \param[out] statement The result set. AdbcStatementGetStream can
+/// be called immediately; do not call Execute or Prepare.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
+ uint32_t* info_codes, size_t info_codes_length,
+ struct AdbcStatement* statement,
+ struct AdbcError* error);
+
+/// \brief The database vendor/product name (e.g. the server name).
+/// (type: utf8).
+#define ADBC_INFO_VENDOR_NAME 0
+/// \brief The database vendor/product version (type: utf8).
+#define ADBC_INFO_VENDOR_VERSION 1
+/// \brief The database vendor/product Arrow library version (type:
+/// utf8).
+#define ADBC_INFO_VENDOR_ARROW_VERSION 2
+
+/// \brief The driver name (type: utf8).
+#define ADBC_INFO_DRIVER_NAME 100
+/// \brief The driver version (type: utf8).
+#define ADBC_INFO_DRIVER_VERSION 101
+/// \brief The driver Arrow library version (type: utf8).
+#define ADBC_INFO_DRIVER_ARROW_VERSION 102
+
/// \brief Get a hierarchical view of all catalogs, database schemas,
/// tables, and columns.
///
@@ -819,6 +874,8 @@ struct ADBC_EXPORT AdbcDriver {
AdbcStatusCode (*DatabaseInit)(struct AdbcDatabase*, struct AdbcError*);
AdbcStatusCode (*DatabaseRelease)(struct AdbcDatabase*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetInfo)(struct AdbcConnection*, uint32_t*, size_t,
+ struct AdbcStatement*, struct AdbcError*);
AdbcStatusCode (*ConnectionNew)(struct AdbcConnection*, struct AdbcError*);
AdbcStatusCode (*ConnectionSetOption)(struct AdbcConnection*, const char*, const char*,
struct AdbcError*);
diff --git a/c/driver_manager/adbc_driver_manager.cc b/c/driver_manager/adbc_driver_manager.cc
index a3f8bf9..a6c4efb 100644
--- a/c/driver_manager/adbc_driver_manager.cc
+++ b/c/driver_manager/adbc_driver_manager.cc
@@ -320,6 +320,17 @@ AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
return connection->private_driver->ConnectionCommit(connection, error);
}
+AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
+ uint32_t* info_codes, size_t info_codes_length,
+ struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ if (!connection->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ return connection->private_driver->ConnectionGetInfo(
+ connection, info_codes, info_codes_length, statement, error);
+}
+
AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int depth,
const char* catalog, const char* db_schema,
const char* table_name, const char** table_types,
@@ -681,6 +692,7 @@ AdbcStatusCode AdbcLoadDriver(const char* driver_name, const char* entrypoint,
CHECK_REQUIRED(driver, DatabaseInit);
CHECK_REQUIRED(driver, DatabaseRelease);
+ CHECK_REQUIRED(driver, ConnectionGetInfo);
CHECK_REQUIRED(driver, ConnectionNew);
CHECK_REQUIRED(driver, ConnectionInit);
CHECK_REQUIRED(driver, ConnectionRelease);
diff --git a/c/driver_manager/adbc_driver_manager_test.cc b/c/driver_manager/adbc_driver_manager_test.cc
index 2adcdf2..3f59b82 100644
--- a/c/driver_manager/adbc_driver_manager_test.cc
+++ b/c/driver_manager/adbc_driver_manager_test.cc
@@ -107,6 +107,51 @@ TEST_F(DriverManager, ConnectionInitRelease) {
ADBC_ASSERT_OK_WITH_ERROR(error, AdbcConnectionRelease(&connection, &error));
}
+TEST_F(DriverManager, MetadataGetInfo) {
+ static std::shared_ptr<arrow::Schema> kInfoSchema = arrow::schema({
+ arrow::field("info_name", arrow::uint32(), /*nullable=*/false),
+ arrow::field(
+ "info_value",
+ arrow::dense_union({
+ arrow::field("string_value", arrow::utf8()),
+ arrow::field("bool_value", arrow::boolean()),
+ arrow::field("int64_value", arrow::int64()),
+ arrow::field("int32_bitmask", arrow::int32()),
+ arrow::field("string_list", arrow::list(arrow::utf8())),
+ arrow::field("int32_to_int32_list_map",
+ arrow::map(arrow::int32(), arrow::list(arrow::int32()))),
+ })),
+ });
+
+ AdbcStatement statement;
+ std::memset(&statement, 0, sizeof(statement));
+ ADBC_ASSERT_OK_WITH_ERROR(error, AdbcStatementNew(&connection, &statement, &error));
+ ADBC_ASSERT_OK_WITH_ERROR(
+ error, AdbcConnectionGetInfo(&connection, nullptr, 0, &statement, &error));
+
+ std::shared_ptr<arrow::Schema> schema;
+ arrow::RecordBatchVector batches;
+ ReadStatement(&statement, &schema, &batches);
+ ASSERT_SCHEMA_EQ(*schema, *kInfoSchema);
+ ASSERT_EQ(1, batches.size());
+
+ std::vector<uint32_t> info = {
+ ADBC_INFO_DRIVER_NAME,
+ ADBC_INFO_DRIVER_VERSION,
+ ADBC_INFO_VENDOR_NAME,
+ ADBC_INFO_VENDOR_VERSION,
+ };
+ ADBC_ASSERT_OK_WITH_ERROR(error, AdbcStatementNew(&connection, &statement, &error));
+ ADBC_ASSERT_OK_WITH_ERROR(
+ error,
+ AdbcConnectionGetInfo(&connection, info.data(), info.size(), &statement, &error));
+ batches.clear();
+ ReadStatement(&statement, &schema, &batches);
+ ASSERT_SCHEMA_EQ(*schema, *kInfoSchema);
+ ASSERT_EQ(1, batches.size());
+ ASSERT_EQ(4, batches[0]->num_rows());
+}
+
TEST_F(DriverManager, SqlExecute) {
std::string query = "SELECT 1";
AdbcStatement statement;
diff --git a/c/drivers/sqlite/sqlite.cc b/c/drivers/sqlite/sqlite.cc
index ab2a2c7..81372d7 100644
--- a/c/drivers/sqlite/sqlite.cc
+++ b/c/drivers/sqlite/sqlite.cc
@@ -29,6 +29,7 @@
#include <arrow/record_batch.h>
#include <arrow/status.h>
#include <arrow/table.h>
+#include <arrow/util/config.h>
#include <arrow/util/logging.h>
#include <arrow/util/string_builder.h>
@@ -621,6 +622,102 @@ class SqliteStatementImpl {
return ADBC_STATUS_INVALID_STATE;
}
+ AdbcStatusCode GetInfo(const std::shared_ptr<SqliteStatementImpl>& self,
+ uint32_t* info_codes, size_t info_codes_length,
+ struct AdbcError* error) {
+ static std::shared_ptr<arrow::Schema> kInfoSchema = arrow::schema({
+ arrow::field("info_name", arrow::uint32(), /*nullable=*/false),
+ arrow::field(
+ "info_value",
+ arrow::dense_union({
+ arrow::field("string_value", arrow::utf8()),
+ arrow::field("bool_value", arrow::boolean()),
+ arrow::field("int64_value", arrow::int64()),
+ arrow::field("int32_bitmask", arrow::int32()),
+ arrow::field("string_list", arrow::list(arrow::utf8())),
+ arrow::field("int32_to_int32_list_map",
+ arrow::map(arrow::int32(), arrow::list(arrow::int32()))),
+ })),
+ });
+ static int kStringValueCode = 0;
+
+ static std::vector<uint32_t> kSupported = {
+ ADBC_INFO_VENDOR_NAME, ADBC_INFO_VENDOR_VERSION, ADBC_INFO_DRIVER_NAME,
+ ADBC_INFO_DRIVER_VERSION, ADBC_INFO_DRIVER_ARROW_VERSION,
+ };
+
+ if (!info_codes) {
+ info_codes = kSupported.data();
+ info_codes_length = kSupported.size();
+ }
+
+ arrow::UInt32Builder info_name;
+ std::unique_ptr<arrow::ArrayBuilder> info_value_builder;
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(MakeBuilder(arrow::default_memory_pool(),
+ kInfoSchema->field(1)->type(), &info_value_builder),
+ error));
+ arrow::DenseUnionBuilder* info_value =
+ static_cast<arrow::DenseUnionBuilder*>(info_value_builder.get());
+ arrow::StringBuilder* info_string =
+ static_cast<arrow::StringBuilder*>(info_value->child_builder(0).get());
+
+ for (size_t i = 0; i < info_codes_length; i++) {
+ switch (info_codes[i]) {
+ case ADBC_INFO_VENDOR_NAME:
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_name.Append(info_codes[i]), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_value->Append(kStringValueCode), error));
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_string->Append("SQLite3"), error));
+ break;
+ case ADBC_INFO_VENDOR_VERSION:
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_name.Append(info_codes[i]), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_value->Append(kStringValueCode), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_string->Append(sqlite3_libversion()), error));
+ break;
+ case ADBC_INFO_DRIVER_NAME:
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_name.Append(info_codes[i]), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_value->Append(kStringValueCode), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_string->Append("ADBC C SQLite3"), error));
+ break;
+ case ADBC_INFO_DRIVER_VERSION:
+ // TODO: set up CMake to embed version info
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_name.Append(info_codes[i]), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_value->Append(kStringValueCode), error));
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_string->Append("0.0.1"), error));
+ break;
+ case ADBC_INFO_DRIVER_ARROW_VERSION:
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_name.Append(info_codes[i]), error));
+ ADBC_RETURN_NOT_OK(
+ FromArrowStatus(info_value->Append(kStringValueCode), error));
+ ADBC_RETURN_NOT_OK(FromArrowStatus(
+ info_string->Append("Arrow/C++ " ARROW_VERSION_STRING), error));
+ break;
+ default:
+ // Unrecognized
+ break;
+ }
+ }
+
+ arrow::ArrayVector arrays(2);
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_name.Finish(&arrays[0]), error));
+ ADBC_RETURN_NOT_OK(FromArrowStatus(info_value->Finish(&arrays[1]), error));
+ const int64_t rows = arrays[0]->length();
+ auto status = arrow::RecordBatchReader::Make(
+ {
+ arrow::RecordBatch::Make(kInfoSchema, rows, std::move(arrays)),
+ },
+ kInfoSchema)
+ .Value(&result_reader_);
+ ADBC_RETURN_NOT_OK(FromArrowStatus(status, error));
+ return ADBC_STATUS_OK;
+ }
+
AdbcStatusCode GetObjects(const std::shared_ptr<SqliteStatementImpl>& self, int depth,
const char* catalog, const char* db_schema,
const char* table_name, const char** table_type,
@@ -1234,6 +1331,16 @@ AdbcStatusCode SqliteConnectionCommit(struct AdbcConnection* connection,
return (*ptr)->Commit(error);
}
+AdbcStatusCode SqliteConnectionGetInfo(struct AdbcConnection* connection,
+ uint32_t* info_codes, size_t info_codes_length,
+ struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast<std::shared_ptr<SqliteStatementImpl>*>(statement->private_data);
+ return (*ptr)->GetInfo(*ptr, info_codes, info_codes_length, error);
+}
+
AdbcStatusCode SqliteConnectionGetObjects(
struct AdbcConnection* connection, int depth, const char* catalog,
const char* db_schema, const char* table_name, const char** table_types,
@@ -1441,6 +1548,14 @@ AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
return SqliteConnectionCommit(connection, error);
}
+AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
+ uint32_t* info_codes, size_t info_codes_length,
+ struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ return SqliteConnectionGetInfo(connection, info_codes, info_codes_length, statement,
+ error);
+}
+
AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int depth,
const char* catalog, const char* db_schema,
const char* table_name, const char** table_types,
@@ -1566,6 +1681,7 @@ AdbcStatusCode AdbcSqliteDriverInit(size_t count, struct AdbcDriver* driver,
driver->DatabaseSetOption = SqliteDatabaseSetOption;
driver->ConnectionCommit = SqliteConnectionCommit;
+ driver->ConnectionGetInfo = SqliteConnectionGetInfo;
driver->ConnectionGetObjects = SqliteConnectionGetObjects;
driver->ConnectionGetTableSchema = SqliteConnectionGetTableSchema;
driver->ConnectionGetTableTypes = SqliteConnectionGetTableTypes;
diff --git a/c/drivers/sqlite/sqlite_test.cc b/c/drivers/sqlite/sqlite_test.cc
index 9f5035a..7958c64 100644
--- a/c/drivers/sqlite/sqlite_test.cc
+++ b/c/drivers/sqlite/sqlite_test.cc
@@ -421,6 +421,51 @@ TEST_F(Sqlite, MultipleConnections) {
ADBC_ASSERT_OK_WITH_ERROR(error, AdbcConnectionRelease(&connection2, &error));
}
+TEST_F(Sqlite, MetadataGetInfo) {
+ static std::shared_ptr<arrow::Schema> kInfoSchema = arrow::schema({
+ arrow::field("info_name", arrow::uint32(), /*nullable=*/false),
+ arrow::field(
+ "info_value",
+ arrow::dense_union({
+ arrow::field("string_value", arrow::utf8()),
+ arrow::field("bool_value", arrow::boolean()),
+ arrow::field("int64_value", arrow::int64()),
+ arrow::field("int32_bitmask", arrow::int32()),
+ arrow::field("string_list", arrow::list(arrow::utf8())),
+ arrow::field("int32_to_int32_list_map",
+ arrow::map(arrow::int32(), arrow::list(arrow::int32()))),
+ })),
+ });
+
+ AdbcStatement statement;
+ std::memset(&statement, 0, sizeof(statement));
+ ADBC_ASSERT_OK_WITH_ERROR(error, AdbcStatementNew(&connection, &statement, &error));
+ ADBC_ASSERT_OK_WITH_ERROR(
+ error, AdbcConnectionGetInfo(&connection, nullptr, 0, &statement, &error));
+
+ std::shared_ptr<arrow::Schema> schema;
+ arrow::RecordBatchVector batches;
+ ReadStatement(&statement, &schema, &batches);
+ ASSERT_SCHEMA_EQ(*schema, *kInfoSchema);
+ ASSERT_EQ(1, batches.size());
+
+ std::vector<uint32_t> info = {
+ ADBC_INFO_DRIVER_NAME,
+ ADBC_INFO_DRIVER_VERSION,
+ ADBC_INFO_VENDOR_NAME,
+ ADBC_INFO_VENDOR_VERSION,
+ };
+ ADBC_ASSERT_OK_WITH_ERROR(error, AdbcStatementNew(&connection, &statement, &error));
+ ADBC_ASSERT_OK_WITH_ERROR(
+ error,
+ AdbcConnectionGetInfo(&connection, info.data(), info.size(), &statement, &error));
+ batches.clear();
+ ReadStatement(&statement, &schema, &batches);
+ ASSERT_SCHEMA_EQ(*schema, *kInfoSchema);
+ ASSERT_EQ(1, batches.size());
+ ASSERT_EQ(4, batches[0]->num_rows());
+}
+
TEST_F(Sqlite, MetadataGetTableTypes) {
AdbcStatement statement;
std::memset(&statement, 0, sizeof(statement));
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java
index 00002a8..3e3fe6f 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java
+++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java
@@ -54,6 +54,20 @@ public interface AdbcConnection extends AutoCloseable {
"Connection does not support deserializePartitionDescriptor(ByteBuffer)");
}
+ AdbcStatement getInfo(int[] infoCodes) throws AdbcException;
+
+ default AdbcStatement getInfo(AdbcInfoCode[] infoCodes) throws AdbcException {
+ int[] codes = new int[infoCodes.length];
+ for (int i = 0; i < infoCodes.length; i++) {
+ codes[i] = infoCodes[i].getValue();
+ }
+ return getInfo(codes);
+ }
+
+ default AdbcStatement getInfo() throws AdbcException {
+ return getInfo((int[]) null);
+ }
+
/**
* Get a hierarchical view of all catalogs, database schemas, tables, and columns.
*
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java
new file mode 100644
index 0000000..52c0956
--- /dev/null
+++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java
@@ -0,0 +1,45 @@
+/*
+ * 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.core;
+
+/** Integer IDs used for requesting information about the database/driver. */
+public enum AdbcInfoCode {
+ /** The database vendor/product name (e.g. the server name) (type: utf8). */
+ VENDOR_NAME(0),
+ /** The database vendor/product version (type: utf8). */
+ VENDOR_VERSION(1),
+ /** The database vendor/product Arrow library version (type: utf8). */
+ VENDOR_ARROW_VERSION(2),
+
+ /** The driver name (type: utf8). */
+ DRIVER_NAME(100),
+ /** The driver version (type: utf8). */
+ DRIVER_VERSION(101),
+ /** The driver Arrow library version (type: utf8). */
+ DRIVER_ARROW_VERSION(102),
+ ;
+
+ private final int value;
+
+ AdbcInfoCode(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java b/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java
index af35cbb..a14c04c 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java
+++ b/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java
@@ -19,7 +19,7 @@ package org.apache.arrow.adbc.core;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import org.apache.arrow.vector.complex.BaseRepeatedValueVector;
+import org.apache.arrow.vector.types.UnionMode;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
@@ -32,6 +32,38 @@ public final class StandardSchemas {
private static final ArrowType INT16 = new ArrowType.Int(16, true);
private static final ArrowType INT32 = new ArrowType.Int(32, true);
+ private static final ArrowType INT64 = new ArrowType.Int(64, true);
+ private static final ArrowType UINT32 = new ArrowType.Int(32, false);
+
+ /** The schema of the result set of {@link AdbcConnection#getInfo(int[])}}. */
+ public static final Schema GET_INFO_SCHEMA =
+ new Schema(
+ Arrays.asList(
+ Field.notNullable("info_name", UINT32),
+ new Field(
+ "info_value",
+ FieldType.nullable(
+ new ArrowType.Union(UnionMode.Dense, new int[] {0, 1, 2, 3, 4, 5})),
+ Arrays.asList(
+ Field.nullable("string_value", ArrowType.Utf8.INSTANCE),
+ Field.nullable("bool_value", ArrowType.Bool.INSTANCE),
+ Field.nullable("int64_value", INT64),
+ Field.nullable("int32_bitmask", INT32),
+ new Field(
+ "string_list",
+ FieldType.nullable(ArrowType.List.INSTANCE),
+ Collections.singletonList(
+ Field.nullable("item", ArrowType.Utf8.INSTANCE))),
+ new Field(
+ "int32_to_int32_list_map",
+ FieldType.nullable(new ArrowType.Map(/*keysSorted*/ false)),
+ Collections.singletonList(
+ new Field(
+ "entries",
+ FieldType.notNullable(ArrowType.Struct.INSTANCE),
+ Arrays.asList(
+ Field.notNullable("key", INT32),
+ Field.nullable("value", INT32)))))))));
/** The schema of the result set of {@link AdbcConnection#getTableTypes()}. */
public static final Schema TABLE_TYPES_SCHEMA =
@@ -52,16 +84,12 @@ public final class StandardSchemas {
new Field(
"constraint_column_names",
FieldType.notNullable(ArrowType.List.INSTANCE),
- Collections.singletonList(
- Field.nullable(BaseRepeatedValueVector.DATA_VECTOR_NAME, new ArrowType.Utf8()))),
+ Collections.singletonList(Field.nullable("item", new ArrowType.Utf8()))),
new Field(
"constraint_column_usage",
FieldType.notNullable(ArrowType.List.INSTANCE),
Collections.singletonList(
- new Field(
- BaseRepeatedValueVector.DATA_VECTOR_NAME,
- FieldType.nullable(ArrowType.Struct.INSTANCE),
- USAGE_SCHEMA))));
+ new Field("item", FieldType.nullable(ArrowType.Struct.INSTANCE), USAGE_SCHEMA))));
public static final List<Field> COLUMN_SCHEMA =
Arrays.asList(
@@ -93,18 +121,13 @@ public final class StandardSchemas {
"table_columns",
FieldType.notNullable(ArrowType.List.INSTANCE),
Collections.singletonList(
- new Field(
- BaseRepeatedValueVector.DATA_VECTOR_NAME,
- FieldType.nullable(ArrowType.Struct.INSTANCE),
- COLUMN_SCHEMA))),
+ new Field("item", FieldType.nullable(ArrowType.Struct.INSTANCE), COLUMN_SCHEMA))),
new Field(
"table_constraints",
FieldType.notNullable(ArrowType.List.INSTANCE),
Collections.singletonList(
new Field(
- BaseRepeatedValueVector.DATA_VECTOR_NAME,
- FieldType.nullable(ArrowType.Struct.INSTANCE),
- CONSTRAINT_SCHEMA))));
+ "item", FieldType.nullable(ArrowType.Struct.INSTANCE), CONSTRAINT_SCHEMA))));
public static final List<Field> DB_SCHEMA_SCHEMA =
Arrays.asList(
@@ -113,10 +136,7 @@ public final class StandardSchemas {
"db_schema_tables",
FieldType.notNullable(ArrowType.List.INSTANCE),
Collections.singletonList(
- new Field(
- BaseRepeatedValueVector.DATA_VECTOR_NAME,
- FieldType.nullable(ArrowType.Struct.INSTANCE),
- TABLE_SCHEMA))));
+ new Field("item", FieldType.nullable(ArrowType.Struct.INSTANCE), TABLE_SCHEMA))));
public static final Schema GET_OBJECTS_SCHEMA =
new Schema(
@@ -127,7 +147,7 @@ public final class StandardSchemas {
FieldType.notNullable(ArrowType.List.INSTANCE),
Collections.singletonList(
new Field(
- BaseRepeatedValueVector.DATA_VECTOR_NAME,
+ "item",
FieldType.nullable(ArrowType.Struct.INSTANCE),
DB_SCHEMA_SCHEMA)))));
}
diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/InfoMetadataBuilder.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/InfoMetadataBuilder.java
new file mode 100644
index 0000000..02c2cca
--- /dev/null
+++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/InfoMetadataBuilder.java
@@ -0,0 +1,121 @@
+/*
+ * 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.jdbc;
+
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.apache.arrow.adbc.core.AdbcInfoCode;
+import org.apache.arrow.adbc.core.StandardSchemas;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.util.AutoCloseables;
+import org.apache.arrow.vector.UInt4Vector;
+import org.apache.arrow.vector.VarCharVector;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.complex.DenseUnionVector;
+
+/** Helper class to track state needed to build up the info structure. */
+final class InfoMetadataBuilder implements AutoCloseable {
+ private static final byte STRING_VALUE_TYPE_ID = (byte) 0;
+ private static final Map<Integer, AddInfo> SUPPORTED_CODES = new HashMap<>();
+ private final Collection<Integer> requestedCodes;
+ private final DatabaseMetaData dbmd;
+ private VectorSchemaRoot root;
+
+ final UInt4Vector infoCodes;
+ final DenseUnionVector infoValues;
+ final VarCharVector stringValues;
+
+ @FunctionalInterface
+ interface AddInfo {
+ void accept(InfoMetadataBuilder builder, int rowIndex) throws SQLException;
+ }
+
+ static {
+ SUPPORTED_CODES.put(
+ AdbcInfoCode.VENDOR_NAME.getValue(),
+ (b, idx) -> {
+ b.setStringValue(idx, b.dbmd.getDatabaseProductName());
+ });
+ SUPPORTED_CODES.put(
+ AdbcInfoCode.VENDOR_VERSION.getValue(),
+ (b, idx) -> {
+ b.setStringValue(idx, b.dbmd.getDatabaseProductVersion());
+ });
+ SUPPORTED_CODES.put(
+ AdbcInfoCode.DRIVER_NAME.getValue(),
+ (b, idx) -> {
+ final String driverName = "ADBC JDBC Driver (" + b.dbmd.getDriverName() + ")";
+ b.setStringValue(idx, driverName);
+ });
+ SUPPORTED_CODES.put(
+ AdbcInfoCode.DRIVER_VERSION.getValue(),
+ (b, idx) -> {
+ final String driverVersion = b.dbmd.getDriverVersion() + " (ADBC Driver Version 0.0.1)";
+ b.setStringValue(idx, driverVersion);
+ });
+ }
+
+ InfoMetadataBuilder(BufferAllocator allocator, Connection connection, int[] infoCodes)
+ throws SQLException {
+ this.requestedCodes =
+ infoCodes == null
+ ? SUPPORTED_CODES.keySet()
+ : IntStream.of(infoCodes).boxed().collect(Collectors.toList());
+ this.root = VectorSchemaRoot.create(StandardSchemas.GET_INFO_SCHEMA, allocator);
+ this.dbmd = connection.getMetaData();
+ this.infoCodes = (UInt4Vector) root.getVector(0);
+ this.infoValues = (DenseUnionVector) root.getVector(1);
+ this.stringValues = this.infoValues.getVarCharVector((byte) 0);
+ }
+
+ void setStringValue(int index, final String value) {
+ infoValues.setValueCount(index + 1);
+ infoValues.setTypeId(index, STRING_VALUE_TYPE_ID);
+ stringValues.setSafe(index, value.getBytes(StandardCharsets.UTF_8));
+ infoValues
+ .getOffsetBuffer()
+ .setInt((long) index * DenseUnionVector.OFFSET_WIDTH, stringValues.getLastSet());
+ }
+
+ VectorSchemaRoot build() throws SQLException {
+ int rowIndex = 0;
+ for (final Integer code : requestedCodes) {
+ final AddInfo metadata = SUPPORTED_CODES.get(code);
+ if (metadata == null) {
+ continue;
+ }
+ infoCodes.setSafe(rowIndex, code);
+ metadata.accept(this, rowIndex++);
+ }
+ root.setRowCount(rowIndex);
+ VectorSchemaRoot result = root;
+ root = null;
+ return result;
+ }
+
+ @Override
+ public void close() throws Exception {
+ AutoCloseables.close(root);
+ }
+}
diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
index 8824b12..5bbb3b0 100644
--- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
+++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
@@ -68,6 +68,17 @@ public class JdbcConnection implements AdbcConnection {
return JdbcStatement.ingestRoot(allocator, connection, quirks, targetTableName, mode);
}
+ @Override
+ public AdbcStatement getInfo(int[] infoCodes) throws AdbcException {
+ try {
+ final VectorSchemaRoot root =
+ new InfoMetadataBuilder(allocator, connection, infoCodes).build();
+ return new FixedJdbcStatement(allocator, root);
+ } catch (SQLException e) {
+ throw JdbcDriverUtil.fromSqlException(e);
+ }
+ }
+
@Override
public AdbcStatement getObjects(
final GetObjectsDepth depth,
@@ -80,7 +91,7 @@ public class JdbcConnection implements AdbcConnection {
// Build up the metadata in-memory and then return a constant reader.
try {
final VectorSchemaRoot root =
- new JdbcMetadataBuilder(
+ new ObjectMetadataBuilder(
allocator,
connection,
depth,
diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcMetadataBuilder.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/ObjectMetadataBuilder.java
similarity index 98%
rename from java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcMetadataBuilder.java
rename to java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/ObjectMetadataBuilder.java
index 1d2984b..abc7cf9 100644
--- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcMetadataBuilder.java
+++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/ObjectMetadataBuilder.java
@@ -35,8 +35,8 @@ import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.complex.ListVector;
import org.apache.arrow.vector.complex.StructVector;
-/** Helper class to track state needed to build up the metadata structure. */
-final class JdbcMetadataBuilder implements AutoCloseable {
+/** Helper class to track state needed to build up the object metadata structure. */
+final class ObjectMetadataBuilder implements AutoCloseable {
private final AdbcConnection.GetObjectsDepth depth;
private final String catalogPattern;
private final String dbSchemaPattern;
@@ -73,7 +73,7 @@ final class JdbcMetadataBuilder implements AutoCloseable {
final VarCharVector columnUsageFkTables;
final VarCharVector columnUsageFkColumns;
- JdbcMetadataBuilder(
+ ObjectMetadataBuilder(
BufferAllocator allocator,
Connection connection,
final AdbcConnection.GetObjectsDepth depth,
diff --git a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
index 6d9b3fc..ecb0f4e 100644
--- a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
+++ b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
@@ -27,6 +27,7 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.arrow.adbc.core.AdbcConnection;
import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.adbc.core.AdbcInfoCode;
import org.apache.arrow.adbc.core.AdbcStatement;
import org.apache.arrow.adbc.core.BulkIngestMode;
import org.apache.arrow.adbc.core.StandardSchemas;
@@ -35,8 +36,10 @@ import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.util.AutoCloseables;
import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.FieldVector;
+import org.apache.arrow.vector.UInt4Vector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.complex.DenseUnionVector;
import org.apache.arrow.vector.complex.ListVector;
import org.apache.arrow.vector.complex.StructVector;
import org.apache.arrow.vector.ipc.ArrowReader;
@@ -75,6 +78,39 @@ public abstract class AbstractConnectionMetadataTest {
AutoCloseables.close(connection, database, allocator);
}
+ @Test
+ void getInfo() throws Exception {
+ try (final AdbcStatement stmt = connection.getInfo()) {
+ try (final ArrowReader reader = stmt.getArrowReader()) {
+ assertThat(reader.getVectorSchemaRoot().getSchema())
+ .isEqualTo(StandardSchemas.GET_INFO_SCHEMA);
+ assertThat(reader.loadNextBatch()).isTrue();
+ assertThat(reader.getVectorSchemaRoot().getRowCount()).isGreaterThan(0);
+ }
+ }
+ }
+
+ @Test
+ void getInfoByCode() throws Exception {
+ try (final AdbcStatement stmt =
+ connection.getInfo(new AdbcInfoCode[] {AdbcInfoCode.DRIVER_NAME})) {
+ try (final ArrowReader reader = stmt.getArrowReader()) {
+ final VectorSchemaRoot root = reader.getVectorSchemaRoot();
+ assertThat(root.getSchema()).isEqualTo(StandardSchemas.GET_INFO_SCHEMA);
+ assertThat(reader.loadNextBatch()).isTrue();
+ assertThat(root.getRowCount()).isEqualTo(1);
+ assertThat(((UInt4Vector) root.getVector(0)).getObject(0))
+ .isEqualTo(AdbcInfoCode.DRIVER_NAME.getValue());
+ assertThat(
+ ((DenseUnionVector) root.getVector(1))
+ .getVarCharVector((byte) 0)
+ .getObject(0)
+ .toString())
+ .isNotEmpty();
+ }
+ }
+ }
+
@Test
void getObjectsColumns() throws Exception {
final Schema schema = util.ingestTableIntsStrs(allocator, connection, tableName);
diff --git a/python/adbc_driver_manager/.gitignore b/python/adbc_driver_manager/.gitignore
index 92bc507..d87abbf 100644
--- a/python/adbc_driver_manager/.gitignore
+++ b/python/adbc_driver_manager/.gitignore
@@ -16,4 +16,5 @@
# under the License.
adbc_driver_manager/*.c
+adbc_driver_manager/*.cpp
build/
diff --git a/python/adbc_driver_manager/adbc_driver_manager/__init__.py b/python/adbc_driver_manager/adbc_driver_manager/__init__.py
index c07a38b..4d92221 100644
--- a/python/adbc_driver_manager/adbc_driver_manager/__init__.py
+++ b/python/adbc_driver_manager/adbc_driver_manager/__init__.py
@@ -19,6 +19,7 @@ from ._lib import ( # noqa: F401
INGEST_OPTION_TARGET_TABLE,
AdbcConnection,
AdbcDatabase,
+ AdbcInfoCode,
AdbcStatement,
AdbcStatusCode,
ArrowArrayHandle,
diff --git a/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx b/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx
index 6dbcd40..97b1c9b 100644
--- a/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx
+++ b/python/adbc_driver_manager/adbc_driver_manager/_lib.pyx
@@ -24,8 +24,9 @@ import typing
from typing import List
import cython
-from libc.stdint cimport int32_t, uint8_t, uintptr_t
+from libc.stdint cimport int32_t, uint8_t, uint32_t, uintptr_t
from libc.string cimport memset
+from libcpp.vector cimport vector as c_vector
if typing.TYPE_CHECKING:
from typing import Self
@@ -70,6 +71,13 @@ cdef extern from "adbc.h" nogil:
cdef int ADBC_OBJECT_DEPTH_TABLES
cdef int ADBC_OBJECT_DEPTH_COLUMNS
+ cdef uint32_t ADBC_INFO_VENDOR_NAME
+ cdef uint32_t ADBC_INFO_VENDOR_VERSION
+ cdef uint32_t ADBC_INFO_VENDOR_ARROW_VERSION
+ cdef uint32_t ADBC_INFO_DRIVER_NAME
+ cdef uint32_t ADBC_INFO_DRIVER_VERSION
+ cdef uint32_t ADBC_INFO_DRIVER_ARROW_VERSION
+
ctypedef void (*CAdbcErrorRelease)(CAdbcError*)
cdef struct CAdbcError"AdbcError":
@@ -108,6 +116,12 @@ cdef extern from "adbc.h" nogil:
size_t serialized_length,
CAdbcStatement* statement,
CAdbcError* error)
+ CAdbcStatusCode AdbcConnectionGetInfo(
+ CAdbcConnection* connection,
+ uint32_t* info_codes,
+ size_t info_codes_length,
+ CAdbcStatement* statement,
+ CAdbcError* error)
CAdbcStatusCode AdbcConnectionGetObjects(
CAdbcConnection* connection,
int depth,
@@ -217,6 +231,15 @@ class AdbcStatusCode(enum.IntEnum):
UNAUTHORIZED = ADBC_STATUS_UNAUTHORIZED
+class AdbcInfoCode(enum.IntEnum):
+ VENDOR_NAME = ADBC_INFO_VENDOR_NAME
+ VENDOR_VERSION = ADBC_INFO_VENDOR_VERSION
+ VENDOR_ARROW_VERSION = ADBC_INFO_VENDOR_ARROW_VERSION
+ DRIVER_NAME = ADBC_INFO_DRIVER_NAME
+ DRIVER_VERSION = ADBC_INFO_DRIVER_VERSION
+ DRIVER_ARROW_VERSION = ADBC_INFO_DRIVER_ARROW_VERSION
+
+
class Error(Exception):
"""PEP-249 compliant base exception class.
@@ -474,6 +497,39 @@ cdef class AdbcConnection(_AdbcHandle):
cdef CAdbcError c_error = empty_error()
check_error(AdbcConnectionCommit(&self.connection, &c_error), &c_error)
+ def get_info(self, info_codes=None):
+ """
+ Get metadata about the database/driver.
+ """
+ cdef CAdbcError c_error = empty_error()
+ cdef CAdbcStatusCode status
+ cdef AdbcStatement statement = AdbcStatement(self)
+ cdef c_vector[uint32_t] c_info_codes
+
+ if info_codes:
+ for info_code in info_codes:
+ if isinstance(info_code, int):
+ c_info_codes.push_back(info_code)
+ else:
+ c_info_codes.push_back(info_code.value)
+
+ status = AdbcConnectionGetInfo(
+ &self.connection,
+ c_info_codes.data(),
+ c_info_codes.size(),
+ &statement.statement,
+ &c_error)
+ else:
+ status = AdbcConnectionGetInfo(
+ &self.connection,
+ NULL,
+ 0,
+ &statement.statement,
+ &c_error)
+
+ check_error(status, &c_error)
+ return statement
+
def get_objects(self, depth, catalog=None, db_schema=None, table_name=None,
table_types=None, column_name=None) -> AdbcStatement:
"""
diff --git a/python/adbc_driver_manager/adbc_driver_manager/tests/test_lowlevel.py b/python/adbc_driver_manager/adbc_driver_manager/tests/test_lowlevel.py
index 5f716e9..e112353 100644
--- a/python/adbc_driver_manager/adbc_driver_manager/tests/test_lowlevel.py
+++ b/python/adbc_driver_manager/adbc_driver_manager/tests/test_lowlevel.py
@@ -56,6 +56,28 @@ def test_database_init():
pass
+def test_connection_get_info(sqlite):
+ _, conn = sqlite
+ codes = [
+ adbc_driver_manager.AdbcInfoCode.VENDOR_NAME,
+ adbc_driver_manager.AdbcInfoCode.VENDOR_VERSION.value,
+ adbc_driver_manager.AdbcInfoCode.DRIVER_NAME,
+ adbc_driver_manager.AdbcInfoCode.DRIVER_VERSION.value,
+ ]
+ with conn.get_info() as stmt:
+ table = _import(stmt.get_stream()).read_all()
+ assert table.num_rows > 0
+ data = dict(zip(table[0].to_pylist(), table[1].to_pylist()))
+ for code in codes:
+ assert code in data
+ assert data[code]
+
+ with conn.get_info(codes) as stmt:
+ table = _import(stmt.get_stream()).read_all()
+ assert table.num_rows > 0
+ assert set(codes) == set(table[0].to_pylist())
+
+
def test_connection_get_objects(sqlite):
_, conn = sqlite
data = pyarrow.record_batch(
diff --git a/python/adbc_driver_manager/setup.py b/python/adbc_driver_manager/setup.py
index 74f1f7c..422fca8 100644
--- a/python/adbc_driver_manager/setup.py
+++ b/python/adbc_driver_manager/setup.py
@@ -25,12 +25,13 @@ setup(
ext_modules=cythonize(
Extension(
name="adbc_driver_manager._lib",
+ extra_compile_args=["-ggdb", "-Og"],
+ include_dirs=["../../", "../../c/driver_manager"],
+ language="c++",
sources=[
"adbc_driver_manager/_lib.pyx",
"../../c/driver_manager/adbc_driver_manager.cc",
],
- include_dirs=["../../", "../../c/driver_manager"],
- extra_compile_args=["-ggdb", "-Og"],
),
),
packages=["adbc_driver_manager"],