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