Skip to main content

reifydb_sdk/operator/
view_column.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use postcard::from_bytes;
5use reifydb_abi::data::column::ColumnTypeCode;
6use reifydb_type::value::decimal::Decimal;
7
8use crate::operator::change::{BorrowedColumn, BorrowedColumns};
9
10#[derive(Clone, Copy)]
11pub struct ColumnView<'a> {
12	inner: BorrowedColumn<'a>,
13}
14
15impl<'a> ColumnView<'a> {
16	pub(crate) fn new(inner: BorrowedColumn<'a>) -> Self {
17		Self {
18			inner,
19		}
20	}
21
22	pub fn name(&self) -> &'a str {
23		self.inner.name()
24	}
25
26	pub fn type_code(&self) -> ColumnTypeCode {
27		self.inner.type_code()
28	}
29
30	pub fn len(&self) -> usize {
31		self.inner.row_count()
32	}
33
34	pub fn is_empty(&self) -> bool {
35		self.len() == 0
36	}
37
38	pub fn raw(&self) -> BorrowedColumn<'a> {
39		self.inner
40	}
41
42	pub fn defined_bitvec(&self) -> Option<&'a [u8]> {
43		let bv = self.inner.defined_bitvec();
44		if bv.is_empty() {
45			None
46		} else {
47			Some(bv)
48		}
49	}
50
51	pub fn is_defined(&self, index: usize) -> bool {
52		match self.defined_bitvec() {
53			None => true,
54			Some(bv) => match bv.get(index / 8) {
55				Some(byte) => (byte >> (index % 8)) & 1 == 1,
56				None => false,
57			},
58		}
59	}
60
61	pub fn u8(&self) -> Option<&'a [u8]> {
62		if self.type_code() != ColumnTypeCode::Uint1 {
63			return None;
64		}
65		unsafe { self.inner.as_slice::<u8>() }
66	}
67
68	pub fn u16(&self) -> Option<&'a [u16]> {
69		if self.type_code() != ColumnTypeCode::Uint2 {
70			return None;
71		}
72		unsafe { self.inner.as_slice::<u16>() }
73	}
74
75	pub fn u32(&self) -> Option<&'a [u32]> {
76		if self.type_code() != ColumnTypeCode::Uint4 {
77			return None;
78		}
79		unsafe { self.inner.as_slice::<u32>() }
80	}
81
82	pub fn u64(&self) -> Option<&'a [u64]> {
83		if self.type_code() != ColumnTypeCode::Uint8 {
84			return None;
85		}
86		unsafe { self.inner.as_slice::<u64>() }
87	}
88
89	pub fn u128(&self) -> Option<&'a [u128]> {
90		if self.type_code() != ColumnTypeCode::Uint16 {
91			return None;
92		}
93		unsafe { self.inner.as_slice::<u128>() }
94	}
95
96	pub fn i8(&self) -> Option<&'a [i8]> {
97		if self.type_code() != ColumnTypeCode::Int1 {
98			return None;
99		}
100		unsafe { self.inner.as_slice::<i8>() }
101	}
102
103	pub fn i16(&self) -> Option<&'a [i16]> {
104		if self.type_code() != ColumnTypeCode::Int2 {
105			return None;
106		}
107		unsafe { self.inner.as_slice::<i16>() }
108	}
109
110	pub fn i32(&self) -> Option<&'a [i32]> {
111		if self.type_code() != ColumnTypeCode::Int4 {
112			return None;
113		}
114		unsafe { self.inner.as_slice::<i32>() }
115	}
116
117	pub fn i64(&self) -> Option<&'a [i64]> {
118		if self.type_code() != ColumnTypeCode::Int8 {
119			return None;
120		}
121		unsafe { self.inner.as_slice::<i64>() }
122	}
123
124	pub fn i128(&self) -> Option<&'a [i128]> {
125		if self.type_code() != ColumnTypeCode::Int16 {
126			return None;
127		}
128		unsafe { self.inner.as_slice::<i128>() }
129	}
130
131	pub fn f32(&self) -> Option<&'a [f32]> {
132		if self.type_code() != ColumnTypeCode::Float4 {
133			return None;
134		}
135		unsafe { self.inner.as_slice::<f32>() }
136	}
137
138	pub fn f64(&self) -> Option<&'a [f64]> {
139		if self.type_code() != ColumnTypeCode::Float8 {
140			return None;
141		}
142		unsafe { self.inner.as_slice::<f64>() }
143	}
144
145	pub fn bool_iter(&self) -> Option<BoolIter<'a>> {
146		if self.type_code() != ColumnTypeCode::Bool {
147			return None;
148		}
149		Some(BoolIter {
150			data: self.inner.data_bytes(),
151			row_count: self.inner.row_count(),
152			index: 0,
153		})
154	}
155
156	pub fn utf8_iter(&self) -> Option<impl Iterator<Item = &'a str> + 'a> {
157		if self.type_code() != ColumnTypeCode::Utf8 {
158			return None;
159		}
160		Some(self.inner.iter_str())
161	}
162
163	pub fn blob_iter(&self) -> Option<impl Iterator<Item = &'a [u8]> + 'a> {
164		if self.type_code() != ColumnTypeCode::Blob {
165			return None;
166		}
167		Some(self.inner.iter_bytes())
168	}
169
170	pub fn decimal_iter(&self) -> Option<impl Iterator<Item = Option<Decimal>> + 'a> {
171		if self.type_code() != ColumnTypeCode::Decimal {
172			return None;
173		}
174		let data = self.inner.data_bytes();
175		let offsets = self.inner.offsets();
176		let row_count = self.inner.row_count();
177		Some(DecimalIter {
178			data,
179			offsets,
180			row_count,
181			index: 0,
182		})
183	}
184
185	pub fn to_u64_vec(&self) -> Option<Vec<u64>> {
186		match self.type_code() {
187			ColumnTypeCode::Uint8 => self.u64().map(|s| s.to_vec()),
188			ColumnTypeCode::Uint4 => self.u32().map(|s| s.iter().map(|v| u64::from(*v)).collect()),
189			ColumnTypeCode::Uint2 => self.u16().map(|s| s.iter().map(|v| u64::from(*v)).collect()),
190			ColumnTypeCode::Uint1 => self.u8().map(|s| s.iter().map(|v| u64::from(*v)).collect()),
191			_ => None,
192		}
193	}
194
195	pub fn to_i64_vec(&self) -> Option<Vec<i64>> {
196		match self.type_code() {
197			ColumnTypeCode::Int8 => self.i64().map(|s| s.to_vec()),
198			ColumnTypeCode::Int4 => self.i32().map(|s| s.iter().map(|v| i64::from(*v)).collect()),
199			ColumnTypeCode::Int2 => self.i16().map(|s| s.iter().map(|v| i64::from(*v)).collect()),
200			ColumnTypeCode::Int1 => self.i8().map(|s| s.iter().map(|v| i64::from(*v)).collect()),
201			_ => None,
202		}
203	}
204
205	pub fn to_f64_vec(&self) -> Option<Vec<f64>> {
206		match self.type_code() {
207			ColumnTypeCode::Float8 => self.f64().map(|s| s.to_vec()),
208			ColumnTypeCode::Float4 => self.f32().map(|s| s.iter().map(|v| f64::from(*v)).collect()),
209			_ => None,
210		}
211	}
212}
213
214pub struct BoolIter<'a> {
215	data: &'a [u8],
216	row_count: usize,
217	index: usize,
218}
219
220impl<'a> Iterator for BoolIter<'a> {
221	type Item = bool;
222
223	fn next(&mut self) -> Option<bool> {
224		if self.index >= self.row_count {
225			return None;
226		}
227		let byte = *self.data.get(self.index / 8)?;
228		let bit = (byte >> (self.index % 8)) & 1 == 1;
229		self.index += 1;
230		Some(bit)
231	}
232
233	fn size_hint(&self) -> (usize, Option<usize>) {
234		let remaining = self.row_count - self.index;
235		(remaining, Some(remaining))
236	}
237}
238
239impl<'a> ExactSizeIterator for BoolIter<'a> {}
240
241pub struct DecimalIter<'a> {
242	data: &'a [u8],
243	offsets: &'a [u64],
244	row_count: usize,
245	index: usize,
246}
247
248impl<'a> Iterator for DecimalIter<'a> {
249	type Item = Option<Decimal>;
250
251	fn next(&mut self) -> Option<Option<Decimal>> {
252		if self.index >= self.row_count {
253			return None;
254		}
255		let i = self.index;
256		self.index += 1;
257		if i + 1 >= self.offsets.len() {
258			return Some(None);
259		}
260		let start = self.offsets[i] as usize;
261		let end = self.offsets[i + 1] as usize;
262		if end > self.data.len() || start > end {
263			return Some(None);
264		}
265		Some(from_bytes::<Decimal>(&self.data[start..end]).ok())
266	}
267
268	fn size_hint(&self) -> (usize, Option<usize>) {
269		let remaining = self.row_count - self.index;
270		(remaining, Some(remaining))
271	}
272}
273
274impl<'a> ExactSizeIterator for DecimalIter<'a> {}
275
276impl<'a> BorrowedColumns<'a> {
277	pub fn column_view(&self, name: &str) -> Option<ColumnView<'a>> {
278		self.column(name).map(ColumnView::new)
279	}
280
281	pub fn column_view_at(&self, index: usize) -> Option<ColumnView<'a>> {
282		self.columns().nth(index).map(ColumnView::new)
283	}
284
285	pub fn column_views(&self) -> impl Iterator<Item = ColumnView<'a>> + 'a {
286		self.columns().map(ColumnView::new)
287	}
288}