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 ebf2aea17 fix(rust/ffi): clear release callback on partitions after 
release (#4873)
ebf2aea17 is described below

commit ebf2aea170f109fd7ad71b5c421e9b4ae6f45c7d
Author: David Li <[email protected]>
AuthorDate: Thu Oct 8 13:54:14 2026 -0700

    fix(rust/ffi): clear release callback on partitions after release (#4873)
    
    Assisted-by: GPT-6.1 Sol
---
 rust/ffi/src/types.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 69 insertions(+), 1 deletion(-)

diff --git a/rust/ffi/src/types.rs b/rust/ffi/src/types.rs
index b9cad8f64..87bcbaec3 100644
--- a/rust/ffi/src/types.rs
+++ b/rust/ffi/src/types.rs
@@ -278,9 +278,13 @@ unsafe extern "C" fn release_ffi_partitions(partitions: 
*mut FFI_AdbcPartitions)
     match partitions.as_mut() {
         None => (),
         Some(partitions) => {
+            if partitions.release.take().is_none() {
+                return;
+            }
+
             // SAFETY: `partitions.private_data` was necessarily obtained with 
`Box::into_raw`.
             // Additionally, the boxed data is necessarily 
`PartitionsPrivateData`.
-            // Finally, C should call the release function only once.
+            // The release callback was cleared above to prevent repeated 
deallocation.
             let private_data = Box::from_raw(partitions.private_data as *mut 
PartitionsPrivateData);
 
             #[allow(clippy::needless_range_loop)]
@@ -315,6 +319,10 @@ unsafe extern "C" fn release_ffi_partitions(partitions: 
*mut FFI_AdbcPartitions)
             drop(partitions_vec);
 
             drop(private_data);
+            partitions.num_partitions = 0;
+            partitions.partitions = null_mut();
+            partitions.partition_lengths = null_mut();
+            partitions.private_data = null_mut();
         }
     }
 }
@@ -710,6 +718,66 @@ mod tests {
         assert_eq!(partitions_expected, partitions_actual);
     }
 
+    #[test]
+    fn test_release_ffi_partitions_nulls_fields() {
+        let mut partition = Vec::with_capacity(16);
+        partition.extend_from_slice(b"partition");
+        let inputs: [Partitions; 3] = [
+            vec![],
+            vec![vec![], Vec::with_capacity(8)],
+            vec![b"A".to_vec(), partition],
+        ];
+
+        for input in inputs {
+            let mut partitions: FFI_AdbcPartitions = input.into();
+            assert!(!partitions.partitions.is_null());
+            assert!(!partitions.partition_lengths.is_null());
+            assert!(!partitions.private_data.is_null());
+
+            if let Some(release) = partitions.release {
+                unsafe { release(&mut partitions) };
+            } else {
+                assert!(partitions.release.is_some(), "expected a release 
callback");
+            }
+
+            assert!(partitions.release.is_none());
+            assert_eq!(partitions.num_partitions, 0);
+            assert!(partitions.partitions.is_null());
+            assert!(partitions.partition_lengths.is_null());
+            assert!(partitions.private_data.is_null());
+            drop(partitions);
+        }
+    }
+
+    #[test]
+    fn test_release_ffi_partitions_idempotent() {
+        let mut partitions: FFI_AdbcPartitions = 
vec![b"partition".to_vec()].into();
+        if let Some(release) = partitions.release {
+            unsafe {
+                release(&mut partitions);
+                release(&mut partitions);
+            }
+        } else {
+            assert!(partitions.release.is_some(), "expected a release 
callback");
+        }
+        assert!(partitions.release.is_none());
+        drop(partitions);
+    }
+
+    #[test]
+    fn test_release_ffi_partitions_null_and_default() {
+        let mut partitions = FFI_AdbcPartitions::default();
+        unsafe {
+            release_ffi_partitions(null_mut());
+            release_ffi_partitions(&mut partitions);
+        }
+        assert!(partitions.release.is_none());
+        assert_eq!(partitions.num_partitions, 0);
+        assert!(partitions.partitions.is_null());
+        assert!(partitions.partition_lengths.is_null());
+        assert!(partitions.private_data.is_null());
+    }
+
     #[test]
     fn test_release_ffi_error_nulls_fields() {
         // Use details to ensure private_data is non-null before release.

Reply via email to