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
70pub 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
90pub 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}