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 29f942a  Fix Windows build (#16)
29f942a is described below

commit 29f942a1a9f165b52426e28bc9dae64459807916
Author: David Li <[email protected]>
AuthorDate: Tue Jun 14 10:55:47 2022 -0400

    Fix Windows build (#16)
    
    * Add Windows visibility specifiers
    
    * Try to implement Windows loader
    
    * Add driver cleanup
    
    * Add driver cleanup
    
    * Fix driver cleanup
    
    * Address TODOs
---
 adbc.h                                          |  61 +++++++--
 adbc_driver_manager/CMakeLists.txt              |   9 +-
 adbc_driver_manager/adbc_driver_manager.cc      | 167 ++++++++++++++++++++++--
 adbc_driver_manager/adbc_driver_manager.h       |   2 +
 adbc_driver_manager/adbc_driver_manager_test.cc |   8 ++
 drivers/flight_sql/CMakeLists.txt               |   5 +
 drivers/sqlite/CMakeLists.txt                   |   5 +
 drivers/sqlite/sqlite.cc                        |  21 +--
 drivers/util.h                                  |   7 -
 9 files changed, 235 insertions(+), 50 deletions(-)

diff --git a/adbc.h b/adbc.h
index fb3db59..a1a3bda 100644
--- a/adbc.h
+++ b/adbc.h
@@ -116,6 +116,17 @@ struct ArrowArrayStream {
 #ifndef ADBC
 #define ADBC
 
+// Storage class macros for Windows
+#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)
+
 /// \file ADBC: Arrow DataBase connectivity (client API)
 ///
 /// Implemented by libadbc.so (provided by Arrow/C++), which in turn
@@ -160,7 +171,7 @@ typedef uint8_t AdbcStatusCode;
 #define ADBC_STATUS_IO 6
 
 /// \brief A detailed error message for an operation.
-struct AdbcError {
+struct ADBC_EXPORT AdbcError {
   /// \brief The error message.
   char* message;
 
@@ -184,7 +195,7 @@ struct AdbcError {
 /// \brief An instance of a database.
 ///
 /// Must be kept alive as long as any connections exist.
-struct AdbcDatabase {
+struct ADBC_EXPORT AdbcDatabase {
   /// \brief Opaque implementation-defined state.
   /// This field is NULLPTR iff the connection is unintialized/freed.
   void* private_data;
@@ -194,9 +205,11 @@ struct AdbcDatabase {
 };
 
 /// \brief Allocate a new (but uninitialized) database.
+ADBC_EXPORT
 AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct 
AdbcError* error);
 
 /// \brief Set a char* option.
+ADBC_EXPORT
 AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const 
char* key,
                                      const char* value, struct AdbcError* 
error);
 
@@ -204,12 +217,14 @@ AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* 
database, const char*
 ///
 /// Some backends 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);
 
@@ -219,7 +234,7 @@ AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* 
database,
 /// @{
 
 /// \brief A set of connection options.
-struct AdbcConnectionOptions {
+struct ADBC_EXPORT AdbcConnectionOptions {
   /// \brief The database to connect to.
   struct AdbcDatabase* database;
 
@@ -236,7 +251,7 @@ struct AdbcConnectionOptions {
 ///
 /// Connections are not thread-safe and clients should take care to
 /// serialize accesses to a connection.
-struct AdbcConnection {
+struct ADBC_EXPORT AdbcConnection {
   /// \brief Opaque implementation-defined state.
   /// This field is NULLPTR iff the connection is unintialized/freed.
   void* private_data;
@@ -246,14 +261,17 @@ struct AdbcConnection {
 };
 
 /// \brief Allocate a new (but uninitialized) connection.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionNew(struct AdbcDatabase* database,
                                  struct AdbcConnection* connection,
                                  struct AdbcError* error);
 
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, 
const char* key,
                                        const char* value, struct AdbcError* 
error);
 
 /// \brief Finish setting options and initialize the connection.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
                                   struct AdbcError* error);
 
@@ -261,6 +279,7 @@ AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* 
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);
 
@@ -281,6 +300,7 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* 
connection,
 ///   statement can then be read independently.
 ///
 /// A partition can be retrieved from AdbcStatementGetPartitionDesc.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionDeserializePartitionDesc(struct AdbcConnection* 
connection,
                                                       const uint8_t* 
serialized_partition,
                                                       size_t serialized_length,
@@ -318,6 +338,7 @@ AdbcStatusCode 
AdbcConnectionDeserializePartitionDesc(struct AdbcConnection* con
 /// \param[in] connection The database connection.
 /// \param[out] statement The result set.
 /// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionGetCatalogs(struct AdbcConnection* connection,
                                          struct AdbcStatement* statement,
                                          struct AdbcError* error);
@@ -334,6 +355,7 @@ AdbcStatusCode AdbcConnectionGetCatalogs(struct 
AdbcConnection* connection,
 /// \param[in] connection The database connection.
 /// \param[out] statement The result set.
 /// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionGetDbSchemas(struct AdbcConnection* connection,
                                           struct AdbcStatement* statement,
                                           struct AdbcError* error);
@@ -349,6 +371,7 @@ AdbcStatusCode AdbcConnectionGetDbSchemas(struct 
AdbcConnection* connection,
 /// \param[in] connection The database connection.
 /// \param[out] statement The result set.
 /// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionGetTableTypes(struct AdbcConnection* connection,
                                            struct AdbcStatement* statement,
                                            struct AdbcError* error);
@@ -379,6 +402,7 @@ AdbcStatusCode AdbcConnectionGetTableTypes(struct 
AdbcConnection* connection,
 ///   from get_table_types.
 /// \param[out] statement The result set.
 /// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
 AdbcStatusCode AdbcConnectionGetTables(struct AdbcConnection* connection,
                                        const char* catalog, const char* 
db_schema,
                                        const char* table_name, const char** 
table_types,
@@ -402,7 +426,7 @@ AdbcStatusCode AdbcConnectionGetTables(struct 
AdbcConnection* connection,
 ///
 /// Statements are not thread-safe and clients should take care to
 /// serialize access.
-struct AdbcStatement {
+struct ADBC_EXPORT AdbcStatement {
   /// \brief Opaque implementation-defined state.
   /// This field is NULLPTR iff the connection is unintialized/freed.
   void* private_data;
@@ -416,6 +440,7 @@ struct AdbcStatement {
 ///
 /// Set options on the statement, then call AdbcStatementExecute or
 /// AdbcStatementPrepare.
+ADBC_EXPORT
 AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
                                 struct AdbcStatement* statement, struct 
AdbcError* error);
 
@@ -423,14 +448,17 @@ AdbcStatusCode AdbcStatementNew(struct AdbcConnection* 
connection,
 /// \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.
+ADBC_EXPORT
 AdbcStatusCode AdbcStatementExecute(struct AdbcStatement* statement,
                                     struct AdbcError* error);
 
 /// \brief Create a prepared statement to be executed multiple times.
+ADBC_EXPORT
 AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
                                     struct AdbcError* error);
 
@@ -450,6 +478,7 @@ AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* 
statement,
 /// \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);
 
@@ -472,6 +501,7 @@ AdbcStatusCode AdbcStatementSetSqlQuery(struct 
AdbcStatement* 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);
@@ -487,6 +517,7 @@ AdbcStatusCode AdbcStatementSetSubstraitPlan(struct 
AdbcStatement* statement,
 /// \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);
@@ -499,6 +530,7 @@ AdbcStatusCode AdbcStatementBind(struct AdbcStatement* 
statement,
 ///   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* values,
                                        struct AdbcError* error);
@@ -511,11 +543,13 @@ AdbcStatusCode AdbcStatementBindStream(struct 
AdbcStatement* statement,
 ///
 /// \return out A stream of Arrow data. The stream itself must be
 ///   released before the statement is released.
+ADBC_EXPORT
 AdbcStatusCode AdbcStatementGetStream(struct AdbcStatement* statement,
                                       struct ArrowArrayStream* out,
                                       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);
 
@@ -564,6 +598,7 @@ AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* 
statement, const cha
 ///   are no more partitions.
 /// \param[out] error An optional location to return an error message if
 ///   necessary.
+ADBC_EXPORT
 AdbcStatusCode AdbcStatementGetPartitionDescSize(struct AdbcStatement* 
statement,
                                                  size_t* length, struct 
AdbcError* error);
 
@@ -583,6 +618,7 @@ AdbcStatusCode AdbcStatementGetPartitionDescSize(struct 
AdbcStatement* statement
 ///   queried with AdbcStatementGetPartitionDescSize.
 /// \param[out] error An optional location to return an error message if
 ///   necessary.
+ADBC_EXPORT
 AdbcStatusCode AdbcStatementGetPartitionDesc(struct AdbcStatement* statement,
                                              uint8_t* partition_desc,
                                              struct AdbcError* error);
@@ -601,12 +637,21 @@ AdbcStatusCode AdbcStatementGetPartitionDesc(struct 
AdbcStatement* statement,
 /// struct, and applications can call ADBC functions through this
 /// struct, without worrying about multiple definitions of the same
 /// symbol.
-struct AdbcDriver {
-  /// \brief Opaque implementation-defined state.
+struct ADBC_EXPORT AdbcDriver {
+  /// \brief Opaque driver-defined state.
   /// This field is NULLPTR if the driver is unintialized/freed (but
   /// it need not have a value even if the driver is initialized).
   void* private_data;
-  // TODO: DriverRelease
+  /// \brief Opaque driver manager-defined state.
+  /// This field is NULLPTR 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.
+  ///
+  /// Unlike other structures, 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 (*DatabaseNew)(struct AdbcDatabase*, struct AdbcError*);
   AdbcStatusCode (*DatabaseSetOption)(struct AdbcDatabase*, const char*, const 
char*,
diff --git a/adbc_driver_manager/CMakeLists.txt 
b/adbc_driver_manager/CMakeLists.txt
index 81887e7..2533e07 100644
--- a/adbc_driver_manager/CMakeLists.txt
+++ b/adbc_driver_manager/CMakeLists.txt
@@ -27,8 +27,15 @@ project(adbc_driver_manager
         VERSION "${ADBC_BASE_VERSION}"
         LANGUAGES CXX)
 include(CTest)
-add_arrow_lib(adbc_driver_manager SOURCES adbc_driver_manager.cc)
+add_arrow_lib(adbc_driver_manager
+              SOURCES
+              adbc_driver_manager.cc
+              OUTPUTS
+              ADBC_LIBRARIES)
 include_directories(SYSTEM ${REPOSITORY_ROOT})
+foreach(LIB_TARGET ${ADBC_LIBRARIES})
+  target_compile_definitions(${LIB_TARGET} PRIVATE ADBC_EXPORTING)
+endforeach()
 
 if(ADBC_BUILD_TESTS)
   find_package(Arrow REQUIRED)
diff --git a/adbc_driver_manager/adbc_driver_manager.cc 
b/adbc_driver_manager/adbc_driver_manager.cc
index 7872bdf..c273fb4 100644
--- a/adbc_driver_manager/adbc_driver_manager.cc
+++ b/adbc_driver_manager/adbc_driver_manager.cc
@@ -17,12 +17,20 @@
 
 #include "adbc_driver_manager.h"
 
-#include <dlfcn.h>
 #include <algorithm>
 #include <cstring>
 #include <string>
 #include <unordered_map>
 
+#if defined(_WIN32)
+#include <windows.h>  // Must come first
+
+#include <libloaderapi.h>
+#include <strsafe.h>
+#else
+#include <dlfcn.h>
+#endif  // defined(_WIN32)
+
 namespace {
 void ReleaseError(struct AdbcError* error) {
   if (error) {
@@ -32,9 +40,23 @@ void ReleaseError(struct AdbcError* error) {
 }
 
 void SetError(struct AdbcError* error, const std::string& message) {
-  error->message = new char[message.size() + 1];
-  message.copy(error->message, message.size());
-  error->message[message.size()] = '\0';
+  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;
 }
 
@@ -74,6 +96,56 @@ struct TempDatabase {
   std::string driver;
   std::string entrypoint;
 };
+
+#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);
+}
+
+/// Hold the driver DLL and the driver release callback in the driver struct.
+struct ManagerDriverState {
+  // The loaded DLL
+  HMODULE handle;
+  // The original release callback
+  AdbcStatusCode (*driver_release)(struct AdbcDriver* driver, struct 
AdbcError* error);
+};
+
+/// 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 (!FreeLibrary(state->handle)) {
+    std::string message = "FreeLibrary() failed: ";
+    GetWinError(&message);
+    SetError(error, message);
+  }
+
+  driver->private_manager = nullptr;
+  delete state;
+  return status;
+}
+#endif
 }  // namespace
 
 // Direct implementations of API methods
@@ -125,17 +197,34 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* 
database, struct AdbcError*
                      database->private_driver, &initialized, error);
   if (status != ADBC_STATUS_OK) {
     delete args;
+
+    if (database->private_driver->release) {
+      database->private_driver->release(database->private_driver, error);
+    }
     delete database->private_driver;
     return status;
   } else if (initialized < ADBC_VERSION_0_0_1) {
     delete args;
+
+    if (database->private_driver->release) {
+      database->private_driver->release(database->private_driver, error);
+    }
     delete database->private_driver;
-    SetError(error, "Database version is too old");  // TODO: clearer error
+
+    std::string message = "Database version is too old, expected ";
+    message += std::to_string(ADBC_VERSION_0_0_1);
+    message += " but got ";
+    message += std::to_string(initialized);
+    SetError(error, message);
     return status;
   }
   status = database->private_driver->DatabaseNew(database, error);
   if (status != ADBC_STATUS_OK) {
     delete args;
+
+    if (database->private_driver->release) {
+      database->private_driver->release(database->private_driver, error);
+    }
     delete database->private_driver;
     return status;
   }
@@ -144,6 +233,10 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* 
database, struct AdbcError*
                                                          
option.second.c_str(), error);
     if (status != ADBC_STATUS_OK) {
       delete args;
+
+      if (database->private_driver->release) {
+        database->private_driver->release(database->private_driver, error);
+      }
       delete database->private_driver;
       return status;
     }
@@ -158,6 +251,9 @@ AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* 
database,
     return ADBC_STATUS_UNINITIALIZED;
   }
   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;
   return status;
 }
@@ -176,7 +272,6 @@ AdbcStatusCode AdbcConnectionNew(struct AdbcDatabase* 
database,
 AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
                                   struct AdbcError* error) {
   if (!connection->private_driver) {
-    // TODO: set error
     return ADBC_STATUS_INVALID_ARGUMENT;
   }
   return connection->private_driver->ConnectionInit(connection, error);
@@ -318,16 +413,54 @@ AdbcStatusCode AdbcLoadDriver(const char* driver_name, 
const char* entrypoint,
     return ADBC_STATUS_INTERNAL;                                               
\
   }
 
-  // TODO: handle Windows
+  AdbcDriverInitFunc init_func;
+  std::string error_message;
+
+#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 = GetProcAddress(handle, entrypoint);
+  init_func = reinterpret_cast<AdbcDriverInitFunc>(load_handle);
+  if (!init_func) {
+    std::string message = "GetProcAddress() 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
-
-  std::string error_message;
+#endif  // defined(__APPLE__)
 
   void* handle = dlopen(driver_name, RTLD_NOW | RTLD_LOCAL);
   if (!handle) {
@@ -359,19 +492,25 @@ AdbcStatusCode AdbcLoadDriver(const char* driver_name, 
const char* entrypoint,
   }
   if (!handle) {
     SetError(error, error_message);
-    return ADBC_STATUS_UNKNOWN;
+    return ADBC_STATUS_INTERNAL;
   }
 
   void* load_handle = dlsym(handle, entrypoint);
-  auto* load = reinterpret_cast<AdbcDriverInitFunc>(load_handle);
-  if (!load) {
+  init_func = reinterpret_cast<AdbcDriverInitFunc>(load_handle);
+  if (!init_func) {
     std::string message = "dlsym() failed: ";
     message += dlerror();
     SetError(error, message);
     return ADBC_STATUS_INTERNAL;
   }
 
-  auto result = load(count, driver, initialized, error);
+#endif  // defined(_WIN32)
+
+  auto result = init_func(count, driver, initialized, error);
+#if defined(_WIN32)
+  driver->private_manager = new ManagerDriverState{handle, driver->release};
+  driver->release = &ReleaseDriver;
+#endif  // defined(_WIN32)
   if (result != ADBC_STATUS_OK) {
     return result;
   }
diff --git a/adbc_driver_manager/adbc_driver_manager.h 
b/adbc_driver_manager/adbc_driver_manager.h
index d20c4b1..bdc25a2 100644
--- a/adbc_driver_manager/adbc_driver_manager.h
+++ b/adbc_driver_manager/adbc_driver_manager.h
@@ -44,11 +44,13 @@ extern "C" {
 ///   initialized (can be less than count).
 /// \param[out] error An optional location to return an error message
 ///   if necessary.
+ADBC_EXPORT
 AdbcStatusCode AdbcLoadDriver(const char* driver_name, const char* entrypoint,
                               size_t count, struct AdbcDriver* driver,
                               size_t* initialized, 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
diff --git a/adbc_driver_manager/adbc_driver_manager_test.cc 
b/adbc_driver_manager/adbc_driver_manager_test.cc
index f365101..cdfc4e2 100644
--- a/adbc_driver_manager/adbc_driver_manager_test.cc
+++ b/adbc_driver_manager/adbc_driver_manager_test.cc
@@ -36,6 +36,8 @@ using arrow::PointeesEqual;
 class DriverManager : public ::testing::Test {
  public:
   void SetUp() override {
+    std::memset(&driver, 0, sizeof(driver));
+
     size_t initialized = 0;
     ADBC_ASSERT_OK_WITH_ERROR(
         error, AdbcLoadDriver("adbc_driver_sqlite", "AdbcSqliteDriverInit",
@@ -63,6 +65,12 @@ class DriverManager : public ::testing::Test {
 
     ADBC_ASSERT_OK_WITH_ERROR(error, AdbcDatabaseRelease(&database, &error));
     ASSERT_EQ(database.private_data, nullptr);
+
+    if (driver.release) {
+      ADBC_ASSERT_OK_WITH_ERROR(error, driver.release(&driver, &error));
+      ASSERT_EQ(driver.private_data, nullptr);
+      ASSERT_EQ(driver.private_manager, nullptr);
+    }
   }
 
  protected:
diff --git a/drivers/flight_sql/CMakeLists.txt 
b/drivers/flight_sql/CMakeLists.txt
index 0841a9e..33f53e1 100644
--- a/drivers/flight_sql/CMakeLists.txt
+++ b/drivers/flight_sql/CMakeLists.txt
@@ -34,6 +34,8 @@ find_package(ArrowFlightSql REQUIRED HINTS 
${ARROW_CONFIG_PATH})
 add_arrow_lib(adbc_driver_flight_sql
               SOURCES
               flight_sql.cc
+              OUTPUTS
+              ADBC_LIBRARIES
               SHARED_LINK_LIBS
               arrow_flight_sql_shared
               arrow_flight_shared
@@ -43,6 +45,9 @@ add_arrow_lib(adbc_driver_flight_sql
               arrow_flight_static
               arrow_static)
 include_directories(SYSTEM ${REPOSITORY_ROOT})
+foreach(LIB_TARGET ${ADBC_LIBRARIES})
+  target_compile_definitions(${LIB_TARGET} PRIVATE ADBC_EXPORTING)
+endforeach()
 
 if(ADBC_BUILD_TESTS)
   if(ADBC_TEST_LINKAGE STREQUAL "shared")
diff --git a/drivers/sqlite/CMakeLists.txt b/drivers/sqlite/CMakeLists.txt
index 11b1b72..0872486 100644
--- a/drivers/sqlite/CMakeLists.txt
+++ b/drivers/sqlite/CMakeLists.txt
@@ -32,6 +32,8 @@ find_package(SQLite3 REQUIRED)
 add_arrow_lib(adbc_driver_sqlite
               SOURCES
               sqlite.cc
+              OUTPUTS
+              ADBC_LIBRARIES
               SHARED_LINK_LIBS
               SQLite::SQLite3
               arrow_shared
@@ -40,6 +42,9 @@ add_arrow_lib(adbc_driver_sqlite
               arrow_static)
 include_directories(SYSTEM ${REPOSITORY_ROOT})
 include_directories(SYSTEM ${SQLite3_INCLUDE_DIRS})
+foreach(LIB_TARGET ${ADBC_LIBRARIES})
+  target_compile_definitions(${LIB_TARGET} PRIVATE ADBC_EXPORTING)
+endforeach()
 
 if(ADBC_TEST_LINKAGE STREQUAL "shared")
   set(TEST_LINK_LIBS adbc_driver_sqlite_shared)
diff --git a/drivers/sqlite/sqlite.cc b/drivers/sqlite/sqlite.cc
index e141fa6..2973794 100644
--- a/drivers/sqlite/sqlite.cc
+++ b/drivers/sqlite/sqlite.cc
@@ -777,127 +777,108 @@ AdbcStatusCode SqliteStatementSetSqlQuery(struct 
AdbcStatement* statement,
 
 }  // namespace
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct 
AdbcError* error) {
   return SqliteDatabaseInit(database, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct 
AdbcError* error) {
   return SqliteDatabaseNew(database, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const 
char* key,
                                      const char* value, struct AdbcError* 
error) {
   return SqliteDatabaseSetOption(database, key, value, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
                                    struct AdbcError* error) {
   return SqliteDatabaseRelease(database, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcConnectionNew(struct AdbcDatabase* database,
                                  struct AdbcConnection* connection,
                                  struct AdbcError* error) {
   return SqliteConnectionNew(database, connection, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, 
const char* key,
                                        const char* value, struct AdbcError* 
error) {
   return SqliteConnectionSetOption(connection, key, value, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
                                   struct AdbcError* error) {
   return SqliteConnectionInit(connection, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
                                      struct AdbcError* error) {
   return SqliteConnectionRelease(connection, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementBind(struct AdbcStatement* statement,
                                  struct ArrowArray* values, struct 
ArrowSchema* schema,
                                  struct AdbcError* error) {
   return SqliteStatementBind(statement, values, schema, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
                                        struct ArrowArrayStream* stream,
                                        struct AdbcError* error) {
   return SqliteStatementBindStream(statement, stream, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementExecute(struct AdbcStatement* statement,
                                     struct AdbcError* error) {
   return SqliteStatementExecute(statement, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementGetPartitionDesc(struct AdbcStatement* statement,
                                              uint8_t* partition_desc,
                                              struct AdbcError* error) {
   return SqliteStatementGetPartitionDesc(statement, partition_desc, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementGetPartitionDescSize(struct AdbcStatement* 
statement,
                                                  size_t* length,
                                                  struct AdbcError* error) {
   return SqliteStatementGetPartitionDescSize(statement, length, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementGetStream(struct AdbcStatement* statement,
                                       struct ArrowArrayStream* out,
                                       struct AdbcError* error) {
   return SqliteStatementGetStream(statement, out, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
                                 struct AdbcStatement* statement,
                                 struct AdbcError* error) {
   return SqliteStatementNew(connection, statement, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
                                     struct AdbcError* error) {
   return SqliteStatementPrepare(statement, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementRelease(struct AdbcStatement* statement,
                                     struct AdbcError* error) {
   return SqliteStatementRelease(statement, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const 
char* key,
                                       const char* value, struct AdbcError* 
error) {
   return SqliteStatementSetOption(statement, key, value, error);
 }
 
-ADBC_DRIVER_EXPORT
 AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
                                         const char* query, struct AdbcError* 
error) {
   return SqliteStatementSetSqlQuery(statement, query, error);
 }
 
 extern "C" {
-ARROW_EXPORT
+ADBC_EXPORT
 AdbcStatusCode AdbcSqliteDriverInit(size_t count, struct AdbcDriver* driver,
                                     size_t* initialized, struct AdbcError* 
error) {
   if (count < ADBC_VERSION_0_0_1) return ADBC_STATUS_NOT_IMPLEMENTED;
diff --git a/drivers/util.h b/drivers/util.h
index da998a0..1ac8408 100644
--- a/drivers/util.h
+++ b/drivers/util.h
@@ -21,13 +21,6 @@
 #include "arrow/result.h"
 #include "arrow/util/string_view.h"
 
-// TODO: dllimport/dllexport for Windows. Should that go in adbc.h instead?
-#ifdef __linux__
-#define ADBC_DRIVER_EXPORT
-#else
-#define ADBC_DRIVER_EXPORT
-#endif  // ifdef __linux__
-
 #define ADBC_RETURN_NOT_OK(expr)         \
   do {                                   \
     auto _s = (expr);                    \

Reply via email to