This is an automated email from the ASF dual-hosted git repository.

zeroshade 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 991092a  fix(go/adbc/drivermgr): go doesn't package symbolic links 
(#709)
991092a is described below

commit 991092abdec429cfefeb21b3dfd281e4e28be894
Author: Matt Topol <[email protected]>
AuthorDate: Thu May 25 15:43:57 2023 -0400

    fix(go/adbc/drivermgr): go doesn't package symbolic links (#709)
---
 .pre-commit-config.yaml                  |    6 +
 ci/scripts/run_cgo_drivermgr_check.sh    |   45 ++
 go/adbc/drivermgr/adbc.h                 | 1223 +++++++++++++++++++++++++++++-
 go/adbc/drivermgr/adbc_driver_manager.cc |  825 +++++++++++++++++++-
 go/adbc/drivermgr/adbc_driver_manager.h  |   85 ++-
 5 files changed, 2181 insertions(+), 3 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 37b5e9c..ab6d551 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -86,5 +86,11 @@ repos:
       language: script
       pass_filenames: false
       entry: "./ci/scripts/run_rat_local.sh"
+    - id: check-cgo-adbc-header
+      name: Ensure CGO adbc.h is sync'd
+      language: script
+      pass_filenames: true
+      files: '(c/driver_manager/adbc_driver_manager\.)|(^adbc\.h)'
+      entry: "./ci/scripts/run_cgo_drivermgr_check.sh"
 
 exclude: "^c/vendor/.*"
diff --git a/ci/scripts/run_cgo_drivermgr_check.sh 
b/ci/scripts/run_cgo_drivermgr_check.sh
new file mode 100755
index 0000000..a03da98
--- /dev/null
+++ b/ci/scripts/run_cgo_drivermgr_check.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+# Ensure the headers in go/adbc/drivermgr/ match the actual headers
+# since Go doesn't package symlinks we need to have actual copies
+# of the files. So we need to make sure they stay in sync.
+
+set -e
+
+main() {
+  local -r source_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+  local -r source_top_dir="$(cd "${source_dir}/../../" && pwd)"
+
+  pushd "${source_top_dir}"
+
+  for f in "$@"; do
+    fn=$(basename $f)
+    if ! diff -q "$f" "go/adbc/drivermgr/$fn" &>/dev/null; then
+      >&2 echo "OUT OF SYNC: $f differs from go/adbc/drivermgr/$fn"
+      popd
+      return 1
+    fi
+  done
+
+  popd
+}
+
+main "$@"
diff --git a/go/adbc/drivermgr/adbc.h b/go/adbc/drivermgr/adbc.h
deleted file mode 120000
index 4ee3333..0000000
--- a/go/adbc/drivermgr/adbc.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../adbc.h
\ No newline at end of file
diff --git a/go/adbc/drivermgr/adbc.h b/go/adbc/drivermgr/adbc.h
new file mode 100644
index 0000000..154e881
--- /dev/null
+++ b/go/adbc/drivermgr/adbc.h
@@ -0,0 +1,1222 @@
+// 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.
+
+/// \file adbc.h ADBC: Arrow Database connectivity
+///
+/// An Arrow-based interface between applications and database
+/// drivers.  ADBC aims to provide a vendor-independent API for SQL
+/// and Substrait-based database access that is targeted at
+/// analytics/OLAP use cases.
+///
+/// This API is intended to be implemented directly by drivers and
+/// used directly by client applications.  To assist portability
+/// between different vendors, a "driver manager" library is also
+/// provided, which implements this same API, but dynamically loads
+/// drivers internally and forwards calls appropriately.
+///
+/// ADBC uses structs with free functions that operate on those
+/// structs to model objects.
+///
+/// In general, objects allow serialized access from multiple threads,
+/// but not concurrent access.  Specific implementations may permit
+/// multiple threads.
+///
+/// \version 1.0.0
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+/// \defgroup Arrow C Data Interface
+/// Definitions for the C Data Interface/C Stream Interface.
+///
+/// See https://arrow.apache.org/docs/format/CDataInterface.html
+///
+/// @{
+
+//! @cond Doxygen_Suppress
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Extra guard for versions of Arrow without the canonical guard
+#ifndef ARROW_FLAG_DICTIONARY_ORDERED
+
+#ifndef ARROW_C_DATA_INTERFACE
+#define ARROW_C_DATA_INTERFACE
+
+#define ARROW_FLAG_DICTIONARY_ORDERED 1
+#define ARROW_FLAG_NULLABLE 2
+#define ARROW_FLAG_MAP_KEYS_SORTED 4
+
+struct ArrowSchema {
+  // Array type description
+  const char* format;
+  const char* name;
+  const char* metadata;
+  int64_t flags;
+  int64_t n_children;
+  struct ArrowSchema** children;
+  struct ArrowSchema* dictionary;
+
+  // Release callback
+  void (*release)(struct ArrowSchema*);
+  // Opaque producer-specific data
+  void* private_data;
+};
+
+struct ArrowArray {
+  // Array data description
+  int64_t length;
+  int64_t null_count;
+  int64_t offset;
+  int64_t n_buffers;
+  int64_t n_children;
+  const void** buffers;
+  struct ArrowArray** children;
+  struct ArrowArray* dictionary;
+
+  // Release callback
+  void (*release)(struct ArrowArray*);
+  // Opaque producer-specific data
+  void* private_data;
+};
+
+#endif  // ARROW_C_DATA_INTERFACE
+
+#ifndef ARROW_C_STREAM_INTERFACE
+#define ARROW_C_STREAM_INTERFACE
+
+struct ArrowArrayStream {
+  // Callback to get the stream type
+  // (will be the same for all arrays in the stream).
+  //
+  // Return value: 0 if successful, an `errno`-compatible error code otherwise.
+  //
+  // If successful, the ArrowSchema must be released independently from the 
stream.
+  int (*get_schema)(struct ArrowArrayStream*, struct ArrowSchema* out);
+
+  // Callback to get the next array
+  // (if no error and the array is released, the stream has ended)
+  //
+  // Return value: 0 if successful, an `errno`-compatible error code otherwise.
+  //
+  // If successful, the ArrowArray must be released independently from the 
stream.
+  int (*get_next)(struct ArrowArrayStream*, struct ArrowArray* out);
+
+  // Callback to get optional detailed error information.
+  // This must only be called if the last stream operation failed
+  // with a non-0 return code.
+  //
+  // Return value: pointer to a null-terminated character array describing
+  // the last error, or NULL if no description is available.
+  //
+  // The returned pointer is only valid until the next operation on this stream
+  // (including release).
+  const char* (*get_last_error)(struct ArrowArrayStream*);
+
+  // Release callback: release the stream's own resources.
+  // Note that arrays returned by `get_next` must be individually released.
+  void (*release)(struct ArrowArrayStream*);
+
+  // Opaque producer-specific data
+  void* private_data;
+};
+
+#endif  // ARROW_C_STREAM_INTERFACE
+#endif  // ARROW_FLAG_DICTIONARY_ORDERED
+
+//! @endcond
+
+/// @}
+
+#ifndef ADBC
+#define ADBC
+
+// Storage class macros for Windows
+// Allow overriding/aliasing with application-defined macros
+#if !defined(ADBC_EXPORT)
+#if defined(_WIN32)
+#if defined(ADBC_EXPORTING)
+#define ADBC_EXPORT __declspec(dllexport)
+#else
+#define ADBC_EXPORT __declspec(dllimport)
+#endif  // defined(ADBC_EXPORTING)
+#else
+#define ADBC_EXPORT
+#endif  // defined(_WIN32)
+#endif  // !defined(ADBC_EXPORT)
+
+/// \defgroup adbc-error-handling Error Handling
+/// ADBC uses integer error codes to signal errors. To provide more
+/// detail about errors, functions may also return an AdbcError via an
+/// optional out parameter, which can be inspected. If provided, it is
+/// the responsibility of the caller to zero-initialize the AdbcError
+/// value.
+///
+/// @{
+
+/// \brief Error codes for operations that may fail.
+typedef uint8_t AdbcStatusCode;
+
+/// \brief No error.
+#define ADBC_STATUS_OK 0
+/// \brief An unknown error occurred.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_UNKNOWN 1
+/// \brief The operation is not implemented or supported.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_NOT_IMPLEMENTED 2
+/// \brief A requested resource was not found.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_NOT_FOUND 3
+/// \brief A requested resource already exists.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_ALREADY_EXISTS 4
+/// \brief The arguments are invalid, likely a programming error.
+///
+/// For instance, they may be of the wrong format, or out of range.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_INVALID_ARGUMENT 5
+/// \brief The preconditions for the operation are not met, likely a
+///   programming error.
+///
+/// For instance, the object may be uninitialized, or may have not
+/// been fully configured.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_INVALID_STATE 6
+/// \brief Invalid data was processed (not a programming error).
+///
+/// For instance, a division by zero may have occurred during query
+/// execution.
+///
+/// May indicate a database-side error only.
+#define ADBC_STATUS_INVALID_DATA 7
+/// \brief The database's integrity was affected.
+///
+/// For instance, a foreign key check may have failed, or a uniqueness
+/// constraint may have been violated.
+///
+/// May indicate a database-side error only.
+#define ADBC_STATUS_INTEGRITY 8
+/// \brief An error internal to the driver or database occurred.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_INTERNAL 9
+/// \brief An I/O error occurred.
+///
+/// For instance, a remote service may be unavailable.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_IO 10
+/// \brief The operation was cancelled, not due to a timeout.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_CANCELLED 11
+/// \brief The operation was cancelled due to a timeout.
+///
+/// May indicate a driver-side or database-side error.
+#define ADBC_STATUS_TIMEOUT 12
+/// \brief Authentication failed.
+///
+/// May indicate a database-side error only.
+#define ADBC_STATUS_UNAUTHENTICATED 13
+/// \brief The client is not authorized to perform the given operation.
+///
+/// May indicate a database-side error only.
+#define ADBC_STATUS_UNAUTHORIZED 14
+
+/// \brief A detailed error message for an operation.
+struct ADBC_EXPORT AdbcError {
+  /// \brief The error message.
+  char* message;
+
+  /// \brief A vendor-specific error code, if applicable.
+  int32_t vendor_code;
+
+  /// \brief A SQLSTATE error code, if provided, as defined by the
+  ///   SQL:2003 standard.  If not set, it should be set to
+  ///   "\0\0\0\0\0".
+  char sqlstate[5];
+
+  /// \brief Release the contained error.
+  ///
+  /// Unlike other structures, this is an embedded callback to make it
+  /// easier for the driver manager and driver to cooperate.
+  void (*release)(struct AdbcError* error);
+};
+
+/// @}
+
+/// \defgroup adbc-constants Constants
+/// @{
+
+/// \brief ADBC revision 1.0.0.
+///
+/// When passed to an AdbcDriverInitFunc(), the driver parameter must
+/// point to an AdbcDriver.
+#define ADBC_VERSION_1_0_0 1000000
+
+/// \brief Canonical option value for enabling an option.
+///
+/// For use as the value in SetOption calls.
+#define ADBC_OPTION_VALUE_ENABLED "true"
+/// \brief Canonical option value for disabling an option.
+///
+/// For use as the value in SetOption calls.
+#define ADBC_OPTION_VALUE_DISABLED "false"
+
+/// \brief The database vendor/product name (e.g. the server name).
+///   (type: utf8).
+///
+/// \see AdbcConnectionGetInfo
+#define ADBC_INFO_VENDOR_NAME 0
+/// \brief The database vendor/product version (type: utf8).
+///
+/// \see AdbcConnectionGetInfo
+#define ADBC_INFO_VENDOR_VERSION 1
+/// \brief The database vendor/product Arrow library version (type:
+///   utf8).
+///
+/// \see AdbcConnectionGetInfo
+#define ADBC_INFO_VENDOR_ARROW_VERSION 2
+
+/// \brief The driver name (type: utf8).
+///
+/// \see AdbcConnectionGetInfo
+#define ADBC_INFO_DRIVER_NAME 100
+/// \brief The driver version (type: utf8).
+///
+/// \see AdbcConnectionGetInfo
+#define ADBC_INFO_DRIVER_VERSION 101
+/// \brief The driver Arrow library version (type: utf8).
+///
+/// \see AdbcConnectionGetInfo
+#define ADBC_INFO_DRIVER_ARROW_VERSION 102
+
+/// \brief Return metadata on catalogs, schemas, tables, and columns.
+///
+/// \see AdbcConnectionGetObjects
+#define ADBC_OBJECT_DEPTH_ALL 0
+/// \brief Return metadata on catalogs only.
+///
+/// \see AdbcConnectionGetObjects
+#define ADBC_OBJECT_DEPTH_CATALOGS 1
+/// \brief Return metadata on catalogs and schemas.
+///
+/// \see AdbcConnectionGetObjects
+#define ADBC_OBJECT_DEPTH_DB_SCHEMAS 2
+/// \brief Return metadata on catalogs, schemas, and tables.
+///
+/// \see AdbcConnectionGetObjects
+#define ADBC_OBJECT_DEPTH_TABLES 3
+/// \brief Return metadata on catalogs, schemas, tables, and columns.
+///
+/// \see AdbcConnectionGetObjects
+#define ADBC_OBJECT_DEPTH_COLUMNS ADBC_OBJECT_DEPTH_ALL
+
+/// \brief The name of the canonical option for whether autocommit is
+///   enabled.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_CONNECTION_OPTION_AUTOCOMMIT "adbc.connection.autocommit"
+
+/// \brief The name of the canonical option for whether the current
+///   connection should be restricted to being read-only.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_CONNECTION_OPTION_READ_ONLY "adbc.connection.readonly"
+
+/// \brief The name of the canonical option for setting the isolation
+///   level of a transaction.
+///
+/// Should only be used in conjunction with autocommit disabled and
+/// AdbcConnectionCommit / AdbcConnectionRollback. If the desired
+/// isolation level is not supported by a driver, it should return an
+/// appropriate error.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_CONNECTION_OPTION_ISOLATION_LEVEL \
+  "adbc.connection.transaction.isolation_level"
+
+/// \brief Use database or driver default isolation level
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_DEFAULT \
+  "adbc.connection.transaction.isolation.default"
+
+/// \brief The lowest isolation level. Dirty reads are allowed, so one
+///   transaction may see not-yet-committed changes made by others.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_READ_UNCOMMITTED \
+  "adbc.connection.transaction.isolation.read_uncommitted"
+
+/// \brief Lock-based concurrency control keeps write locks until the
+///   end of the transaction, but read locks are released as soon as a
+///   SELECT is performed. Non-repeatable reads can occur in this
+///   isolation level.
+///
+/// More simply put, Read Committed is an isolation level that guarantees
+/// that any data read is committed at the moment it is read. It simply
+/// restricts the reader from seeing any intermediate, uncommitted,
+/// 'dirty' reads. It makes no promise whatsoever that if the transaction
+/// re-issues the read, it will find the same data; data is free to change
+/// after it is read.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_READ_COMMITTED \
+  "adbc.connection.transaction.isolation.read_committed"
+
+/// \brief Lock-based concurrency control keeps read AND write locks
+///   (acquired on selection data) until the end of the transaction.
+///
+/// However, range-locks are not managed, so phantom reads can occur.
+/// Write skew is possible at this isolation level in some systems.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_REPEATABLE_READ \
+  "adbc.connection.transaction.isolation.repeatable_read"
+
+/// \brief This isolation guarantees that all reads in the transaction
+///   will see a consistent snapshot of the database and the transaction
+///   should only successfully commit if no updates conflict with any
+///   concurrent updates made since that snapshot.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_SNAPSHOT \
+  "adbc.connection.transaction.isolation.snapshot"
+
+/// \brief Serializability requires read and write locks to be released
+///   only at the end of the transaction. This includes acquiring range-
+///   locks when a select query uses a ranged WHERE clause to avoid
+///   phantom reads.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_SERIALIZABLE \
+  "adbc.connection.transaction.isolation.serializable"
+
+/// \brief The central distinction between serializability and linearizability
+///   is that serializability is a global property; a property of an entire
+///   history of operations and transactions. Linearizability is a local
+///   property; a property of a single operation/transaction.
+///
+/// Linearizability can be viewed as a special case of strict serializability
+/// where transactions are restricted to consist of a single operation applied
+/// to a single object.
+///
+/// \see AdbcConnectionSetOption
+#define ADBC_OPTION_ISOLATION_LEVEL_LINEARIZABLE \
+  "adbc.connection.transaction.isolation.linearizable"
+
+/// \defgroup adbc-statement-ingestion Bulk Data Ingestion
+/// While it is possible to insert data via prepared statements, it can
+/// be more efficient to explicitly perform a bulk insert.  For
+/// compatible drivers, this can be accomplished by setting up and
+/// executing a statement.  Instead of setting a SQL query or Substrait
+/// plan, bind the source data via AdbcStatementBind, and set the name
+/// of the table to be created via AdbcStatementSetOption and the
+/// options below.  Then, call AdbcStatementExecute with a NULL for
+/// the out parameter (to indicate you do not expect a result set).
+///
+/// @{
+
+/// \brief The name of the target table for a bulk insert.
+///
+/// The driver should attempt to create the table if it does not
+/// exist.  If the table exists but has a different schema,
+/// ADBC_STATUS_ALREADY_EXISTS should be raised.  Else, data should be
+/// appended to the target table.
+#define ADBC_INGEST_OPTION_TARGET_TABLE "adbc.ingest.target_table"
+/// \brief Whether to create (the default) or append.
+#define ADBC_INGEST_OPTION_MODE "adbc.ingest.mode"
+/// \brief Create the table and insert data; error if the table exists.
+#define ADBC_INGEST_OPTION_MODE_CREATE "adbc.ingest.mode.create"
+/// \brief Do not create the table, and insert data; error if the
+///   table does not exist (ADBC_STATUS_NOT_FOUND) or does not match
+///   the schema of the data to append (ADBC_STATUS_ALREADY_EXISTS).
+#define ADBC_INGEST_OPTION_MODE_APPEND "adbc.ingest.mode.append"
+
+/// @}
+
+/// @}
+
+/// \defgroup adbc-database Database Initialization
+/// Clients first initialize a database, then create a connection
+/// (below).  This gives the implementation a place to initialize and
+/// own any common connection state.  For example, in-memory databases
+/// can place ownership of the actual database in this object.
+/// @{
+
+/// \brief An instance of a database.
+///
+/// Must be kept alive as long as any connections exist.
+struct ADBC_EXPORT AdbcDatabase {
+  /// \brief Opaque implementation-defined state.
+  /// This field is NULLPTR iff the connection is unintialized/freed.
+  void* private_data;
+  /// \brief The associated driver (used by the driver manager to help
+  ///   track state).
+  struct AdbcDriver* private_driver;
+};
+
+/// @}
+
+/// \defgroup adbc-connection Connection Establishment
+/// Functions for creating, using, and releasing database connections.
+/// @{
+
+/// \brief An active database connection.
+///
+/// Provides methods for query execution, managing prepared
+/// statements, using transactions, and so on.
+///
+/// Connections are not required to be thread-safe, but they can be
+/// used from multiple threads so long as clients take care to
+/// serialize accesses to a connection.
+struct ADBC_EXPORT AdbcConnection {
+  /// \brief Opaque implementation-defined state.
+  /// This field is NULLPTR iff the connection is unintialized/freed.
+  void* private_data;
+  /// \brief The associated driver (used by the driver manager to help
+  ///   track state).
+  struct AdbcDriver* private_driver;
+};
+
+/// @}
+
+/// \defgroup adbc-statement Managing Statements
+/// Applications should first initialize a statement with
+/// AdbcStatementNew. Then, the statement should be configured with
+/// functions like AdbcStatementSetSqlQuery and
+/// AdbcStatementSetOption. Finally, the statement can be executed
+/// with AdbcStatementExecuteQuery (or call AdbcStatementPrepare first
+/// to turn it into a prepared statement instead).
+/// @{
+
+/// \brief A container for all state needed to execute a database
+/// query, such as the query itself, parameters for prepared
+/// statements, driver parameters, etc.
+///
+/// Statements may represent queries or prepared statements.
+///
+/// Statements may be used multiple times and can be reconfigured
+/// (e.g. they can be reused to execute multiple different queries).
+/// However, executing a statement (and changing certain other state)
+/// will invalidate result sets obtained prior to that execution.
+///
+/// Multiple statements may be created from a single connection.
+/// However, the driver may block or error if they are used
+/// concurrently (whether from a single thread or multiple threads).
+///
+/// Statements are not required to be thread-safe, but they can be
+/// used from multiple threads so long as clients take care to
+/// serialize accesses to a statement.
+struct ADBC_EXPORT AdbcStatement {
+  /// \brief Opaque implementation-defined state.
+  /// This field is NULLPTR iff the connection is unintialized/freed.
+  void* private_data;
+
+  /// \brief The associated driver (used by the driver manager to help
+  ///   track state).
+  struct AdbcDriver* private_driver;
+};
+
+/// \defgroup adbc-statement-partition Partitioned Results
+/// Some backends may internally partition the results. These
+/// partitions are exposed to clients who may wish to integrate them
+/// with a threaded or distributed execution model, where partitions
+/// can be divided among threads or machines and fetched in parallel.
+///
+/// To use partitioning, execute the statement with
+/// AdbcStatementExecutePartitions to get the partition descriptors.
+/// Call AdbcConnectionReadPartition to turn the individual
+/// descriptors into ArrowArrayStream instances.  This may be done on
+/// a different connection than the one the partition was created
+/// with, or even in a different process on another machine.
+///
+/// Drivers are not required to support partitioning.
+///
+/// @{
+
+/// \brief The partitions of a distributed/partitioned result set.
+struct AdbcPartitions {
+  /// \brief The number of partitions.
+  size_t num_partitions;
+
+  /// \brief The partitions of the result set, where each entry (up to
+  ///   num_partitions entries) is an opaque identifier that can be
+  ///   passed to AdbcConnectionReadPartition.
+  const uint8_t** partitions;
+
+  /// \brief The length of each corresponding entry in partitions.
+  const size_t* partition_lengths;
+
+  /// \brief Opaque implementation-defined state.
+  /// This field is NULLPTR iff the connection is unintialized/freed.
+  void* private_data;
+
+  /// \brief Release the contained partitions.
+  ///
+  /// Unlike other structures, this is an embedded callback to make it
+  /// easier for the driver manager and driver to cooperate.
+  void (*release)(struct AdbcPartitions* partitions);
+};
+
+/// @}
+
+/// @}
+
+/// \defgroup adbc-driver Driver Initialization
+///
+/// These functions are intended to help support integration between a
+/// driver and the driver manager.
+/// @{
+
+/// \brief An instance of an initialized database driver.
+///
+/// This provides a common interface for vendor-specific driver
+/// initialization routines. Drivers should populate this struct, and
+/// applications can call ADBC functions through this struct, without
+/// worrying about multiple definitions of the same symbol.
+struct ADBC_EXPORT AdbcDriver {
+  /// \brief Opaque driver-defined state.
+  /// This field is NULL if the driver is unintialized/freed (but
+  /// it need not have a value even if the driver is initialized).
+  void* private_data;
+  /// \brief Opaque driver manager-defined state.
+  /// This field is NULL if the driver is unintialized/freed (but
+  /// it need not have a value even if the driver is initialized).
+  void* private_manager;
+
+  /// \brief Release the driver and perform any cleanup.
+  ///
+  /// This is an embedded callback to make it easier for the driver
+  /// manager and driver to cooperate.
+  AdbcStatusCode (*release)(struct AdbcDriver* driver, struct AdbcError* 
error);
+
+  AdbcStatusCode (*DatabaseInit)(struct AdbcDatabase*, struct AdbcError*);
+  AdbcStatusCode (*DatabaseNew)(struct AdbcDatabase*, struct AdbcError*);
+  AdbcStatusCode (*DatabaseSetOption)(struct AdbcDatabase*, const char*, const 
char*,
+                                      struct AdbcError*);
+  AdbcStatusCode (*DatabaseRelease)(struct AdbcDatabase*, struct AdbcError*);
+
+  AdbcStatusCode (*ConnectionCommit)(struct AdbcConnection*, struct 
AdbcError*);
+  AdbcStatusCode (*ConnectionGetInfo)(struct AdbcConnection*, uint32_t*, 
size_t,
+                                      struct ArrowArrayStream*, struct 
AdbcError*);
+  AdbcStatusCode (*ConnectionGetObjects)(struct AdbcConnection*, int, const 
char*,
+                                         const char*, const char*, const 
char**,
+                                         const char*, struct ArrowArrayStream*,
+                                         struct AdbcError*);
+  AdbcStatusCode (*ConnectionGetTableSchema)(struct AdbcConnection*, const 
char*,
+                                             const char*, const char*,
+                                             struct ArrowSchema*, struct 
AdbcError*);
+  AdbcStatusCode (*ConnectionGetTableTypes)(struct AdbcConnection*,
+                                            struct ArrowArrayStream*, struct 
AdbcError*);
+  AdbcStatusCode (*ConnectionInit)(struct AdbcConnection*, struct 
AdbcDatabase*,
+                                   struct AdbcError*);
+  AdbcStatusCode (*ConnectionNew)(struct AdbcConnection*, struct AdbcError*);
+  AdbcStatusCode (*ConnectionSetOption)(struct AdbcConnection*, const char*, 
const char*,
+                                        struct AdbcError*);
+  AdbcStatusCode (*ConnectionReadPartition)(struct AdbcConnection*, const 
uint8_t*,
+                                            size_t, struct ArrowArrayStream*,
+                                            struct AdbcError*);
+  AdbcStatusCode (*ConnectionRelease)(struct AdbcConnection*, struct 
AdbcError*);
+  AdbcStatusCode (*ConnectionRollback)(struct AdbcConnection*, struct 
AdbcError*);
+
+  AdbcStatusCode (*StatementBind)(struct AdbcStatement*, struct ArrowArray*,
+                                  struct ArrowSchema*, struct AdbcError*);
+  AdbcStatusCode (*StatementBindStream)(struct AdbcStatement*, struct 
ArrowArrayStream*,
+                                        struct AdbcError*);
+  AdbcStatusCode (*StatementExecuteQuery)(struct AdbcStatement*, struct 
ArrowArrayStream*,
+                                          int64_t*, struct AdbcError*);
+  AdbcStatusCode (*StatementExecutePartitions)(struct AdbcStatement*, struct 
ArrowSchema*,
+                                               struct AdbcPartitions*, 
int64_t*,
+                                               struct AdbcError*);
+  AdbcStatusCode (*StatementGetParameterSchema)(struct AdbcStatement*,
+                                                struct ArrowSchema*, struct 
AdbcError*);
+  AdbcStatusCode (*StatementNew)(struct AdbcConnection*, struct AdbcStatement*,
+                                 struct AdbcError*);
+  AdbcStatusCode (*StatementPrepare)(struct AdbcStatement*, struct AdbcError*);
+  AdbcStatusCode (*StatementRelease)(struct AdbcStatement*, struct AdbcError*);
+  AdbcStatusCode (*StatementSetOption)(struct AdbcStatement*, const char*, 
const char*,
+                                       struct AdbcError*);
+  AdbcStatusCode (*StatementSetSqlQuery)(struct AdbcStatement*, const char*,
+                                         struct AdbcError*);
+  AdbcStatusCode (*StatementSetSubstraitPlan)(struct AdbcStatement*, const 
uint8_t*,
+                                              size_t, struct AdbcError*);
+};
+
+/// @}
+
+/// \addtogroup adbc-database
+/// @{
+
+/// \brief Allocate a new (but uninitialized) database.
+///
+/// Callers pass in a zero-initialized AdbcDatabase.
+///
+/// Drivers should allocate their internal data structure and set the 
private_data
+/// field to point to the newly allocated struct. This struct should be 
released
+/// when AdbcDatabaseRelease is called.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct 
AdbcError* error);
+
+/// \brief Set a char* option.
+///
+/// Options may be set before AdbcDatabaseInit.  Some drivers may
+/// support setting options after initialization as well.
+///
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const 
char* key,
+                                     const char* value, struct AdbcError* 
error);
+
+/// \brief Finish setting options and initialize the database.
+///
+/// Some drivers may support setting options after initialization
+/// as well.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct 
AdbcError* error);
+
+/// \brief Destroy this database. No connections may exist.
+/// \param[in] database The database to release.
+/// \param[out] error An optional location to return an error
+///   message if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
+                                   struct AdbcError* error);
+
+/// @}
+
+/// \addtogroup adbc-connection
+/// @{
+
+/// \brief Allocate a new (but uninitialized) connection.
+///
+/// Callers pass in a zero-initialized AdbcConnection.
+///
+/// Drivers should allocate their internal data structure and set the 
private_data
+/// field to point to the newly allocated struct. This struct should be 
released
+/// when AdbcConnectionRelease is called.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionNew(struct AdbcConnection* connection,
+                                 struct AdbcError* error);
+
+/// \brief Set a char* option.
+///
+/// Options may be set before AdbcConnectionInit.  Some drivers may
+/// support setting options after initialization as well.
+///
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, 
const char* key,
+                                       const char* value, struct AdbcError* 
error);
+
+/// \brief Finish setting options and initialize the connection.
+///
+/// Some drivers may support setting options after initialization
+/// as well.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
+                                  struct AdbcDatabase* database, struct 
AdbcError* error);
+
+/// \brief Destroy this connection.
+///
+/// \param[in] connection The connection to release.
+/// \param[out] error An optional location to return an error
+///   message if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
+                                     struct AdbcError* error);
+
+/// \defgroup adbc-connection-metadata Metadata
+/// Functions for retrieving metadata about the database.
+///
+/// Generally, these functions return an ArrowArrayStream that can be
+/// consumed to get the metadata as Arrow data.  The returned metadata
+/// has an expected schema given in the function docstring. Schema
+/// fields are nullable unless otherwise marked.  While no
+/// AdbcStatement is used in these functions, the result set may count
+/// as an active statement to the driver for the purposes of
+/// concurrency management (e.g. if the driver has a limit on
+/// concurrent active statements and it must execute a SQL query
+/// internally in order to implement the metadata function).
+///
+/// Some functions accept "search pattern" arguments, which are
+/// strings that can contain the special character "%" to match zero
+/// or more characters, or "_" to match exactly one character.  (See
+/// the documentation of DatabaseMetaData in JDBC or "Pattern Value
+/// Arguments" in the ODBC documentation.)  Escaping is not currently
+/// supported.
+///
+/// @{
+
+/// \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] out The result set.
+/// \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 ArrowArrayStream* out,
+                                     struct AdbcError* error);
+
+/// \brief Get a hierarchical view of all catalogs, database schemas,
+///   tables, and columns.
+///
+/// The result is an Arrow dataset with the following schema:
+///
+/// | Field Name               | Field Type              |
+/// |--------------------------|-------------------------|
+/// | catalog_name             | utf8                    |
+/// | catalog_db_schemas       | list<DB_SCHEMA_SCHEMA>  |
+///
+/// DB_SCHEMA_SCHEMA is a Struct with fields:
+///
+/// | Field Name               | Field Type              |
+/// |--------------------------|-------------------------|
+/// | db_schema_name           | utf8                    |
+/// | db_schema_tables         | list<TABLE_SCHEMA>      |
+///
+/// TABLE_SCHEMA is a Struct with fields:
+///
+/// | Field Name               | Field Type              |
+/// |--------------------------|-------------------------|
+/// | table_name               | utf8 not null           |
+/// | table_type               | utf8 not null           |
+/// | table_columns            | list<COLUMN_SCHEMA>     |
+/// | table_constraints        | list<CONSTRAINT_SCHEMA> |
+///
+/// COLUMN_SCHEMA is a Struct with fields:
+///
+/// | Field Name               | Field Type              | Comments |
+/// |--------------------------|-------------------------|----------|
+/// | column_name              | utf8 not null           |          |
+/// | ordinal_position         | int32                   | (1)      |
+/// | remarks                  | utf8                    | (2)      |
+/// | xdbc_data_type           | int16                   | (3)      |
+/// | xdbc_type_name           | utf8                    | (3)      |
+/// | xdbc_column_size         | int32                   | (3)      |
+/// | xdbc_decimal_digits      | int16                   | (3)      |
+/// | xdbc_num_prec_radix      | int16                   | (3)      |
+/// | xdbc_nullable            | int16                   | (3)      |
+/// | xdbc_column_def          | utf8                    | (3)      |
+/// | xdbc_sql_data_type       | int16                   | (3)      |
+/// | xdbc_datetime_sub        | int16                   | (3)      |
+/// | xdbc_char_octet_length   | int32                   | (3)      |
+/// | xdbc_is_nullable         | utf8                    | (3)      |
+/// | xdbc_scope_catalog       | utf8                    | (3)      |
+/// | xdbc_scope_schema        | utf8                    | (3)      |
+/// | xdbc_scope_table         | utf8                    | (3)      |
+/// | xdbc_is_autoincrement    | bool                    | (3)      |
+/// | xdbc_is_generatedcolumn  | bool                    | (3)      |
+///
+/// 1. The column's ordinal position in the table (starting from 1).
+/// 2. Database-specific description of the column.
+/// 3. Optional value.  Should be null if not supported by the driver.
+///    xdbc_ values are meant to provide JDBC/ODBC-compatible metadata
+///    in an agnostic manner.
+///
+/// CONSTRAINT_SCHEMA is a Struct with fields:
+///
+/// | Field Name               | Field Type              | Comments |
+/// |--------------------------|-------------------------|----------|
+/// | constraint_name          | utf8                    |          |
+/// | constraint_type          | utf8 not null           | (1)      |
+/// | constraint_column_names  | list<utf8> not null     | (2)      |
+/// | constraint_column_usage  | list<USAGE_SCHEMA>      | (3)      |
+///
+/// 1. One of 'CHECK', 'FOREIGN KEY', 'PRIMARY KEY', or 'UNIQUE'.
+/// 2. The columns on the current table that are constrained, in
+///    order.
+/// 3. For FOREIGN KEY only, the referenced table and columns.
+///
+/// USAGE_SCHEMA is a Struct with fields:
+///
+/// | Field Name               | Field Type              |
+/// |--------------------------|-------------------------|
+/// | fk_catalog               | utf8                    |
+/// | fk_db_schema             | utf8                    |
+/// | fk_table                 | utf8 not null           |
+/// | fk_column_name           | utf8 not null           |
+///
+/// \param[in] connection The database connection.
+/// \param[in] depth The level of nesting to display. If 0, display
+///   all levels. If 1, display only catalogs (i.e.  catalog_schemas
+///   will be null). If 2, display only catalogs and schemas
+///   (i.e. db_schema_tables will be null), and so on.
+/// \param[in] catalog Only show tables in the given catalog. If NULL,
+///   do not filter by catalog. If an empty string, only show tables
+///   without a catalog.  May be a search pattern (see section
+///   documentation).
+/// \param[in] db_schema Only show tables in the given database schema. If
+///   NULL, do not filter by database schema. If an empty string, only show
+///   tables without a database schema. May be a search pattern (see section
+///   documentation).
+/// \param[in] table_name Only show tables with the given name. If NULL, do not
+///   filter by name. May be a search pattern (see section documentation).
+/// \param[in] table_type Only show tables matching one of the given table
+///   types. If NULL, show tables of any type. Valid table types can be fetched
+///   from GetTableTypes.  Terminate the list with a NULL entry.
+/// \param[in] column_name Only show columns with the given name. If
+///   NULL, do not filter by name.  May be a search pattern (see
+///   section documentation).
+/// \param[out] out The result set.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int 
depth,
+                                        const char* catalog, const char* 
db_schema,
+                                        const char* table_name, const char** 
table_type,
+                                        const char* column_name,
+                                        struct ArrowArrayStream* out,
+                                        struct AdbcError* error);
+
+/// \brief Get the Arrow schema of a table.
+///
+/// \param[in] connection The database connection.
+/// \param[in] catalog The catalog (or nullptr if not applicable).
+/// \param[in] db_schema The database schema (or nullptr if not applicable).
+/// \param[in] table_name The table name.
+/// \param[out] schema The table schema.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
+                                            const char* catalog, const char* 
db_schema,
+                                            const char* table_name,
+                                            struct ArrowSchema* schema,
+                                            struct AdbcError* error);
+
+/// \brief Get a list of table types in the database.
+///
+/// The result is an Arrow dataset with the following schema:
+///
+/// Field Name     | Field Type
+/// ---------------|--------------
+/// table_type     | utf8 not null
+///
+/// \param[in] connection The database connection.
+/// \param[out] out The result set.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetTableTypes(struct AdbcConnection* connection,
+                                           struct ArrowArrayStream* out,
+                                           struct AdbcError* error);
+
+/// @}
+
+/// \defgroup adbc-connection-partition Partitioned Results
+/// Some databases may internally partition the results. These
+/// partitions are exposed to clients who may wish to integrate them
+/// with a threaded or distributed execution model, where partitions
+/// can be divided among threads or machines for processing.
+///
+/// Drivers are not required to support partitioning.
+///
+/// Partitions are not ordered. If the result set is sorted,
+/// implementations should return a single partition.
+///
+/// @{
+
+/// \brief Construct a statement for a partition of a query. The
+///   results can then be read independently.
+///
+/// A partition can be retrieved from AdbcPartitions.
+///
+/// \param[in] connection The connection to use.  This does not have
+///   to be the same connection that the partition was created on.
+/// \param[in] serialized_partition The partition descriptor.
+/// \param[in] serialized_length The partition descriptor length.
+/// \param[out] out The result set.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionReadPartition(struct AdbcConnection* connection,
+                                           const uint8_t* serialized_partition,
+                                           size_t serialized_length,
+                                           struct ArrowArrayStream* out,
+                                           struct AdbcError* error);
+
+/// @}
+
+/// \defgroup adbc-connection-transaction Transaction Semantics
+///
+/// Connections start out in auto-commit mode by default (if
+/// applicable for the given vendor). Use AdbcConnectionSetOption and
+/// ADBC_CONNECTION_OPTION_AUTO_COMMIT to change this.
+///
+/// @{
+
+/// \brief Commit any pending transactions. Only used if autocommit is
+///   disabled.
+///
+/// Behavior is undefined if this is mixed with SQL transaction
+/// statements.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
+                                    struct AdbcError* error);
+
+/// \brief Roll back any pending transactions. Only used if autocommit
+///   is disabled.
+///
+/// Behavior is undefined if this is mixed with SQL transaction
+/// statements.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionRollback(struct AdbcConnection* connection,
+                                      struct AdbcError* error);
+
+/// @}
+
+/// @}
+
+/// \addtogroup adbc-statement
+/// @{
+
+/// \brief Create a new statement for a given connection.
+///
+/// Callers pass in a zero-initialized AdbcStatement.
+///
+/// Drivers should allocate their internal data structure and set the 
private_data
+/// field to point to the newly allocated struct. This struct should be 
released
+/// when AdbcStatementRelease is called.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
+                                struct AdbcStatement* statement, struct 
AdbcError* error);
+
+/// \brief Destroy a statement.
+/// \param[in] statement The statement to release.
+/// \param[out] error An optional location to return an error
+///   message if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementRelease(struct AdbcStatement* statement,
+                                    struct AdbcError* error);
+
+/// \brief Execute a statement and get the results.
+///
+/// This invalidates any prior result sets.
+///
+/// \param[in] statement The statement to execute.
+/// \param[out] out The results. Pass NULL if the client does not
+///   expect a result set.
+/// \param[out] rows_affected The number of rows affected if known,
+///   else -1. Pass NULL if the client does not want this information.
+/// \param[out] error An optional location to return an error
+///   message if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
+                                         struct ArrowArrayStream* out,
+                                         int64_t* rows_affected, struct 
AdbcError* error);
+
+/// \brief Turn this statement into a prepared statement to be
+///   executed multiple times.
+///
+/// This invalidates any prior result sets.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
+                                    struct AdbcError* error);
+
+/// \defgroup adbc-statement-sql SQL Semantics
+/// Functions for executing SQL queries, or querying SQL-related
+/// metadata. Drivers are not required to support both SQL and
+/// Substrait semantics. If they do, it may be via converting
+/// between representations internally.
+/// @{
+
+/// \brief Set the SQL query to execute.
+///
+/// The query can then be executed with AdbcStatementExecute.  For
+/// queries expected to be executed repeatedly, AdbcStatementPrepare
+/// the statement first.
+///
+/// \param[in] statement The statement.
+/// \param[in] query The query to execute.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
+                                        const char* query, struct AdbcError* 
error);
+
+/// @}
+
+/// \defgroup adbc-statement-substrait Substrait Semantics
+/// Functions for executing Substrait plans, or querying
+/// Substrait-related metadata.  Drivers are not required to support
+/// both SQL and Substrait semantics.  If they do, it may be via
+/// converting between representations internally.
+/// @{
+
+/// \brief Set the Substrait plan to execute.
+///
+/// The query can then be executed with AdbcStatementExecute.  For
+/// queries expected to be executed repeatedly, AdbcStatementPrepare
+/// the statement first.
+///
+/// \param[in] statement The statement.
+/// \param[in] plan The serialized substrait.Plan to execute.
+/// \param[in] length The length of the serialized plan.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementSetSubstraitPlan(struct AdbcStatement* statement,
+                                             const uint8_t* plan, size_t 
length,
+                                             struct AdbcError* error);
+
+/// @}
+
+/// \brief Bind Arrow data. This can be used for bulk inserts or
+///   prepared statements.
+///
+/// \param[in] statement The statement to bind to.
+/// \param[in] values The values to bind. The driver will call the
+///   release callback itself, although it may not do this until the
+///   statement is released.
+/// \param[in] schema The schema of the values to bind.
+/// \param[out] error An optional location to return an error message
+///   if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementBind(struct AdbcStatement* statement,
+                                 struct ArrowArray* values, struct 
ArrowSchema* schema,
+                                 struct AdbcError* error);
+
+/// \brief Bind Arrow data. This can be used for bulk inserts or
+///   prepared statements.
+/// \param[in] statement The statement to bind to.
+/// \param[in] stream The values to bind. The driver will call the
+///   release callback itself, although it may not do this until the
+///   statement is released.
+/// \param[out] error An optional location to return an error message
+///   if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
+                                       struct ArrowArrayStream* stream,
+                                       struct AdbcError* error);
+
+/// \brief Get the schema for bound parameters.
+///
+/// This retrieves an Arrow schema describing the number, names, and
+/// types of the parameters in a parameterized statement.  The fields
+/// of the schema should be in order of the ordinal position of the
+/// parameters; named parameters should appear only once.
+///
+/// If the parameter does not have a name, or the name cannot be
+/// determined, the name of the corresponding field in the schema will
+/// be an empty string.  If the type cannot be determined, the type of
+/// the corresponding field will be NA (NullType).
+///
+/// This should be called after AdbcStatementPrepare.
+///
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the schema cannot be determined.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
+                                               struct ArrowSchema* schema,
+                                               struct AdbcError* error);
+
+/// \brief Set a string option on a statement.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const 
char* key,
+                                      const char* value, struct AdbcError* 
error);
+
+/// \addtogroup adbc-statement-partition
+/// @{
+
+/// \brief Execute a statement and get the results as a partitioned
+///   result set.
+///
+/// \param[in] statement The statement to execute.
+/// \param[out] schema The schema of the result set.
+/// \param[out] partitions The result partitions.
+/// \param[out] rows_affected The number of rows affected if known,
+///   else -1. Pass NULL if the client does not want this information.
+/// \param[out] error An optional location to return an error
+///   message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the driver does not support
+///   partitioned results
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
+                                              struct ArrowSchema* schema,
+                                              struct AdbcPartitions* 
partitions,
+                                              int64_t* rows_affected,
+                                              struct AdbcError* error);
+
+/// @}
+
+/// @}
+
+/// \addtogroup adbc-driver
+/// @{
+
+/// \brief Common entry point for drivers via the driver manager
+///   (which uses dlopen(3)/LoadLibrary). The driver manager is told
+///   to load a library and call a function of this type to load the
+///   driver.
+///
+/// Although drivers may choose any name for this function, the
+/// recommended name is "AdbcDriverInit".
+///
+/// \param[in] version The ADBC revision to attempt to initialize (see
+///   ADBC_VERSION_1_0_0).
+/// \param[out] driver The table of function pointers to
+///   initialize. Should be a pointer to the appropriate struct for
+///   the given version (see the documentation for the version).
+/// \param[out] error An optional location to return an error message
+///   if necessary.
+/// \return ADBC_STATUS_OK if the driver was initialized, or
+///   ADBC_STATUS_NOT_IMPLEMENTED if the version is not supported.  In
+///   that case, clients may retry with a different version.
+typedef AdbcStatusCode (*AdbcDriverInitFunc)(int version, void* driver,
+                                             struct AdbcError* error);
+
+/// @}
+
+#endif  // ADBC
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/go/adbc/drivermgr/adbc_driver_manager.cc 
b/go/adbc/drivermgr/adbc_driver_manager.cc
deleted file mode 120000
index b113cb2..0000000
--- a/go/adbc/drivermgr/adbc_driver_manager.cc
+++ /dev/null
@@ -1 +0,0 @@
-../../../c/driver_manager/adbc_driver_manager.cc
\ No newline at end of file
diff --git a/go/adbc/drivermgr/adbc_driver_manager.cc 
b/go/adbc/drivermgr/adbc_driver_manager.cc
new file mode 100644
index 0000000..c63560a
--- /dev/null
+++ b/go/adbc/drivermgr/adbc_driver_manager.cc
@@ -0,0 +1,824 @@
+// 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.
+
+#include "adbc_driver_manager.h"
+#include <adbc.h>
+
+#include <algorithm>
+#include <cstring>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#if defined(_WIN32)
+#include <windows.h>  // Must come first
+
+#include <libloaderapi.h>
+#include <strsafe.h>
+#else
+#include <dlfcn.h>
+#endif  // defined(_WIN32)
+
+namespace {
+
+// Platform-specific helpers
+
+#if defined(_WIN32)
+/// Append a description of the Windows error to the buffer.
+void GetWinError(std::string* buffer) {
+  DWORD rc = GetLastError();
+  LPVOID message;
+
+  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+                    FORMAT_MESSAGE_IGNORE_INSERTS,
+                /*lpSource=*/nullptr, rc, MAKELANGID(LANG_NEUTRAL, 
SUBLANG_DEFAULT),
+                reinterpret_cast<LPSTR>(&message), /*nSize=*/0, 
/*Arguments=*/nullptr);
+
+  (*buffer) += '(';
+  (*buffer) += std::to_string(rc);
+  (*buffer) += ") ";
+  (*buffer) += reinterpret_cast<char*>(message);
+  LocalFree(message);
+}
+
+#endif  // defined(_WIN32)
+
+// Error handling
+
+void ReleaseError(struct AdbcError* error) {
+  if (error) {
+    if (error->message) delete[] error->message;
+    error->message = nullptr;
+    error->release = nullptr;
+  }
+}
+
+void SetError(struct AdbcError* error, const std::string& message) {
+  if (!error) return;
+  if (error->message) {
+    // Append
+    std::string buffer = error->message;
+    buffer.reserve(buffer.size() + message.size() + 1);
+    buffer += '\n';
+    buffer += message;
+    error->release(error);
+
+    error->message = new char[buffer.size() + 1];
+    buffer.copy(error->message, buffer.size());
+    error->message[buffer.size()] = '\0';
+  } else {
+    error->message = new char[message.size() + 1];
+    message.copy(error->message, message.size());
+    error->message[message.size()] = '\0';
+  }
+  error->release = ReleaseError;
+}
+
+// Driver state
+
+/// Hold the driver DLL and the driver release callback in the driver struct.
+struct ManagerDriverState {
+  // The original release callback
+  AdbcStatusCode (*driver_release)(struct AdbcDriver* driver, struct 
AdbcError* error);
+
+#if defined(_WIN32)
+  // The loaded DLL
+  HMODULE handle;
+#endif  // defined(_WIN32)
+};
+
+/// Unload the driver DLL.
+static AdbcStatusCode ReleaseDriver(struct AdbcDriver* driver, struct 
AdbcError* error) {
+  AdbcStatusCode status = ADBC_STATUS_OK;
+
+  if (!driver->private_manager) return status;
+  ManagerDriverState* state =
+      reinterpret_cast<ManagerDriverState*>(driver->private_manager);
+
+  if (state->driver_release) {
+    status = state->driver_release(driver, error);
+  }
+
+#if defined(_WIN32)
+  // TODO(apache/arrow-adbc#204): causes tests to segfault
+  // if (!FreeLibrary(state->handle)) {
+  //   std::string message = "FreeLibrary() failed: ";
+  //   GetWinError(&message);
+  //   SetError(error, message);
+  // }
+#endif  // defined(_WIN32)
+
+  driver->private_manager = nullptr;
+  delete state;
+  return status;
+}
+
+// Default stubs
+
+AdbcStatusCode DatabaseSetOption(struct AdbcDatabase* database, const char* 
key,
+                                 const char* value, struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionCommit(struct AdbcConnection*, struct AdbcError* 
error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionGetInfo(struct AdbcConnection* connection, uint32_t* 
info_codes,
+                                 size_t info_codes_length, struct 
ArrowArrayStream* out,
+                                 struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionGetObjects(struct AdbcConnection*, int, const char*, 
const char*,
+                                    const char*, const char**, const char*,
+                                    struct ArrowArrayStream*, struct 
AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionGetTableSchema(struct AdbcConnection*, const char*, 
const char*,
+                                        const char*, struct ArrowSchema*,
+                                        struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionGetTableTypes(struct AdbcConnection*, struct 
ArrowArrayStream*,
+                                       struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionReadPartition(struct AdbcConnection* connection,
+                                       const uint8_t* serialized_partition,
+                                       size_t serialized_length,
+                                       struct ArrowArrayStream* out,
+                                       struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionRollback(struct AdbcConnection*, struct AdbcError* 
error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionSetOption(struct AdbcConnection*, const char*, const 
char*,
+                                   struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementBind(struct AdbcStatement*, struct ArrowArray*,
+                             struct ArrowSchema*, struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementExecutePartitions(struct AdbcStatement* statement,
+                                          struct ArrowSchema* schema,
+                                          struct AdbcPartitions* partitions,
+                                          int64_t* rows_affected,
+                                          struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementGetParameterSchema(struct AdbcStatement* statement,
+                                           struct ArrowSchema* schema,
+                                           struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementPrepare(struct AdbcStatement*, struct AdbcError* 
error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementSetOption(struct AdbcStatement*, const char*, const 
char*,
+                                  struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementSetSqlQuery(struct AdbcStatement*, const char*,
+                                    struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementSetSubstraitPlan(struct AdbcStatement*, const 
uint8_t*, size_t,
+                                         struct AdbcError* error) {
+  return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+/// Temporary state while the database is being configured.
+struct TempDatabase {
+  std::unordered_map<std::string, std::string> options;
+  std::string driver;
+  // Default name (see adbc.h)
+  std::string entrypoint = "AdbcDriverInit";
+  AdbcDriverInitFunc init_func = nullptr;
+};
+
+/// Temporary state while the database is being configured.
+struct TempConnection {
+  std::unordered_map<std::string, std::string> options;
+};
+}  // namespace
+
+// Direct implementations of API methods
+
+AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct 
AdbcError* error) {
+  // Allocate a temporary structure to store options pre-Init
+  database->private_data = new TempDatabase();
+  database->private_driver = nullptr;
+  return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const 
char* key,
+                                     const char* value, struct AdbcError* 
error) {
+  if (database->private_driver) {
+    return database->private_driver->DatabaseSetOption(database, key, value, 
error);
+  }
+
+  TempDatabase* args = reinterpret_cast<TempDatabase*>(database->private_data);
+  if (std::strcmp(key, "driver") == 0) {
+    args->driver = value;
+  } else if (std::strcmp(key, "entrypoint") == 0) {
+    args->entrypoint = value;
+  } else {
+    args->options[key] = value;
+  }
+  return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDriverManagerDatabaseSetInitFunc(struct AdbcDatabase* 
database,
+                                                    AdbcDriverInitFunc 
init_func,
+                                                    struct AdbcError* error) {
+  if (database->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+
+  TempDatabase* args = reinterpret_cast<TempDatabase*>(database->private_data);
+  args->init_func = init_func;
+  return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct 
AdbcError* error) {
+  if (!database->private_data) {
+    SetError(error, "Must call AdbcDatabaseNew first");
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  TempDatabase* args = reinterpret_cast<TempDatabase*>(database->private_data);
+  if (args->init_func) {
+    // Do nothing
+  } else if (args->driver.empty()) {
+    SetError(error, "Must provide 'driver' parameter");
+    return ADBC_STATUS_INVALID_ARGUMENT;
+  }
+
+  database->private_driver = new AdbcDriver;
+  std::memset(database->private_driver, 0, sizeof(AdbcDriver));
+  AdbcStatusCode status;
+  // So we don't confuse a driver into thinking it's initialized already
+  database->private_data = nullptr;
+  if (args->init_func) {
+    status = AdbcLoadDriverFromInitFunc(args->init_func, ADBC_VERSION_1_0_0,
+                                        database->private_driver, error);
+  } else {
+    status = AdbcLoadDriver(args->driver.c_str(), args->entrypoint.c_str(),
+                            ADBC_VERSION_1_0_0, database->private_driver, 
error);
+  }
+  if (status != ADBC_STATUS_OK) {
+    // Restore private_data so it will be released by AdbcDatabaseRelease
+    database->private_data = args;
+    if (database->private_driver->release) {
+      database->private_driver->release(database->private_driver, error);
+    }
+    delete database->private_driver;
+    database->private_driver = nullptr;
+    return status;
+  }
+  status = database->private_driver->DatabaseNew(database, error);
+  if (status != ADBC_STATUS_OK) {
+    if (database->private_driver->release) {
+      database->private_driver->release(database->private_driver, error);
+    }
+    delete database->private_driver;
+    database->private_driver = nullptr;
+    return status;
+  }
+  for (const auto& option : args->options) {
+    status = database->private_driver->DatabaseSetOption(database, 
option.first.c_str(),
+                                                         
option.second.c_str(), error);
+    if (status != ADBC_STATUS_OK) {
+      delete args;
+      // Release the database
+      std::ignore = database->private_driver->DatabaseRelease(database, error);
+      if (database->private_driver->release) {
+        database->private_driver->release(database->private_driver, error);
+      }
+      delete database->private_driver;
+      database->private_driver = nullptr;
+      // Should be redundant, but ensure that AdbcDatabaseRelease
+      // below doesn't think that it contains a TempDatabase
+      database->private_data = nullptr;
+      return status;
+    }
+  }
+  delete args;
+  return database->private_driver->DatabaseInit(database, error);
+}
+
+AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
+                                   struct AdbcError* error) {
+  if (!database->private_driver) {
+    if (database->private_data) {
+      TempDatabase* args = 
reinterpret_cast<TempDatabase*>(database->private_data);
+      delete args;
+      database->private_data = nullptr;
+      return ADBC_STATUS_OK;
+    }
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  auto status = database->private_driver->DatabaseRelease(database, error);
+  if (database->private_driver->release) {
+    database->private_driver->release(database->private_driver, error);
+  }
+  delete database->private_driver;
+  database->private_data = nullptr;
+  database->private_driver = nullptr;
+  return status;
+}
+
+AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
+                                    struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionCommit(connection, error);
+}
+
+AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
+                                     uint32_t* info_codes, size_t 
info_codes_length,
+                                     struct ArrowArrayStream* out,
+                                     struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionGetInfo(connection, info_codes,
+                                                       info_codes_length, out, 
error);
+}
+
+AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int 
depth,
+                                        const char* catalog, const char* 
db_schema,
+                                        const char* table_name, const char** 
table_types,
+                                        const char* column_name,
+                                        struct ArrowArrayStream* stream,
+                                        struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionGetObjects(
+      connection, depth, catalog, db_schema, table_name, table_types, 
column_name, stream,
+      error);
+}
+
+AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
+                                            const char* catalog, const char* 
db_schema,
+                                            const char* table_name,
+                                            struct ArrowSchema* schema,
+                                            struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionGetTableSchema(
+      connection, catalog, db_schema, table_name, schema, error);
+}
+
+AdbcStatusCode AdbcConnectionGetTableTypes(struct AdbcConnection* connection,
+                                           struct ArrowArrayStream* stream,
+                                           struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionGetTableTypes(connection, 
stream, error);
+}
+
+AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
+                                  struct AdbcDatabase* database,
+                                  struct AdbcError* error) {
+  if (!connection->private_data) {
+    SetError(error, "Must call AdbcConnectionNew first");
+    return ADBC_STATUS_INVALID_STATE;
+  } else if (!database->private_driver) {
+    SetError(error, "Database is not initialized");
+    return ADBC_STATUS_INVALID_ARGUMENT;
+  }
+  TempConnection* args = 
reinterpret_cast<TempConnection*>(connection->private_data);
+  connection->private_data = nullptr;
+  std::unordered_map<std::string, std::string> options = 
std::move(args->options);
+  delete args;
+
+  auto status = database->private_driver->ConnectionNew(connection, error);
+  if (status != ADBC_STATUS_OK) return status;
+  connection->private_driver = database->private_driver;
+
+  for (const auto& option : options) {
+    status = database->private_driver->ConnectionSetOption(
+        connection, option.first.c_str(), option.second.c_str(), error);
+    if (status != ADBC_STATUS_OK) return status;
+  }
+  return connection->private_driver->ConnectionInit(connection, database, 
error);
+}
+
+AdbcStatusCode AdbcConnectionNew(struct AdbcConnection* connection,
+                                 struct AdbcError* error) {
+  // Allocate a temporary structure to store options pre-Init, because
+  // we don't get access to the database (and hence the driver
+  // function table) until then
+  connection->private_data = new TempConnection;
+  connection->private_driver = nullptr;
+  return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcConnectionReadPartition(struct AdbcConnection* connection,
+                                           const uint8_t* serialized_partition,
+                                           size_t serialized_length,
+                                           struct ArrowArrayStream* out,
+                                           struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionReadPartition(
+      connection, serialized_partition, serialized_length, out, error);
+}
+
+AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
+                                     struct AdbcError* error) {
+  if (!connection->private_driver) {
+    if (connection->private_data) {
+      TempConnection* args = 
reinterpret_cast<TempConnection*>(connection->private_data);
+      delete args;
+      connection->private_data = nullptr;
+      return ADBC_STATUS_OK;
+    }
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  auto status = connection->private_driver->ConnectionRelease(connection, 
error);
+  connection->private_driver = nullptr;
+  return status;
+}
+
+AdbcStatusCode AdbcConnectionRollback(struct AdbcConnection* connection,
+                                      struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return connection->private_driver->ConnectionRollback(connection, error);
+}
+
+AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, 
const char* key,
+                                       const char* value, struct AdbcError* 
error) {
+  if (!connection->private_data) {
+    SetError(error, "AdbcConnectionSetOption: must AdbcConnectionNew first");
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  if (!connection->private_driver) {
+    // Init not yet called, save the option
+    TempConnection* args = 
reinterpret_cast<TempConnection*>(connection->private_data);
+    args->options[key] = value;
+    return ADBC_STATUS_OK;
+  }
+  return connection->private_driver->ConnectionSetOption(connection, key, 
value, error);
+}
+
+AdbcStatusCode AdbcStatementBind(struct AdbcStatement* statement,
+                                 struct ArrowArray* values, struct 
ArrowSchema* schema,
+                                 struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementBind(statement, values, schema, 
error);
+}
+
+AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
+                                       struct ArrowArrayStream* stream,
+                                       struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementBindStream(statement, stream, 
error);
+}
+
+// XXX: cpplint gets confused here if declared as 'struct ArrowSchema* schema'
+AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
+                                              ArrowSchema* schema,
+                                              struct AdbcPartitions* 
partitions,
+                                              int64_t* rows_affected,
+                                              struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementExecutePartitions(
+      statement, schema, partitions, rows_affected, error);
+}
+
+AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
+                                         struct ArrowArrayStream* out,
+                                         int64_t* rows_affected,
+                                         struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementExecuteQuery(statement, out, 
rows_affected,
+                                                          error);
+}
+
+AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
+                                               struct ArrowSchema* schema,
+                                               struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementGetParameterSchema(statement, 
schema, error);
+}
+
+AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
+                                struct AdbcStatement* statement,
+                                struct AdbcError* error) {
+  if (!connection->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  auto status = connection->private_driver->StatementNew(connection, 
statement, error);
+  statement->private_driver = connection->private_driver;
+  return status;
+}
+
+AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
+                                    struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementPrepare(statement, error);
+}
+
+AdbcStatusCode AdbcStatementRelease(struct AdbcStatement* statement,
+                                    struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  auto status = statement->private_driver->StatementRelease(statement, error);
+  statement->private_driver = nullptr;
+  return status;
+}
+
+AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const 
char* key,
+                                      const char* value, struct AdbcError* 
error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementSetOption(statement, key, value, 
error);
+}
+
+AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
+                                        const char* query, struct AdbcError* 
error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementSetSqlQuery(statement, query, 
error);
+}
+
+AdbcStatusCode AdbcStatementSetSubstraitPlan(struct AdbcStatement* statement,
+                                             const uint8_t* plan, size_t 
length,
+                                             struct AdbcError* error) {
+  if (!statement->private_driver) {
+    return ADBC_STATUS_INVALID_STATE;
+  }
+  return statement->private_driver->StatementSetSubstraitPlan(statement, plan, 
length,
+                                                              error);
+}
+
+const char* AdbcStatusCodeMessage(AdbcStatusCode code) {
+#define STRINGIFY(s) #s
+#define STRINGIFY_VALUE(s) STRINGIFY(s)
+#define CASE(CONSTANT) \
+  case CONSTANT:       \
+    return #CONSTANT " (" STRINGIFY_VALUE(CONSTANT) ")";
+
+  switch (code) {
+    CASE(ADBC_STATUS_OK);
+    CASE(ADBC_STATUS_UNKNOWN);
+    CASE(ADBC_STATUS_NOT_IMPLEMENTED);
+    CASE(ADBC_STATUS_NOT_FOUND);
+    CASE(ADBC_STATUS_ALREADY_EXISTS);
+    CASE(ADBC_STATUS_INVALID_ARGUMENT);
+    CASE(ADBC_STATUS_INVALID_STATE);
+    CASE(ADBC_STATUS_INVALID_DATA);
+    CASE(ADBC_STATUS_INTEGRITY);
+    CASE(ADBC_STATUS_INTERNAL);
+    CASE(ADBC_STATUS_IO);
+    CASE(ADBC_STATUS_CANCELLED);
+    CASE(ADBC_STATUS_TIMEOUT);
+    CASE(ADBC_STATUS_UNAUTHENTICATED);
+    CASE(ADBC_STATUS_UNAUTHORIZED);
+    default:
+      return "(invalid code)";
+  }
+#undef CASE
+#undef STRINGIFY_VALUE
+#undef STRINGIFY
+}
+
+AdbcStatusCode AdbcLoadDriver(const char* driver_name, const char* entrypoint,
+                              int version, void* raw_driver, struct AdbcError* 
error) {
+  AdbcDriverInitFunc init_func;
+  std::string error_message;
+
+  if (version != ADBC_VERSION_1_0_0) {
+    SetError(error, "Only ADBC 1.0.0 is supported");
+    return ADBC_STATUS_NOT_IMPLEMENTED;
+  }
+
+  auto* driver = reinterpret_cast<struct AdbcDriver*>(raw_driver);
+
+  if (!entrypoint) {
+    // Default entrypoint (see adbc.h)
+    entrypoint = "AdbcDriverInit";
+  }
+
+#if defined(_WIN32)
+
+  HMODULE handle = LoadLibraryExA(driver_name, NULL, 0);
+  if (!handle) {
+    error_message += driver_name;
+    error_message += ": LoadLibraryExA() failed: ";
+    GetWinError(&error_message);
+
+    std::string full_driver_name = driver_name;
+    full_driver_name += ".lib";
+    handle = LoadLibraryExA(full_driver_name.c_str(), NULL, 0);
+    if (!handle) {
+      error_message += '\n';
+      error_message += full_driver_name;
+      error_message += ": LoadLibraryExA() failed: ";
+      GetWinError(&error_message);
+    }
+  }
+  if (!handle) {
+    SetError(error, error_message);
+    return ADBC_STATUS_INTERNAL;
+  }
+
+  void* load_handle = reinterpret_cast<void*>(GetProcAddress(handle, 
entrypoint));
+  init_func = reinterpret_cast<AdbcDriverInitFunc>(load_handle);
+  if (!init_func) {
+    std::string message = "GetProcAddress(";
+    message += entrypoint;
+    message += ") failed: ";
+    GetWinError(&message);
+    if (!FreeLibrary(handle)) {
+      message += "\nFreeLibrary() failed: ";
+      GetWinError(&message);
+    }
+    SetError(error, message);
+    return ADBC_STATUS_INTERNAL;
+  }
+
+#else
+
+#if defined(__APPLE__)
+  static const std::string kPlatformLibraryPrefix = "lib";
+  static const std::string kPlatformLibrarySuffix = ".dylib";
+#else
+  static const std::string kPlatformLibraryPrefix = "lib";
+  static const std::string kPlatformLibrarySuffix = ".so";
+#endif  // defined(__APPLE__)
+
+  void* handle = dlopen(driver_name, RTLD_NOW | RTLD_LOCAL);
+  if (!handle) {
+    error_message = "dlopen() failed: ";
+    error_message += dlerror();
+
+    // If applicable, append the shared library prefix/extension and
+    // try again (this way you don't have to hardcode driver names by
+    // platform in the application)
+    const std::string driver_str = driver_name;
+
+    std::string full_driver_name;
+    if (driver_str.size() < kPlatformLibraryPrefix.size() ||
+        driver_str.compare(0, kPlatformLibraryPrefix.size(), 
kPlatformLibraryPrefix) !=
+            0) {
+      full_driver_name += kPlatformLibraryPrefix;
+    }
+    full_driver_name += driver_name;
+    if (driver_str.size() < kPlatformLibrarySuffix.size() ||
+        driver_str.compare(full_driver_name.size() - 
kPlatformLibrarySuffix.size(),
+                           kPlatformLibrarySuffix.size(), 
kPlatformLibrarySuffix) != 0) {
+      full_driver_name += kPlatformLibrarySuffix;
+    }
+    handle = dlopen(full_driver_name.c_str(), RTLD_NOW | RTLD_LOCAL);
+    if (!handle) {
+      error_message += "\ndlopen() failed: ";
+      error_message += dlerror();
+    }
+  }
+  if (!handle) {
+    SetError(error, error_message);
+    // AdbcDatabaseInit tries to call this if set
+    driver->release = nullptr;
+    return ADBC_STATUS_INTERNAL;
+  }
+
+  void* load_handle = dlsym(handle, entrypoint);
+  if (!load_handle) {
+    std::string message = "dlsym(";
+    message += entrypoint;
+    message += ") failed: ";
+    message += dlerror();
+    SetError(error, message);
+    return ADBC_STATUS_INTERNAL;
+  }
+  init_func = reinterpret_cast<AdbcDriverInitFunc>(load_handle);
+
+#endif  // defined(_WIN32)
+
+  AdbcStatusCode status = AdbcLoadDriverFromInitFunc(init_func, version, 
driver, error);
+  if (status == ADBC_STATUS_OK) {
+    ManagerDriverState* state = new ManagerDriverState;
+    state->driver_release = driver->release;
+#if defined(_WIN32)
+    state->handle = handle;
+#endif  // defined(_WIN32)
+    driver->release = &ReleaseDriver;
+    driver->private_manager = state;
+  } else {
+#if defined(_WIN32)
+    if (!FreeLibrary(handle)) {
+      std::string message = "FreeLibrary() failed: ";
+      GetWinError(&message);
+      SetError(error, message);
+    }
+#endif  // defined(_WIN32)
+  }
+  return status;
+}
+
+AdbcStatusCode AdbcLoadDriverFromInitFunc(AdbcDriverInitFunc init_func, int 
version,
+                                          void* raw_driver, struct AdbcError* 
error) {
+#define FILL_DEFAULT(DRIVER, STUB) \
+  if (!DRIVER->STUB) {             \
+    DRIVER->STUB = &STUB;          \
+  }
+#define CHECK_REQUIRED(DRIVER, STUB)                                           
\
+  if (!DRIVER->STUB) {                                                         
\
+    SetError(error, "Driver does not implement required function Adbc" #STUB); 
\
+    return ADBC_STATUS_INTERNAL;                                               
\
+  }
+
+  auto result = init_func(version, raw_driver, error);
+  if (result != ADBC_STATUS_OK) {
+    return result;
+  }
+
+  if (version == ADBC_VERSION_1_0_0) {
+    auto* driver = reinterpret_cast<struct AdbcDriver*>(raw_driver);
+    CHECK_REQUIRED(driver, DatabaseNew);
+    CHECK_REQUIRED(driver, DatabaseInit);
+    CHECK_REQUIRED(driver, DatabaseRelease);
+    FILL_DEFAULT(driver, DatabaseSetOption);
+
+    CHECK_REQUIRED(driver, ConnectionNew);
+    CHECK_REQUIRED(driver, ConnectionInit);
+    CHECK_REQUIRED(driver, ConnectionRelease);
+    FILL_DEFAULT(driver, ConnectionCommit);
+    FILL_DEFAULT(driver, ConnectionGetInfo);
+    FILL_DEFAULT(driver, ConnectionGetObjects);
+    FILL_DEFAULT(driver, ConnectionGetTableSchema);
+    FILL_DEFAULT(driver, ConnectionGetTableTypes);
+    FILL_DEFAULT(driver, ConnectionReadPartition);
+    FILL_DEFAULT(driver, ConnectionRollback);
+    FILL_DEFAULT(driver, ConnectionSetOption);
+
+    FILL_DEFAULT(driver, StatementExecutePartitions);
+    CHECK_REQUIRED(driver, StatementExecuteQuery);
+    CHECK_REQUIRED(driver, StatementNew);
+    CHECK_REQUIRED(driver, StatementRelease);
+    FILL_DEFAULT(driver, StatementBind);
+    FILL_DEFAULT(driver, StatementGetParameterSchema);
+    FILL_DEFAULT(driver, StatementPrepare);
+    FILL_DEFAULT(driver, StatementSetOption);
+    FILL_DEFAULT(driver, StatementSetSqlQuery);
+    FILL_DEFAULT(driver, StatementSetSubstraitPlan);
+  }
+
+  return ADBC_STATUS_OK;
+
+#undef FILL_DEFAULT
+#undef CHECK_REQUIRED
+}
diff --git a/go/adbc/drivermgr/adbc_driver_manager.h 
b/go/adbc/drivermgr/adbc_driver_manager.h
deleted file mode 120000
index a9f56c5..0000000
--- a/go/adbc/drivermgr/adbc_driver_manager.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../c/driver_manager/adbc_driver_manager.h
\ No newline at end of file
diff --git a/go/adbc/drivermgr/adbc_driver_manager.h 
b/go/adbc/drivermgr/adbc_driver_manager.h
new file mode 100644
index 0000000..69b767a
--- /dev/null
+++ b/go/adbc/drivermgr/adbc_driver_manager.h
@@ -0,0 +1,84 @@
+// 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.
+
+#pragma once
+
+#include <adbc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ADBC_DRIVER_MANAGER_H
+#define ADBC_DRIVER_MANAGER_H
+
+/// \brief Common entry point for drivers via the driver manager.
+///
+/// The driver manager can fill in default implementations of some
+/// ADBC functions for drivers. Drivers must implement a minimum level
+/// of functionality for this to be possible, however, and some
+/// functions must be implemented by the driver.
+///
+/// \param[in] driver_name An identifier for the driver (e.g. a path to a
+///   shared library on Linux).
+/// \param[in] entrypoint An identifier for the entrypoint (e.g. the
+///   symbol to call for AdbcDriverInitFunc on Linux).
+/// \param[in] version The ADBC revision to attempt to initialize.
+/// \param[out] driver The table of function pointers to initialize.
+/// \param[out] error An optional location to return an error message
+///   if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcLoadDriver(const char* driver_name, const char* entrypoint,
+                              int version, void* driver, struct AdbcError* 
error);
+
+/// \brief Common entry point for drivers via the driver manager.
+///
+/// The driver manager can fill in default implementations of some
+/// ADBC functions for drivers. Drivers must implement a minimum level
+/// of functionality for this to be possible, however, and some
+/// functions must be implemented by the driver.
+///
+/// \param[in] init_func The entrypoint to call.
+/// \param[in] version The ADBC revision to attempt to initialize.
+/// \param[out] driver The table of function pointers to initialize.
+/// \param[out] error An optional location to return an error message
+///   if necessary.
+ADBC_EXPORT
+AdbcStatusCode AdbcLoadDriverFromInitFunc(AdbcDriverInitFunc init_func, int 
version,
+                                          void* driver, struct AdbcError* 
error);
+
+/// \brief Set the AdbcDriverInitFunc to use.
+///
+/// This is an extension to the ADBC API. The driver manager shims
+/// the AdbcDatabase* functions to allow you to specify the
+/// driver/entrypoint dynamically. This function lets you set the
+/// entrypoint explicitly, for applications that can dynamically
+/// load drivers on their own.
+ADBC_EXPORT
+AdbcStatusCode AdbcDriverManagerDatabaseSetInitFunc(struct AdbcDatabase* 
database,
+                                                    AdbcDriverInitFunc 
init_func,
+                                                    struct AdbcError* error);
+
+/// \brief Get a human-friendly description of a status code.
+ADBC_EXPORT
+const char* AdbcStatusCodeMessage(AdbcStatusCode code);
+
+#endif  // ADBC_DRIVER_MANAGER_H
+
+#ifdef __cplusplus
+}
+#endif


Reply via email to