Skip to main content

duckdb/core/vector/
flat.rs

1use std::{ffi::CString, marker::PhantomData, slice};
2
3use crate::{
4    Result,
5    core::LogicalTypeHandle,
6    ffi::{
7        duckdb_validity_set_row_invalid, duckdb_vector, duckdb_vector_assign_string_element,
8        duckdb_vector_assign_string_element_len, duckdb_vector_ensure_validity_writable, duckdb_vector_get_column_type,
9        duckdb_vector_get_data, duckdb_vector_get_validity, duckdb_vector_size,
10    },
11};
12
13use super::{try_vector_row_is_null, vector_row_is_null};
14
15/// A flat (contiguous, scalar-row) vector borrowed from a
16/// [`DataChunkHandle`][crate::core::DataChunkHandle].
17pub struct FlatVector<'a> {
18    pub(super) ptr: duckdb_vector,
19    capacity: usize,
20    _phantom: PhantomData<&'a ()>,
21}
22
23impl<'a> FlatVector<'a> {
24    /// Wrap a raw `duckdb_vector` pointer.
25    ///
26    /// # Safety
27    /// `ptr` must be a valid `duckdb_vector` that remains valid for all of `'a`.
28    pub(crate) unsafe fn from_raw(ptr: duckdb_vector) -> Self {
29        Self {
30            ptr,
31            capacity: unsafe { duckdb_vector_size() as usize },
32            _phantom: PhantomData,
33        }
34    }
35
36    /// Wrap a raw vector pointer with a caller-supplied element capacity.
37    ///
38    /// # Safety
39    /// `ptr` must be a valid `duckdb_vector` that remains valid for all of `'a`.
40    /// The caller must ensure `capacity` does not exceed the backing allocation
41    /// for the logical view being exposed. Later typed slice and copy methods
42    /// trust this value for bounds checks.
43    pub(crate) unsafe fn with_capacity(ptr: duckdb_vector, capacity: usize) -> Self {
44        Self {
45            ptr,
46            capacity,
47            _phantom: PhantomData,
48        }
49    }
50
51    /// Returns the capacity of the vector
52    pub fn capacity(&self) -> usize {
53        self.capacity
54    }
55
56    #[cfg(feature = "vtab-arrow")]
57    pub(crate) fn ptr(&self) -> duckdb_vector {
58        self.ptr
59    }
60
61    /// Returns true if the row at the given index is null
62    ///
63    /// # Panics
64    /// Panics if `row` is outside the vector capacity.
65    pub fn row_is_null(&self, row: u64) -> bool {
66        vector_row_is_null(self.ptr, row, self.capacity)
67    }
68
69    /// Returns whether the row is null, or an error if it is out of range.
70    pub fn try_row_is_null(&self, row: u64) -> Result<bool> {
71        try_vector_row_is_null(self.ptr, row, self.capacity)
72    }
73
74    /// Returns a mutable pointer to the vector's backing data cast to `T`.
75    ///
76    /// # Safety
77    /// The caller must ensure `T` matches the DuckDB vector's physical storage.
78    /// Before dereferencing the pointer, the caller must also ensure the target
79    /// element is initialized, in bounds, and not accessed through another
80    /// incompatible or aliased reference.
81    pub unsafe fn as_mut_ptr<T>(&self) -> *mut T {
82        unsafe { duckdb_vector_get_data(self.ptr).cast() }
83    }
84
85    /// Returns a typed slice of the vector.
86    ///
87    /// # Safety
88    /// The caller must ensure `T` matches the DuckDB vector's physical storage,
89    /// the backing allocation is sized for `self.capacity()` elements of `T`,
90    /// each returned element is initialized before it is read, and no `&mut`
91    /// references to the same storage exist for the returned lifetime.
92    pub unsafe fn as_slice<T>(&self) -> &[T] {
93        unsafe { slice::from_raw_parts(self.as_mut_ptr(), self.capacity()) }
94    }
95
96    /// Returns a typed slice of the vector up to a certain length.
97    ///
98    /// # Safety
99    /// The caller must ensure `T` matches the DuckDB vector's physical storage,
100    /// the backing allocation is sized for at least `len` elements of `T`, each
101    /// returned element is initialized before it is read, and no `&mut`
102    /// references to the same storage exist for the returned lifetime.
103    pub unsafe fn as_slice_with_len<T>(&self, len: usize) -> &[T] {
104        unsafe { slice::from_raw_parts(self.as_mut_ptr(), len) }
105    }
106
107    /// Returns a typed mutable slice of the vector.
108    ///
109    /// # Safety
110    /// The caller must ensure `T` matches the DuckDB vector's physical storage,
111    /// the backing allocation is sized for `self.capacity()` elements of `T`,
112    /// and no other references to the same storage exist for the returned
113    /// lifetime.
114    pub unsafe fn as_mut_slice<T>(&mut self) -> &mut [T] {
115        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.capacity()) }
116    }
117
118    /// Returns a typed mutable slice of the vector up to a certain length.
119    ///
120    /// # Safety
121    /// The caller must ensure `T` matches the DuckDB vector's physical storage,
122    /// the backing allocation is sized for at least `len` elements of `T`, and
123    /// no other references to the same storage exist for the returned lifetime.
124    pub unsafe fn as_mut_slice_with_len<T>(&mut self, len: usize) -> &mut [T] {
125        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), len) }
126    }
127
128    /// Returns the logical type of the vector
129    pub fn logical_type(&self) -> LogicalTypeHandle {
130        unsafe { LogicalTypeHandle::new(duckdb_vector_get_column_type(self.ptr)) }
131    }
132
133    /// Set row as null
134    pub fn set_null(&mut self, row: usize) {
135        unsafe {
136            duckdb_vector_ensure_validity_writable(self.ptr);
137            let idx = duckdb_vector_get_validity(self.ptr);
138            duckdb_validity_set_row_invalid(idx, row as u64);
139        }
140    }
141
142    /// Copy typed data to the vector.
143    ///
144    /// # Safety
145    /// The caller must ensure `T` matches the DuckDB vector's physical storage
146    /// and no other references to the same storage exist during the copy.
147    ///
148    /// # Panics
149    /// Panics if `data.len()` exceeds the vector capacity.
150    pub unsafe fn copy<T: Copy>(&mut self, data: &[T]) {
151        assert!(data.len() <= self.capacity());
152        (unsafe { self.as_mut_slice::<T>() })[0..data.len()].copy_from_slice(data);
153    }
154}
155
156/// A trait for inserting data into a vector.
157pub trait Inserter<T> {
158    /// Insert a value into the vector.
159    fn insert(&self, index: usize, value: T);
160}
161
162impl Inserter<CString> for FlatVector<'_> {
163    fn insert(&self, index: usize, value: CString) {
164        unsafe {
165            duckdb_vector_assign_string_element(self.ptr, index as u64, value.as_ptr());
166        }
167    }
168}
169
170impl Inserter<&str> for FlatVector<'_> {
171    fn insert(&self, index: usize, value: &str) {
172        self.insert(index, value.as_bytes());
173    }
174}
175
176impl Inserter<&String> for FlatVector<'_> {
177    fn insert(&self, index: usize, value: &String) {
178        self.insert(index, value.as_str());
179    }
180}
181
182impl Inserter<&[u8]> for FlatVector<'_> {
183    fn insert(&self, index: usize, value: &[u8]) {
184        let value_size = value.len();
185        unsafe {
186            // This function also works for binary data. https://duckdb.org/docs/api/c/api#duckdb_vector_assign_string_element_len
187            duckdb_vector_assign_string_element_len(
188                self.ptr,
189                index as u64,
190                value.as_ptr() as *const ::std::os::raw::c_char,
191                value_size as u64,
192            );
193        }
194    }
195}
196
197impl Inserter<&Vec<u8>> for FlatVector<'_> {
198    fn insert(&self, index: usize, value: &Vec<u8>) {
199        self.insert(index, value.as_slice());
200    }
201}