Skip to main content

quill_sql/utils/
util.rs

1use crate::error::{QuillSQLError, QuillSQLResult};
2use crate::execution::physical_plan::PhysicalPlan;
3use crate::plan::logical_plan::LogicalPlan;
4use comfy_table::Cell;
5
6use crate::storage::tuple::Tuple;
7
8pub fn pretty_format_tuples(tuples: &Vec<Tuple>) -> comfy_table::Table {
9    let mut table = comfy_table::Table::new();
10    table.load_preset("||--+-++|    ++++++");
11
12    if tuples.is_empty() {
13        return table;
14    }
15
16    let schema = &tuples[0].schema;
17
18    let mut header = Vec::new();
19    for column in schema.columns.iter() {
20        header.push(Cell::new(column.name.clone()));
21    }
22    table.set_header(header);
23
24    for tuple in tuples {
25        let mut cells = Vec::new();
26        for value in tuple.data.iter() {
27            cells.push(Cell::new(format!("{value}")));
28        }
29        table.add_row(cells);
30    }
31
32    table
33}
34
35pub fn pretty_format_logical_plan(plan: &LogicalPlan) -> String {
36    pretty_format_logical_plan_recursively(plan, 0)
37}
38
39fn pretty_format_logical_plan_recursively(plan: &LogicalPlan, indent: usize) -> String {
40    let mut result = format!("{:indent$}{}", "", plan);
41
42    for input in plan.inputs() {
43        result.push('\n');
44        result.push_str(&pretty_format_logical_plan_recursively(input, indent + 2));
45    }
46    result
47}
48
49pub fn pretty_format_physical_plan(plan: &PhysicalPlan) -> String {
50    pretty_format_physical_plan_recursively(plan, 0)
51}
52
53fn pretty_format_physical_plan_recursively(plan: &PhysicalPlan, indent: usize) -> String {
54    let mut result = format!("{:indent$}{}", "", plan);
55
56    for input in plan.inputs() {
57        result.push('\n');
58        result.push_str(&pretty_format_physical_plan_recursively(input, indent + 2));
59    }
60    result
61}
62
63pub fn time() -> u128 {
64    std::time::SystemTime::now()
65        .duration_since(std::time::UNIX_EPOCH)
66        .unwrap()
67        .as_nanos()
68}
69
70/// Compute the minimal contiguous diff window between two equal-length slices.
71/// Returns Some((start, end)) where [start, end) is the changed window; None if identical.
72pub fn find_contiguous_diff(a: &[u8], b: &[u8]) -> Option<(usize, usize)> {
73    if a.len() != b.len() {
74        return None;
75    }
76    let mut start = 0usize;
77    while start < a.len() && a[start] == b[start] {
78        start += 1;
79    }
80    if start == a.len() {
81        return None;
82    }
83    let mut end = a.len();
84    while end > start && a[end - 1] == b[end - 1] {
85        end -= 1;
86    }
87    Some((start, end))
88}
89
90/// Apply a delta to dst at given offset, returns false if OOB.
91pub fn apply_delta_checked(dst: &mut [u8], offset: usize, data: &[u8]) -> bool {
92    if offset >= dst.len() {
93        return false;
94    }
95    match offset.checked_add(data.len()) {
96        Some(end) if end <= dst.len() => {
97            dst[offset..end].copy_from_slice(data);
98            true
99        }
100        _ => false,
101    }
102}
103
104pub fn extract_id_from_filename(entry: &std::path::Path) -> QuillSQLResult<u128> {
105    entry
106        .extension()
107        .ok_or_else(|| {
108            QuillSQLError::Internal("Missing extension (ie. not in format: data.<id>)".to_string())
109        })?
110        .to_str()
111        .ok_or_else(|| {
112            QuillSQLError::Internal("Failed to convert extension to string".to_string())
113        })?
114        .parse::<u128>()
115        .map_err(|e| QuillSQLError::Internal(format!("Failed to parse id: {}", e)))
116}