Skip to main content

akar_common/
vector.rs

1//! LegacyValueVector — typed columnar data array used throughout the query engine.
2
3use crate::types::{PhysicalTypeID, Value};
4
5/// A vector of values of the same physical type.
6/// This is the fundamental columnar data unit in Akar's query execution.
7#[derive(Debug, Clone)]
8pub struct LegacyValueVector {
9    physical_type: PhysicalTypeID,
10    /// The actual data buffer (type-erased byte buffer).
11    data: Vec<u8>,
12    /// Nullability mask (true = not null).
13    null_mask: Vec<bool>,
14    /// Number of elements currently in the vector.
15    size: usize,
16    /// Capacity of the vector (number of elements, not bytes).
17    capacity: usize,
18}
19
20impl LegacyValueVector {
21    pub fn new(physical_type: PhysicalTypeID, capacity: usize) -> Self {
22        let type_size = physical_type_size(physical_type);
23        Self {
24            physical_type,
25            data: vec![0u8; capacity * type_size],
26            null_mask: vec![true; capacity],
27            size: 0,
28            capacity,
29        }
30    }
31
32    #[inline(always)]
33    pub fn physical_type(&self) -> PhysicalTypeID {
34        self.physical_type
35    }
36
37    #[inline(always)]
38    pub fn size(&self) -> usize {
39        self.size
40    }
41
42    #[inline(always)]
43    pub fn capacity(&self) -> usize {
44        self.capacity
45    }
46
47    #[inline(always)]
48    pub fn is_null(&self, idx: usize) -> bool {
49        !self.null_mask[idx]
50    }
51
52    #[inline(always)]
53    pub fn set_null(&mut self, idx: usize, is_null: bool) {
54        self.null_mask[idx] = !is_null;
55    }
56
57    #[inline(always)]
58    pub fn resize(&mut self, new_size: usize) {
59        assert!(new_size <= self.capacity);
60        self.size = new_size;
61    }
62
63    /// Get a reference to the raw data buffer.
64    #[inline(always)]
65    pub fn data(&self) -> &[u8] {
66        &self.data[..self.size * physical_type_size(self.physical_type)]
67    }
68
69    #[inline]
70    pub fn data_mut(&mut self) -> &mut [u8] {
71        let type_size = physical_type_size(self.physical_type);
72        &mut self.data[..self.size * type_size]
73    }
74}
75
76/// Returns the byte size of a given physical type.
77pub const fn physical_type_size(t: PhysicalTypeID) -> usize {
78    match t {
79        PhysicalTypeID::Bool => 1,
80        PhysicalTypeID::Int8 | PhysicalTypeID::UInt8 => 1,
81        PhysicalTypeID::Int16 | PhysicalTypeID::UInt16 => 2,
82        PhysicalTypeID::Int32 | PhysicalTypeID::UInt32 | PhysicalTypeID::Float => 4,
83        PhysicalTypeID::Int64 | PhysicalTypeID::UInt64 | PhysicalTypeID::Double | PhysicalTypeID::Interval => 8,
84        PhysicalTypeID::Int128 => 16,
85        PhysicalTypeID::String => 256, // inline string up to 255 chars for prototype
86        PhysicalTypeID::Struct => 8,   // pointer to struct data
87        PhysicalTypeID::List | PhysicalTypeID::Array => 16, // list header
88        PhysicalTypeID::Blob => 256,
89        PhysicalTypeID::Any => 1,
90    }
91}
92
93// --- Typed getters/setters ---
94
95impl LegacyValueVector {
96    /// Get an i64 value at index.
97    #[inline]
98    pub fn get_i64(&self, idx: usize) -> Option<i64> {
99        if self.is_null(idx) {
100            return None;
101        }
102        let type_size = physical_type_size(self.physical_type);
103        let offset = idx * type_size;
104        let mut buf = [0u8; 8];
105        buf.copy_from_slice(&self.data[offset..offset + 8]);
106        Some(i64::from_le_bytes(buf))
107    }
108
109    /// Set an i64 value at index.
110    #[inline]
111    pub fn set_i64(&mut self, idx: usize, val: i64) {
112        let type_size = physical_type_size(self.physical_type);
113        let offset = idx * type_size;
114        self.data[offset..offset + 8].copy_from_slice(&val.to_le_bytes());
115        self.null_mask[idx] = true;
116        if idx >= self.size {
117            self.size = idx + 1;
118        }
119    }
120
121    /// Get an i32 value at index.
122    #[inline]
123    pub fn get_i32(&self, idx: usize) -> Option<i32> {
124        if self.is_null(idx) {
125            return None;
126        }
127        let type_size = physical_type_size(self.physical_type);
128        let offset = idx * type_size;
129        let mut buf = [0u8; 4];
130        buf.copy_from_slice(&self.data[offset..offset + 4]);
131        Some(i32::from_le_bytes(buf))
132    }
133
134    /// Set an i32 value at index.
135    #[inline]
136    pub fn set_i32(&mut self, idx: usize, val: i32) {
137        let type_size = physical_type_size(self.physical_type);
138        let offset = idx * type_size;
139        self.data[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
140        self.null_mask[idx] = true;
141        if idx >= self.size {
142            self.size = idx + 1;
143        }
144    }
145
146    /// Get an f64 (double) value at index.
147    #[inline]
148    pub fn get_double(&self, idx: usize) -> Option<f64> {
149        if self.is_null(idx) {
150            return None;
151        }
152        let type_size = physical_type_size(self.physical_type);
153        let offset = idx * type_size;
154        let mut buf = [0u8; 8];
155        buf.copy_from_slice(&self.data[offset..offset + 8]);
156        Some(f64::from_le_bytes(buf))
157    }
158
159    /// Set an f64 (double) value at index.
160    #[inline]
161    pub fn set_double(&mut self, idx: usize, val: f64) {
162        let type_size = physical_type_size(self.physical_type);
163        let offset = idx * type_size;
164        self.data[offset..offset + 8].copy_from_slice(&val.to_le_bytes());
165        self.null_mask[idx] = true;
166        if idx >= self.size {
167            self.size = idx + 1;
168        }
169    }
170}
171
172impl LegacyValueVector {
173    /// Get a Value enum from this vector at a given row index.
174    /// This converts the raw byte buffer into the appropriate Value variant.
175    pub fn get_value(&self, idx: usize) -> Option<Value> {
176        if idx >= self.size || self.is_null(idx) {
177            return None;
178        }
179        let type_size = physical_type_size(self.physical_type);
180        let offset = idx * type_size;
181        match self.physical_type {
182            PhysicalTypeID::Bool => Some(Value::Bool(self.data[offset] != 0)),
183            PhysicalTypeID::Int64 => {
184                let mut buf = [0u8; 8];
185                buf.copy_from_slice(&self.data[offset..offset + 8]);
186                Some(Value::Int64(i64::from_le_bytes(buf)))
187            }
188            PhysicalTypeID::Int32 => {
189                let mut buf = [0u8; 4];
190                buf.copy_from_slice(&self.data[offset..offset + 4]);
191                Some(Value::Int32(i32::from_le_bytes(buf)))
192            }
193            PhysicalTypeID::Int16 => {
194                let mut buf = [0u8; 2];
195                buf.copy_from_slice(&self.data[offset..offset + 2]);
196                Some(Value::Int16(i16::from_le_bytes(buf)))
197            }
198            PhysicalTypeID::Int8 => Some(Value::Int8(self.data[offset] as i8)),
199            PhysicalTypeID::UInt64 => {
200                let mut buf = [0u8; 8];
201                buf.copy_from_slice(&self.data[offset..offset + 8]);
202                Some(Value::UInt64(u64::from_le_bytes(buf)))
203            }
204            PhysicalTypeID::UInt32 => {
205                let mut buf = [0u8; 4];
206                buf.copy_from_slice(&self.data[offset..offset + 4]);
207                Some(Value::UInt32(u32::from_le_bytes(buf)))
208            }
209            PhysicalTypeID::UInt16 => {
210                let mut buf = [0u8; 2];
211                buf.copy_from_slice(&self.data[offset..offset + 2]);
212                Some(Value::UInt16(u16::from_le_bytes(buf)))
213            }
214            PhysicalTypeID::UInt8 => Some(Value::UInt8(self.data[offset])),
215            PhysicalTypeID::Double => {
216                let mut buf = [0u8; 8];
217                buf.copy_from_slice(&self.data[offset..offset + 8]);
218                Some(Value::Double(f64::from_le_bytes(buf)))
219            }
220            PhysicalTypeID::Float => {
221                let mut buf = [0u8; 4];
222                buf.copy_from_slice(&self.data[offset..offset + 4]);
223                Some(Value::Float(f32::from_le_bytes(buf)))
224            }
225            PhysicalTypeID::String => {
226                let len = self.data[offset] as usize;
227                let s = String::from_utf8_lossy(&self.data[offset + 1..offset + 1 + len.min(255)]).to_string();
228                Some(Value::String(s))
229            }
230            // For struct/list types, return a simplified representation
231            PhysicalTypeID::Struct => Some(Value::Struct(Vec::new())),
232            PhysicalTypeID::List => Some(Value::List(Vec::new())),
233            _ => None,
234        }
235    }
236
237    /// Set a `Value` at the given row index.
238    ///
239    /// Converts the `Value` variant to the vector's physical type.
240    /// Returns an error if the type cannot be converted.
241    pub fn set_value(&mut self, idx: usize, val: &Value) -> Result<(), String> {
242        match (self.physical_type, val) {
243            (_, Value::Null) => {
244                self.set_null(idx, true);
245                if idx >= self.size {
246                    self.size = idx + 1;
247                }
248                Ok(())
249            }
250            (PhysicalTypeID::Bool, Value::Bool(b)) => {
251                let byte: u8 = if *b { 1 } else { 0 };
252                self.data[idx] = byte;
253                self.null_mask[idx] = true;
254                if idx >= self.size {
255                    self.size = idx + 1;
256                }
257                Ok(())
258            }
259            (PhysicalTypeID::Int64, Value::Int64(v)) => {
260                self.set_i64(idx, *v);
261                Ok(())
262            }
263            (PhysicalTypeID::Int64, Value::Int32(v)) => {
264                self.set_i64(idx, *v as i64);
265                Ok(())
266            }
267            (PhysicalTypeID::Int64, Value::Int16(v)) => {
268                self.set_i64(idx, *v as i64);
269                Ok(())
270            }
271            (PhysicalTypeID::Int64, Value::Int8(v)) => {
272                self.set_i64(idx, *v as i64);
273                Ok(())
274            }
275            (PhysicalTypeID::Int64, Value::UInt64(v)) => {
276                self.set_i64(idx, *v as i64);
277                Ok(())
278            }
279            (PhysicalTypeID::Int64, Value::UInt32(v)) => {
280                self.set_i64(idx, *v as i64);
281                Ok(())
282            }
283            (PhysicalTypeID::Int64, Value::UInt16(v)) => {
284                self.set_i64(idx, *v as i64);
285                Ok(())
286            }
287            (PhysicalTypeID::Int64, Value::UInt8(v)) => {
288                self.set_i64(idx, *v as i64);
289                Ok(())
290            }
291            (PhysicalTypeID::Int64, Value::Double(v)) => {
292                self.set_i64(idx, *v as i64);
293                Ok(())
294            }
295            (PhysicalTypeID::Int64, Value::Float(v)) => {
296                self.set_i64(idx, *v as i64);
297                Ok(())
298            }
299            (PhysicalTypeID::Int64, Value::Date(v)) => {
300                self.set_i64(idx, v.0 as i64);
301                Ok(())
302            }
303            (PhysicalTypeID::Int64, Value::Timestamp(v))
304            | (PhysicalTypeID::Int64, Value::TimestampNs(v))
305            | (PhysicalTypeID::Int64, Value::TimestampMs(v))
306            | (PhysicalTypeID::Int64, Value::TimestampSec(v)) => {
307                self.set_i64(idx, v.0);
308                Ok(())
309            }
310            (PhysicalTypeID::Int64, Value::TimestampTz(v)) => {
311                self.set_i64(idx, v.0);
312                Ok(())
313            }
314            (PhysicalTypeID::Int64, Value::DTime(v)) => {
315                self.set_i64(idx, *v);
316                Ok(())
317            }
318            (PhysicalTypeID::Int32, Value::Int32(v)) => {
319                self.set_i32(idx, *v);
320                Ok(())
321            }
322            (PhysicalTypeID::Int32, Value::Int16(v)) => {
323                self.set_i32(idx, *v as i32);
324                Ok(())
325            }
326            (PhysicalTypeID::Int32, Value::Int8(v)) => {
327                self.set_i32(idx, *v as i32);
328                Ok(())
329            }
330            (PhysicalTypeID::Int32, Value::Int64(v)) => {
331                self.set_i32(idx, *v as i32);
332                Ok(())
333            }
334            (PhysicalTypeID::Double, Value::Double(v)) => {
335                self.set_double(idx, *v);
336                Ok(())
337            }
338            (PhysicalTypeID::Double, Value::Float(v)) => {
339                self.set_double(idx, *v as f64);
340                Ok(())
341            }
342            (PhysicalTypeID::Double, Value::Int64(v)) => {
343                self.set_double(idx, *v as f64);
344                Ok(())
345            }
346            (PhysicalTypeID::Float, Value::Float(v)) => {
347                let type_size = physical_type_size(self.physical_type);
348                let offset = idx * type_size;
349                self.data[offset..offset + 4].copy_from_slice(&v.to_le_bytes());
350                self.null_mask[idx] = true;
351                if idx >= self.size {
352                    self.size = idx + 1;
353                }
354                Ok(())
355            }
356            (PhysicalTypeID::Float, Value::Double(v)) => {
357                let type_size = physical_type_size(self.physical_type);
358                let offset = idx * type_size;
359                self.data[offset..offset + 4].copy_from_slice(&(*v as f32).to_le_bytes());
360                self.null_mask[idx] = true;
361                if idx >= self.size {
362                    self.size = idx + 1;
363                }
364                Ok(())
365            }
366            (PhysicalTypeID::String, Value::String(s)) => {
367                let bytes = s.as_bytes();
368                if bytes.len() > 255 {
369                    return Err(format!(
370                        "Cannot store string of {} bytes: inline string storage limit is 255 bytes",
371                        bytes.len()
372                    ));
373                }
374                let type_size = physical_type_size(self.physical_type);
375                let offset = idx * type_size;
376                self.data[offset] = bytes.len() as u8;
377                self.data[offset + 1..offset + 1 + bytes.len()].copy_from_slice(bytes);
378                self.null_mask[idx] = true;
379                if idx >= self.size {
380                    self.size = idx + 1;
381                }
382                Ok(())
383            }
384            // UInt64 — accept UInt64 or Int64 values
385            (PhysicalTypeID::UInt64, Value::UInt64(v)) => {
386                let type_size = physical_type_size(self.physical_type);
387                let offset = idx * type_size;
388                self.data[offset..offset + 8].copy_from_slice(&v.to_le_bytes());
389                self.null_mask[idx] = true;
390                if idx >= self.size {
391                    self.size = idx + 1;
392                }
393                Ok(())
394            }
395            (PhysicalTypeID::UInt64, Value::Int64(v)) => {
396                let type_size = physical_type_size(self.physical_type);
397                let offset = idx * type_size;
398                self.data[offset..offset + 8].copy_from_slice(&(*v as u64).to_le_bytes());
399                self.null_mask[idx] = true;
400                if idx >= self.size {
401                    self.size = idx + 1;
402                }
403                Ok(())
404            }
405            // UInt32 — accept UInt32 or Int32 values
406            (PhysicalTypeID::UInt32, Value::UInt32(v)) => {
407                let type_size = physical_type_size(self.physical_type);
408                let offset = idx * type_size;
409                self.data[offset..offset + 4].copy_from_slice(&v.to_le_bytes());
410                self.null_mask[idx] = true;
411                if idx >= self.size {
412                    self.size = idx + 1;
413                }
414                Ok(())
415            }
416            (PhysicalTypeID::UInt32, Value::Int32(v)) => {
417                let type_size = physical_type_size(self.physical_type);
418                let offset = idx * type_size;
419                self.data[offset..offset + 4].copy_from_slice(&(*v as u32).to_le_bytes());
420                self.null_mask[idx] = true;
421                if idx >= self.size {
422                    self.size = idx + 1;
423                }
424                Ok(())
425            }
426            // UInt16 — accept UInt16 or Int16 values
427            (PhysicalTypeID::UInt16, Value::UInt16(v)) => {
428                let type_size = physical_type_size(self.physical_type);
429                let offset = idx * type_size;
430                self.data[offset..offset + 2].copy_from_slice(&v.to_le_bytes());
431                self.null_mask[idx] = true;
432                if idx >= self.size {
433                    self.size = idx + 1;
434                }
435                Ok(())
436            }
437            (PhysicalTypeID::UInt16, Value::Int16(v)) => {
438                let type_size = physical_type_size(self.physical_type);
439                let offset = idx * type_size;
440                self.data[offset..offset + 2].copy_from_slice(&(*v as u16).to_le_bytes());
441                self.null_mask[idx] = true;
442                if idx >= self.size {
443                    self.size = idx + 1;
444                }
445                Ok(())
446            }
447            // UInt8 — accept UInt8 or Int8 values
448            (PhysicalTypeID::UInt8, Value::UInt8(v)) => {
449                self.data[idx] = *v;
450                self.null_mask[idx] = true;
451                if idx >= self.size {
452                    self.size = idx + 1;
453                }
454                Ok(())
455            }
456            (PhysicalTypeID::UInt8, Value::Int8(v)) => {
457                self.data[idx] = *v as u8;
458                self.null_mask[idx] = true;
459                if idx >= self.size {
460                    self.size = idx + 1;
461                }
462                Ok(())
463            }
464            // Int16
465            (PhysicalTypeID::Int16, Value::Int16(v)) => {
466                let type_size = physical_type_size(self.physical_type);
467                let offset = idx * type_size;
468                self.data[offset..offset + 2].copy_from_slice(&v.to_le_bytes());
469                self.null_mask[idx] = true;
470                if idx >= self.size {
471                    self.size = idx + 1;
472                }
473                Ok(())
474            }
475            // Int8
476            (PhysicalTypeID::Int8, Value::Int8(v)) => {
477                self.data[idx] = *v as u8;
478                self.null_mask[idx] = true;
479                if idx >= self.size {
480                    self.size = idx + 1;
481                }
482                Ok(())
483            }
484            _ => Err(format!(
485                "Cannot set value {:?} into vector of type {:?}",
486                val, self.physical_type
487            )),
488        }
489    }
490
491    /// Push a boolean value to the end of the vector.
492    #[inline]
493    pub fn push_bool(&mut self, val: bool) {
494        let idx = self.size;
495        let byte: u8 = if val { 1 } else { 0 };
496        self.data[idx] = byte;
497        self.null_mask[idx] = true;
498        self.size += 1;
499    }
500
501    /// Get a boolean value at index.
502    #[inline]
503    pub fn get_bool(&self, idx: usize) -> Option<bool> {
504        if self.is_null(idx) {
505            return None;
506        }
507        Some(self.data[idx] != 0)
508    }
509
510    /// Push a string value (stores as inline bytes for now).
511    /// Returns an error if the string exceeds the 255-byte inline storage limit.
512    #[inline]
513    pub fn push_string(&mut self, val: &str) -> Result<(), String> {
514        let idx = self.size;
515        let bytes = val.as_bytes();
516        if bytes.len() > 255 {
517            return Err(format!(
518                "Cannot store string of {} bytes: inline string storage limit is 255 bytes",
519                bytes.len()
520            ));
521        }
522        self.data[idx * 256] = bytes.len() as u8;
523        self.data[idx * 256 + 1..idx * 256 + 1 + bytes.len()].copy_from_slice(bytes);
524        self.null_mask[idx] = true;
525        self.size += 1;
526        Ok(())
527    }
528
529    /// Append a value from another vector (for DataChunk operations).
530    pub fn append(&mut self, other: &LegacyValueVector) {
531        let start = self.size;
532        let count = other.size;
533        let type_size = physical_type_size(self.physical_type);
534        let bytes_to_copy = count * type_size;
535        if start * type_size + bytes_to_copy > self.data.len() {
536            self.data.resize((start + count) * type_size, 0);
537            self.null_mask.resize(start + count, true);
538            self.capacity = start + count;
539        }
540        self.data[start * type_size..start * type_size + bytes_to_copy].copy_from_slice(&other.data[..bytes_to_copy]);
541        for i in 0..count {
542            self.null_mask[start + i] = other.null_mask[i];
543        }
544        self.size = start + count;
545    }
546}
547
548/// Type alias for backward compatibility during Arrow migration.
549/// Use `ValueVector` in existing code; new code should prefer `Vector` from arrow_vector.
550pub type ValueVector = LegacyValueVector;
551
552/// Re-export DataChunk from its own module.
553pub use crate::data_chunk::DataChunk;
554
555#[cfg(test)]
556mod tests {
557    use super::*;
558    use crate::types::Value;
559
560    #[test]
561    fn set_value_string_overflow_returns_error() {
562        let mut v = LegacyValueVector::new(PhysicalTypeID::String, 1);
563        let long = "a".repeat(256);
564        let err = v.set_value(0, &Value::String(long)).unwrap_err();
565        assert!(err.contains("255"), "err: {err}");
566    }
567
568    #[test]
569    fn set_value_string_exact_255_round_trips() {
570        let mut v = LegacyValueVector::new(PhysicalTypeID::String, 1);
571        let s = "a".repeat(255);
572        v.set_value(0, &Value::String(s.clone())).unwrap();
573        assert_eq!(v.get_value(0), Some(Value::String(s)));
574    }
575
576    #[test]
577    fn push_string_overflow_returns_error() {
578        let mut v = LegacyValueVector::new(PhysicalTypeID::String, 1);
579        let err = v.push_string(&"a".repeat(256)).unwrap_err();
580        assert!(err.contains("255"), "err: {err}");
581        assert_eq!(v.size(), 0);
582    }
583}