1use crate::column::Column;
15use crate::update_info::UpdateInfo;
16use akar_common::error::StorageError;
17use akar_common::types::{PhysicalTypeID, Value};
18use arrow::array::{
19 ArrayRef, BooleanBuilder, Float32Builder, Float64Builder, Int8Builder, Int16Builder, Int32Builder, Int64Builder,
20 StringBuilder, UInt64Builder,
21};
22
23pub const NODE_GROUP_SIZE: usize = 4096;
25
26#[derive(Debug, Clone)]
28pub struct ColumnChunk {
29 values: Vec<Value>,
31 capacity: usize,
33 pub update_info: Option<UpdateInfo>,
36 pub stats: crate::predicate::ColumnChunkStats,
38}
39
40impl ColumnChunk {
41 pub fn new() -> Self {
43 Self {
44 values: Vec::with_capacity(NODE_GROUP_SIZE),
45 capacity: NODE_GROUP_SIZE,
46 update_info: None,
47 stats: crate::predicate::ColumnChunkStats::new(None, None),
48 }
49 }
50
51 pub fn with_capacity(capacity: usize) -> Self {
53 Self {
54 values: Vec::with_capacity(capacity),
55 capacity,
56 update_info: None,
57 stats: crate::predicate::ColumnChunkStats::new(None, None),
58 }
59 }
60
61 pub fn enable_update_info(&mut self) {
63 if self.update_info.is_none() {
64 self.update_info = Some(UpdateInfo::new(self.capacity));
65 }
66 }
67
68 pub fn append(&mut self, value: Value) {
77 self.stats.update(&value);
78 self.values.push(value);
79 }
80
81 pub fn set_value(&mut self, idx: usize, value: Value) -> Result<(), StorageError> {
90 if idx >= self.values.len() {
91 return Err(StorageError::Page(format!(
92 "ColumnChunk index {idx} out of bounds (len={})",
93 self.values.len()
94 )));
95 }
96 self.stats.update(&value);
97 self.values[idx] = value;
98 Ok(())
99 }
100
101 pub fn set_value_with_version(&mut self, idx: usize, value: Value, version: u64) -> Result<(), StorageError> {
109 if idx >= self.values.len() {
110 return Err(StorageError::Page(format!(
111 "ColumnChunk index {idx} out of bounds (len={})",
112 self.values.len()
113 )));
114 }
115 if let Some(ref ui) = self.update_info {
116 let old_data = serialize_value_for_version(&self.values[idx]);
117 ui.append_update(idx as u32, version, old_data);
118 }
119 self.stats.update(&value);
120 self.values[idx] = value;
121 Ok(())
122 }
123
124 pub fn get_value_with_snapshot(
134 &self,
135 idx: usize,
136 snapshot_ts: Option<u64>,
137 _commit_history: &[(u64, u64)],
138 ) -> Option<&Value> {
139 if idx >= self.values.len() {
140 return None;
141 }
142 let ts = match snapshot_ts {
144 Some(ts) => ts,
145 None => return self.values.get(idx),
146 };
147 if let Some(ref ui) = self.update_info {
153 let _ = ui.get_version(idx as u32, ts);
154 }
155 self.values.get(idx)
156 }
157
158 pub fn get_value_owned_with_snapshot(
164 &self,
165 idx: usize,
166 snapshot_ts: Option<u64>,
167 _commit_history: &[(u64, u64)],
168 ) -> Option<Value> {
169 if idx >= self.values.len() {
170 return None;
171 }
172 let ts = match snapshot_ts {
173 Some(ts) => ts,
174 None => return Some(self.values[idx].clone()),
175 };
176 if let Some(ref ui) = self.update_info {
179 if let Some(old_data) = ui.get_version(idx as u32, ts) {
180 if let Ok(old_value) = serde_json::from_slice::<Value>(&old_data) {
181 return Some(old_value);
182 }
183 }
184 }
185 Some(self.values[idx].clone())
187 }
188
189 pub fn num_values(&self) -> usize {
191 self.values.len()
192 }
193
194 pub fn is_full(&self) -> bool {
196 self.values.len() >= self.capacity
197 }
198
199 pub fn is_empty(&self) -> bool {
201 self.values.is_empty()
202 }
203
204 pub fn as_slice(&self) -> &[Value] {
206 &self.values
207 }
208
209 pub fn drain(&mut self) -> Vec<Value> {
211 std::mem::take(&mut self.values)
212 }
213
214 pub fn scan(&self, start: usize, count: usize) -> Vec<Value> {
218 let end = (start + count).min(self.values.len());
219 self.values[start..end].to_vec()
220 }
221
222 pub fn flush_to_column(&mut self, column: &mut Column) -> std::io::Result<usize> {
227 let n = self.values.len();
228 if n == 0 {
229 return Ok(0);
230 }
231
232 let batch = std::mem::take(&mut self.values);
234
235 for value in &batch {
236 column.append_value(value)?;
237 }
238
239 self.values = Vec::with_capacity(self.capacity);
241
242 Ok(n)
243 }
244
245 pub fn flush_copy_to_column(&self, column: &mut Column) -> std::io::Result<usize> {
248 let n = self.values.len();
249 if n == 0 {
250 return Ok(0);
251 }
252
253 for value in &self.values {
254 column.append_value(value)?;
255 }
256
257 Ok(n)
258 }
259
260 pub fn clear(&mut self) {
262 self.values.clear();
263 }
264
265 pub fn remaining(&self) -> usize {
267 self.capacity.saturating_sub(self.values.len())
268 }
269
270 pub fn get(&self, index: usize) -> Option<&Value> {
272 self.values.get(index)
273 }
274
275 pub fn capacity(&self) -> usize {
277 self.capacity
278 }
279
280 pub fn to_arrow_array(&self, phys_type: PhysicalTypeID) -> ArrayRef {
287 let size = self.values.len();
288 match phys_type {
289 PhysicalTypeID::Bool => {
290 let mut builder = BooleanBuilder::with_capacity(size);
291 for v in &self.values {
292 match v {
293 Value::Bool(b) => builder.append_value(*b),
294 _ => builder.append_null(),
295 }
296 }
297 std::sync::Arc::new(builder.finish())
298 }
299 PhysicalTypeID::Int64 => {
300 let mut builder = Int64Builder::with_capacity(size);
301 for v in &self.values {
302 match v {
303 Value::Int64(n) => builder.append_value(*n),
304 Value::Int32(n) => builder.append_value(*n as i64),
305 Value::Int16(n) => builder.append_value(*n as i64),
306 Value::Int8(n) => builder.append_value(*n as i64),
307 Value::UInt64(n) => builder.append_value(*n as i64),
308 Value::UInt32(n) => builder.append_value(*n as i64),
309 Value::UInt16(n) => builder.append_value(*n as i64),
310 Value::UInt8(n) => builder.append_value(*n as i64),
311 Value::Date(n) => builder.append_value(n.0 as i64),
312 Value::Timestamp(n)
313 | Value::TimestampNs(n)
314 | Value::TimestampMs(n)
315 | Value::TimestampSec(n) => builder.append_value(n.0),
316 Value::TimestampTz(n) => builder.append_value(n.0),
317 Value::DTime(n) => builder.append_value(*n),
318 _ => builder.append_null(),
319 }
320 }
321 std::sync::Arc::new(builder.finish())
322 }
323 PhysicalTypeID::UInt64 => {
324 let mut builder = UInt64Builder::with_capacity(size);
325 for v in &self.values {
326 match v {
327 Value::UInt64(n) => builder.append_value(*n),
328 Value::UInt32(n) => builder.append_value(*n as u64),
329 Value::UInt16(n) => builder.append_value(*n as u64),
330 Value::UInt8(n) => builder.append_value(*n as u64),
331 Value::Int64(n) if *n >= 0 => builder.append_value(*n as u64),
332 _ => builder.append_null(),
333 }
334 }
335 std::sync::Arc::new(builder.finish())
336 }
337 PhysicalTypeID::Int32 => {
338 let mut builder = Int32Builder::with_capacity(size);
339 for v in &self.values {
340 match v {
341 Value::Int32(n) => builder.append_value(*n),
342 Value::Int16(n) => builder.append_value(*n as i32),
343 Value::Int8(n) => builder.append_value(*n as i32),
344 _ => builder.append_null(),
345 }
346 }
347 std::sync::Arc::new(builder.finish())
348 }
349 PhysicalTypeID::Int16 => {
350 let mut builder = Int16Builder::with_capacity(size);
351 for v in &self.values {
352 match v {
353 Value::Int16(n) => builder.append_value(*n),
354 Value::Int8(n) => builder.append_value(*n as i16),
355 _ => builder.append_null(),
356 }
357 }
358 std::sync::Arc::new(builder.finish())
359 }
360 PhysicalTypeID::Int8 => {
361 let mut builder = Int8Builder::with_capacity(size);
362 for v in &self.values {
363 match v {
364 Value::Int8(n) => builder.append_value(*n),
365 _ => builder.append_null(),
366 }
367 }
368 std::sync::Arc::new(builder.finish())
369 }
370 PhysicalTypeID::Double => {
371 let mut builder = Float64Builder::with_capacity(size);
372 for v in &self.values {
373 match v {
374 Value::Double(n) => builder.append_value(*n),
375 Value::Float(n) => builder.append_value(*n as f64),
376 Value::Int64(n) => builder.append_value(*n as f64),
377 _ => builder.append_null(),
378 }
379 }
380 std::sync::Arc::new(builder.finish())
381 }
382 PhysicalTypeID::Float => {
383 let mut builder = Float32Builder::with_capacity(size);
384 for v in &self.values {
385 match v {
386 Value::Float(n) => builder.append_value(*n),
387 Value::Double(n) => builder.append_value(*n as f32),
388 _ => builder.append_null(),
389 }
390 }
391 std::sync::Arc::new(builder.finish())
392 }
393 PhysicalTypeID::String => {
394 let mut builder = StringBuilder::with_capacity(size, size * 16);
395 for v in &self.values {
396 match v {
397 Value::String(s) => builder.append_value(s),
398 _ => builder.append_null(),
399 }
400 }
401 std::sync::Arc::new(builder.finish())
402 }
403 PhysicalTypeID::List | PhysicalTypeID::Array | PhysicalTypeID::Struct => {
404 akar_common::arrow_vector::arrow_array_from_values(&self.values)
405 }
406 _ => {
407 let mut builder = Int64Builder::with_capacity(size);
408 for _ in 0..size {
409 builder.append_null();
410 }
411 std::sync::Arc::new(builder.finish())
412 }
413 }
414 }
415}
416
417impl Default for ColumnChunk {
418 fn default() -> Self {
419 Self::new()
420 }
421}
422
423impl From<Vec<Value>> for ColumnChunk {
424 fn from(values: Vec<Value>) -> Self {
425 let capacity = values.len().max(NODE_GROUP_SIZE);
426 let mut stats = crate::predicate::ColumnChunkStats::new(None, None);
427 for v in &values {
428 stats.update(v);
429 }
430 Self {
431 values,
432 capacity,
433 update_info: None,
434 stats,
435 }
436 }
437}
438
439fn serialize_value_for_version(v: &Value) -> Vec<u8> {
445 serde_json::to_vec(v).unwrap_or_else(|_| vec![])
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use crate::column::Column;
455 use crate::page::DEFAULT_PAGE_SIZE;
456 use akar_common::memory::MemoryManager;
457 use akar_common::types::LogicalTypeID;
458
459 use std::sync::{Arc, Mutex};
460
461 fn setup_column(db_path: &std::path::Path) -> Column {
462 let mm = Arc::new(MemoryManager::new(64 * 1024 * 1024));
463 let config = crate::buffer_manager::BufferManagerConfig::default();
464 let bm = Arc::new(Mutex::new(crate::buffer_manager::BufferManager::new(
465 db_path.to_path_buf(),
466 mm,
467 config,
468 )));
469 Column::new(LogicalTypeID::Int64, 0, 0, db_path, bm, DEFAULT_PAGE_SIZE)
470 }
471
472 #[test]
473 fn test_empty_chunk() {
474 let chunk = ColumnChunk::new();
475 assert!(chunk.is_empty());
476 assert_eq!(chunk.num_values(), 0);
477 assert_eq!(chunk.remaining(), NODE_GROUP_SIZE);
478 }
479
480 #[test]
481 fn test_append_and_count() {
482 let mut chunk = ColumnChunk::new();
483 chunk.append(Value::Int64(1));
484 chunk.append(Value::Int64(2));
485 chunk.append(Value::Int64(3));
486 assert_eq!(chunk.num_values(), 3);
487 assert!(!chunk.is_empty());
488 }
489
490 #[test]
491 fn test_is_full() {
492 let mut chunk = ColumnChunk::with_capacity(3);
493 assert!(!chunk.is_full());
494 chunk.append(Value::Int64(1));
495 chunk.append(Value::Int64(2));
496 chunk.append(Value::Int64(3));
497 assert!(chunk.is_full());
498 }
499
500 #[test]
501 fn test_scan() {
502 let mut chunk = ColumnChunk::new();
503 for i in 0..10 {
504 chunk.append(Value::Int64(i));
505 }
506 let scanned = chunk.scan(2, 4);
507 assert_eq!(scanned.len(), 4);
508 assert_eq!(scanned[0], Value::Int64(2));
509 assert_eq!(scanned[3], Value::Int64(5));
510 }
511
512 #[test]
513 fn test_drain() {
514 let mut chunk = ColumnChunk::new();
515 chunk.append(Value::Int64(42));
516 chunk.append(Value::Int64(43));
517 assert_eq!(chunk.num_values(), 2);
518
519 let drained = chunk.drain();
520 assert_eq!(drained.len(), 2);
521 assert!(chunk.is_empty());
522 }
523
524 #[test]
525 fn test_flush_to_column() {
526 let dir = tempfile::tempdir().unwrap();
527 let mut column = setup_column(dir.path());
528 let mut chunk = ColumnChunk::new();
529
530 for i in 0i64..50 {
531 chunk.append(Value::Int64(i));
532 }
533
534 assert_eq!(chunk.num_values(), 50);
535 let flushed = chunk.flush_to_column(&mut column).unwrap();
536 assert_eq!(flushed, 50);
537 assert!(chunk.is_empty());
538 assert_eq!(column.num_values, 50);
539
540 for i in 0i64..50 {
542 let v = column.get_value(i as u64).unwrap();
543 assert_eq!(v, Value::Int64(i));
544 }
545 }
546
547 #[test]
548 fn test_flush_copy_to_column_preserves_buffer() {
549 let dir = tempfile::tempdir().unwrap();
550 let mut column = setup_column(dir.path());
551 let mut chunk = ColumnChunk::new();
552
553 chunk.append(Value::Int64(10));
554 chunk.append(Value::Int64(20));
555
556 let flushed = chunk.flush_copy_to_column(&mut column).unwrap();
557 assert_eq!(flushed, 2);
558 assert_eq!(chunk.num_values(), 2);
560 assert_eq!(column.num_values, 2);
561 }
562
563 #[test]
564 fn test_flush_empty_chunk() {
565 let dir = tempfile::tempdir().unwrap();
566 let mut column = setup_column(dir.path());
567 let mut chunk = ColumnChunk::new();
568
569 let flushed = chunk.flush_to_column(&mut column).unwrap();
570 assert_eq!(flushed, 0);
571 assert!(chunk.is_empty());
572 assert_eq!(column.num_values, 0);
573 }
574
575 #[test]
576 fn test_clear() {
577 let mut chunk = ColumnChunk::new();
578 chunk.append(Value::Int64(99));
579 chunk.clear();
580 assert!(chunk.is_empty());
581 assert_eq!(chunk.num_values(), 0);
582 }
583
584 #[test]
585 fn test_remaining() {
586 let mut chunk = ColumnChunk::with_capacity(10);
587 assert_eq!(chunk.remaining(), 10);
588 chunk.append(Value::Int64(1));
589 assert_eq!(chunk.remaining(), 9);
590 chunk.append(Value::Int64(2));
591 assert_eq!(chunk.remaining(), 8);
592 }
593
594 #[test]
595 fn test_from_vec() {
596 let values = vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)];
597 let chunk = ColumnChunk::from(values);
598 assert_eq!(chunk.num_values(), 3);
599 assert_eq!(chunk.get(0), Some(&Value::Int64(1)));
600 assert_eq!(chunk.get(2), Some(&Value::Int64(3)));
601 }
602
603 #[test]
604 fn test_chunk_default_capacity() {
605 let chunk = ColumnChunk::new();
606 assert_eq!(chunk.capacity(), NODE_GROUP_SIZE);
607 }
608
609 #[test]
610 fn test_multiple_flushes_to_same_column() {
611 let dir = tempfile::tempdir().unwrap();
612 let mut column = setup_column(dir.path());
613 let mut chunk = ColumnChunk::with_capacity(20);
614
615 for i in 0i64..15 {
617 chunk.append(Value::Int64(i));
618 }
619 chunk.flush_to_column(&mut column).unwrap();
620 assert_eq!(column.num_values, 15);
621
622 for i in 15i64..30 {
624 chunk.append(Value::Int64(i));
625 }
626 chunk.flush_to_column(&mut column).unwrap();
627 assert_eq!(column.num_values, 30);
628
629 for i in 0i64..30 {
631 let v = column.get_value(i as u64).unwrap();
632 assert_eq!(v, Value::Int64(i));
633 }
634 }
635
636 #[test]
637 fn test_large_flush() {
638 let dir = tempfile::tempdir().unwrap();
639 let mut column = setup_column(dir.path());
640 let mut chunk = ColumnChunk::new();
641
642 for i in 0..NODE_GROUP_SIZE {
644 chunk.append(Value::Int64(i as i64));
645 }
646 assert!(chunk.is_full());
647
648 let flushed = chunk.flush_to_column(&mut column).unwrap();
649 assert_eq!(flushed, NODE_GROUP_SIZE);
650 assert!(chunk.is_empty());
651 assert_eq!(column.num_values, NODE_GROUP_SIZE as u64);
652
653 assert_eq!(column.get_value(0).unwrap(), Value::Int64(0));
655 let last_idx = (NODE_GROUP_SIZE - 1) as u64;
656 assert_eq!(column.get_value(last_idx).unwrap(), Value::Int64(last_idx as i64));
657 }
658}