Skip to main content

duckdb/core/vector/
struct.rs

1use std::marker::PhantomData;
2
3use libduckdb_sys::DuckDbString;
4
5use crate::{
6    Result,
7    core::LogicalTypeHandle,
8    ffi::{
9        duckdb_struct_type_child_count, duckdb_struct_type_child_name, duckdb_struct_vector_get_child,
10        duckdb_validity_set_row_invalid, duckdb_vector, duckdb_vector_ensure_validity_writable,
11        duckdb_vector_get_column_type, duckdb_vector_get_validity, duckdb_vector_size,
12    },
13};
14
15use super::{ArrayVector, FlatVector, ListVector, try_vector_row_is_null, vector_row_is_null};
16
17/// A struct vector borrowed from a [`DataChunkHandle`][crate::core::DataChunkHandle].
18///
19/// Groups one child vector per struct field, all sharing the same row count.
20pub struct StructVector<'a> {
21    ptr: duckdb_vector,
22    capacity: usize,
23    _phantom: PhantomData<&'a ()>,
24}
25
26impl<'a> StructVector<'a> {
27    /// Wrap a raw `duckdb_vector` pointer.
28    ///
29    /// # Safety
30    /// `ptr` must be a valid `duckdb_vector` that remains valid for all of `'a`.
31    pub(crate) unsafe fn from_raw(ptr: duckdb_vector) -> Self {
32        unsafe { Self::from_raw_with_capacity(ptr, duckdb_vector_size() as usize) }
33    }
34
35    /// Wrap a raw struct vector pointer with a caller-supplied row capacity.
36    ///
37    /// # Safety
38    /// `ptr` must be a valid struct `duckdb_vector` that remains valid for all
39    /// of `'a`, and `capacity` must not exceed the backing row allocation.
40    pub(crate) unsafe fn from_raw_with_capacity(ptr: duckdb_vector, capacity: usize) -> Self {
41        Self {
42            ptr,
43            capacity,
44            _phantom: PhantomData,
45        }
46    }
47
48    /// Returns the child by idx in the struct vector.
49    ///
50    /// # Panics
51    /// Panics if `capacity` exceeds the parent vector capacity.
52    pub fn child(&self, idx: usize, capacity: usize) -> FlatVector<'a> {
53        assert!(
54            capacity <= self.capacity,
55            "struct child capacity {capacity} exceeds parent capacity {}",
56            self.capacity
57        );
58        // SAFETY: struct children share the parent allocation, and the check
59        // above bounds the exposed child view by the parent capacity.
60        unsafe { FlatVector::with_capacity(self.child_ptr(idx), capacity) }
61    }
62
63    // Legacy typed child accessors remain public for callers that need nested
64    // vector views.
65
66    /// Take the child as [StructVector].
67    pub fn struct_vector_child(&self, idx: usize) -> StructVector<'a> {
68        unsafe { StructVector::from_raw_with_capacity(self.child_ptr(idx), self.capacity) }
69    }
70
71    /// Take the child as [ListVector].
72    pub fn list_vector_child(&self, idx: usize) -> ListVector<'a> {
73        unsafe { ListVector::from_raw_with_capacity(self.child_ptr(idx), self.capacity) }
74    }
75
76    /// Take the child as [ArrayVector].
77    pub fn array_vector_child(&self, idx: usize) -> ArrayVector<'a> {
78        unsafe { ArrayVector::from_raw_with_capacity(self.child_ptr(idx), self.capacity) }
79    }
80
81    pub(crate) fn child_ptr(&self, idx: usize) -> duckdb_vector {
82        unsafe { duckdb_struct_vector_get_child(self.ptr, idx as u64) }
83    }
84
85    /// Get the logical type of this struct vector.
86    pub fn logical_type(&self) -> LogicalTypeHandle {
87        unsafe { LogicalTypeHandle::new(duckdb_vector_get_column_type(self.ptr)) }
88    }
89
90    /// Returns true if the row at the given index is null.
91    ///
92    /// # Panics
93    /// Panics if `row` is outside the vector capacity.
94    pub fn row_is_null(&self, row: u64) -> bool {
95        vector_row_is_null(self.ptr, row, self.capacity)
96    }
97
98    /// Returns whether the row is null, or an error if it is out of range.
99    pub fn try_row_is_null(&self, row: u64) -> Result<bool> {
100        try_vector_row_is_null(self.ptr, row, self.capacity)
101    }
102
103    /// Get the name of the child by idx.
104    ///
105    /// Panics if `idx` is out of range.
106    pub fn child_name(&self, idx: usize) -> DuckDbString {
107        let logical_type = self.logical_type();
108        unsafe {
109            let child_name_ptr = duckdb_struct_type_child_name(logical_type.ptr, idx as u64);
110            if child_name_ptr.is_null() {
111                panic!("child index {idx} out of range");
112            }
113            DuckDbString::from_ptr(child_name_ptr)
114        }
115    }
116
117    /// Get the number of children.
118    pub fn num_children(&self) -> usize {
119        let logical_type = self.logical_type();
120        unsafe { duckdb_struct_type_child_count(logical_type.ptr) as usize }
121    }
122
123    /// Set row as null
124    pub fn set_null(&mut self, row: usize) {
125        unsafe {
126            duckdb_vector_ensure_validity_writable(self.ptr);
127            let idx = duckdb_vector_get_validity(self.ptr);
128            duckdb_validity_set_row_invalid(idx, row as u64);
129        }
130    }
131}