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 20b243955 fix(rust/ffi): don't assume errors have extended info (#4872)
20b243955 is described below
commit 20b243955bbecc1edecefd28b626d5393ee85599
Author: David Li <[email protected]>
AuthorDate: Thu Oct 8 13:57:28 2026 -0700
fix(rust/ffi): don't assume errors have extended info (#4872)
Assisted-by: GPT-6.1 Sol
---
rust/ffi/src/driver_exporter.rs | 101 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 99 insertions(+), 2 deletions(-)
diff --git a/rust/ffi/src/driver_exporter.rs b/rust/ffi/src/driver_exporter.rs
index 00a29ef1b..28ef7d3ed 100644
--- a/rust/ffi/src/driver_exporter.rs
+++ b/rust/ffi/src/driver_exporter.rs
@@ -29,7 +29,7 @@ use super::{
FFI_AdbcConnection, FFI_AdbcDatabase, FFI_AdbcDriver, FFI_AdbcError,
FFI_AdbcErrorDetail,
FFI_AdbcPartitions, FFI_AdbcStatement, options::get_opt_name,
types::ErrorPrivateData,
};
-use adbc_core::constants::ADBC_STATUS_OK;
+use adbc_core::constants::{ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA,
ADBC_STATUS_OK};
use adbc_core::error::{AdbcStatusCode, Error, Result, Status};
use adbc_core::options::{InfoCode, ObjectDepth, OptionConnection,
OptionDatabase, OptionValue};
use adbc_core::{Connection, Database, Driver, Optionable, Statement};
@@ -1900,6 +1900,10 @@ extern "C" fn statement_get_parameter_schema<DriverType:
Driver>(
// Error
unsafe extern "C" fn error_get_detail_count(error: *const FFI_AdbcError) ->
c_int {
+ if error.is_null() || (*error).vendor_code !=
ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA {
+ return 0;
+ }
+
match error.as_ref() {
Some(error) if !error.private_data.is_null() => {
let private_data = error.private_data as *const ErrorPrivateData;
@@ -1919,7 +1923,7 @@ unsafe extern "C" fn error_get_detail(
) -> FFI_AdbcErrorDetail {
let default = FFI_AdbcErrorDetail::default();
- if index < 0 {
+ if index < 0 || error.is_null() || (*error).vendor_code !=
ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA {
return default;
}
@@ -1949,3 +1953,96 @@ unsafe extern "C" fn error_get_detail(
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::types::FFI_AdbcErrorV100;
+ use std::ptr::{null, null_mut};
+
+ #[test]
+ fn test_error_details_null() {
+ unsafe {
+ assert_eq!(error_get_detail_count(null()), 0);
+ for index in [-1, 0, 1] {
+ let detail = error_get_detail(null(), index);
+ assert!(detail.key.is_null());
+ assert!(detail.value.is_null());
+ assert_eq!(detail.value_length, 0);
+ }
+ }
+ }
+
+ #[test]
+ fn test_error_details_legacy() {
+ for vendor_code in [0, 42, -1] {
+ let error = Box::new(FFI_AdbcErrorV100 {
+ message: null_mut(),
+ vendor_code,
+ sqlstate: [0; 5],
+ release: None,
+ });
+ let error =
std::ptr::from_ref(error.as_ref()).cast::<FFI_AdbcError>();
+ unsafe {
+ assert_eq!(error_get_detail_count(error), 0);
+ for index in [-1, 0, 1] {
+ let detail = error_get_detail(error, index);
+ assert!(detail.key.is_null());
+ assert!(detail.value.is_null());
+ assert_eq!(detail.value_length, 0);
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn test_error_details_uninitialized() {
+ let error = FFI_AdbcError::default();
+ unsafe {
+ assert_eq!(error_get_detail_count(&error), 0);
+ for index in [-1, 0, 1] {
+ let detail = error_get_detail(&error, index);
+ assert!(detail.key.is_null());
+ assert!(detail.value.is_null());
+ assert_eq!(detail.value_length, 0);
+ }
+ }
+ }
+
+ #[test]
+ fn test_error_details_extended() -> std::result::Result<(),
std::ffi::NulError> {
+ let value = b"value\0binary";
+ let mut error = FFI_AdbcError::try_from(Error {
+ message: "test error".into(),
+ status: Status::Unknown,
+ vendor_code: 42,
+ sqlstate: [0; 5],
+ details: Some(vec![("key".into(), value.to_vec())]),
+ })?;
+
+ unsafe {
+ assert_eq!(error_get_detail_count(&error), 0);
+ let detail = error_get_detail(&error, 0);
+ assert!(detail.key.is_null());
+ assert!(detail.value.is_null());
+ assert_eq!(detail.value_length, 0);
+
+ error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA;
+ assert_eq!(error_get_detail_count(&error), 1);
+ let detail = error_get_detail(&error, 0);
+ assert_eq!(CStr::from_ptr(detail.key).to_bytes(), b"key");
+ assert_eq!(detail.value_length, value.len());
+ assert_eq!(
+ std::slice::from_raw_parts(detail.value, detail.value_length),
+ value
+ );
+ for index in [-1, 1, c_int::MAX] {
+ let detail = error_get_detail(&error, index);
+ assert!(detail.key.is_null());
+ assert!(detail.value.is_null());
+ assert_eq!(detail.value_length, 0);
+ }
+ }
+ Ok(())
+ }
+}