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

paleolimbot 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 25725fd5b fix(c/driver_manager): More robust error reporting for 
errors that occur before AdbcDatabaseInit() (#2266)
25725fd5b is described below

commit 25725fd5b4f5199631768891986ec0357e33b97e
Author: Dewey Dunnington <[email protected]>
AuthorDate: Tue Oct 22 16:24:36 2024 +0000

    fix(c/driver_manager): More robust error reporting for errors that occur 
before AdbcDatabaseInit() (#2266)
    
    Fixes #2264.
---
 c/driver/framework/base_driver.h        | 11 ++++++
 c/driver_manager/adbc_driver_manager.cc | 63 +++++++++++++++++++++++++++------
 2 files changed, 63 insertions(+), 11 deletions(-)

diff --git a/c/driver/framework/base_driver.h b/c/driver/framework/base_driver.h
index b52e47412..eecb506ee 100644
--- a/c/driver/framework/base_driver.h
+++ b/c/driver/framework/base_driver.h
@@ -455,11 +455,22 @@ class Driver {
     }
 
     auto error_obj = reinterpret_cast<Status*>(error->private_data);
+    if (!error_obj) {
+      return 0;
+    }
     return error_obj->CDetailCount();
   }
 
   static AdbcErrorDetail CErrorGetDetail(const AdbcError* error, int index) {
+    if (error->vendor_code != ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA) {
+      return {nullptr, nullptr, 0};
+    }
+
     auto error_obj = reinterpret_cast<Status*>(error->private_data);
+    if (!error_obj) {
+      return {nullptr, nullptr, 0};
+    }
+
     return error_obj->CDetail(index);
   }
 
diff --git a/c/driver_manager/adbc_driver_manager.cc 
b/c/driver_manager/adbc_driver_manager.cc
index 44c3d9f98..0ce173a88 100644
--- a/c/driver_manager/adbc_driver_manager.cc
+++ b/c/driver_manager/adbc_driver_manager.cc
@@ -84,6 +84,36 @@ void SetError(struct AdbcError* error, const std::string& 
message) {
   error->release = ReleaseError;
 }
 
+// Copies src_error into error and releases src_error
+void SetError(struct AdbcError* error, struct AdbcError* src_error) {
+  if (!error) return;
+  if (error->release) error->release(error);
+
+  if (src_error->message) {
+    size_t message_size = strlen(src_error->message);
+    error->message = new char[message_size];
+    std::memcpy(error->message, src_error->message, message_size);
+    error->message[message_size] = '\0';
+  } else {
+    error->message = nullptr;
+  }
+
+  error->release = ReleaseError;
+  if (src_error->release) {
+    src_error->release(src_error);
+  }
+}
+
+struct OwnedError {
+  struct AdbcError error = ADBC_ERROR_INIT;
+
+  ~OwnedError() {
+    if (error.release) {
+      error.release(&error);
+    }
+  }
+};
+
 // Driver state
 
 /// A driver DLL.
@@ -666,7 +696,7 @@ std::string AdbcDriverManagerDefaultEntrypoint(const 
std::string& driver) {
 
 int AdbcErrorGetDetailCount(const struct AdbcError* error) {
   if (error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA && 
error->private_data &&
-      error->private_driver) {
+      error->private_driver && error->private_driver->ErrorGetDetailCount) {
     return error->private_driver->ErrorGetDetailCount(error);
   }
   return 0;
@@ -674,7 +704,7 @@ int AdbcErrorGetDetailCount(const struct AdbcError* error) {
 
 struct AdbcErrorDetail AdbcErrorGetDetail(const struct AdbcError* error, int 
index) {
   if (error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA && 
error->private_data &&
-      error->private_driver) {
+      error->private_driver && error->private_driver->ErrorGetDetail) {
     return error->private_driver->ErrorGetDetail(error, index);
   }
   return {nullptr, nullptr, 0};
@@ -900,6 +930,7 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* 
database, struct AdbcError*
     status = AdbcLoadDriver(args->driver.c_str(), nullptr, ADBC_VERSION_1_1_0,
                             database->private_driver, error);
   }
+
   if (status != ADBC_STATUS_OK) {
     // Restore private_data so it will be released by AdbcDatabaseRelease
     database->private_data = args;
@@ -910,10 +941,18 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* 
database, struct AdbcError*
     database->private_driver = nullptr;
     return status;
   }
-  status = database->private_driver->DatabaseNew(database, error);
+
+  // Errors that occur during AdbcDatabaseXXX() refer to the driver via
+  // the private_driver member; however, after we return we have released
+  // the driver and inspecting the error might segfault. Here, we scope
+  // the driver-produced error to this function and make a copy if necessary.
+  OwnedError driver_error;
+
+  status = database->private_driver->DatabaseNew(database, 
&driver_error.error);
   if (status != ADBC_STATUS_OK) {
     if (database->private_driver->release) {
-      database->private_driver->release(database->private_driver, error);
+      SetError(error, &driver_error.error);
+      database->private_driver->release(database->private_driver, nullptr);
     }
     delete database->private_driver;
     database->private_driver = nullptr;
@@ -927,33 +966,34 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* 
database, struct AdbcError*
 
   INIT_ERROR(error, database);
   for (const auto& option : options) {
-    status = database->private_driver->DatabaseSetOption(database, 
option.first.c_str(),
-                                                         
option.second.c_str(), error);
+    status = database->private_driver->DatabaseSetOption(
+        database, option.first.c_str(), option.second.c_str(), 
&driver_error.error);
     if (status != ADBC_STATUS_OK) break;
   }
   for (const auto& option : bytes_options) {
     status = database->private_driver->DatabaseSetOptionBytes(
         database, option.first.c_str(),
         reinterpret_cast<const uint8_t*>(option.second.data()), 
option.second.size(),
-        error);
+        &driver_error.error);
     if (status != ADBC_STATUS_OK) break;
   }
   for (const auto& option : int_options) {
     status = database->private_driver->DatabaseSetOptionInt(
-        database, option.first.c_str(), option.second, error);
+        database, option.first.c_str(), option.second, &driver_error.error);
     if (status != ADBC_STATUS_OK) break;
   }
   for (const auto& option : double_options) {
     status = database->private_driver->DatabaseSetOptionDouble(
-        database, option.first.c_str(), option.second, error);
+        database, option.first.c_str(), option.second, &driver_error.error);
     if (status != ADBC_STATUS_OK) break;
   }
 
   if (status != ADBC_STATUS_OK) {
     // Release the database
-    std::ignore = database->private_driver->DatabaseRelease(database, error);
+    std::ignore = database->private_driver->DatabaseRelease(database, nullptr);
     if (database->private_driver->release) {
-      database->private_driver->release(database->private_driver, error);
+      SetError(error, &driver_error.error);
+      database->private_driver->release(database->private_driver, nullptr);
     }
     delete database->private_driver;
     database->private_driver = nullptr;
@@ -962,6 +1002,7 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* 
database, struct AdbcError*
     database->private_data = nullptr;
     return status;
   }
+
   return database->private_driver->DatabaseInit(database, error);
 }
 

Reply via email to