1use crate::buffer::PAGE_SIZE;
2use crate::error::{QuillSQLError, QuillSQLResult};
3use crate::execution::physical_plan::PhysicalPlan;
4use crate::plan::logical_plan::LogicalPlan;
5use crate::storage::index::btree_index::BPlusTreeIndex;
6use comfy_table::Cell;
7use std::collections::VecDeque;
8
9use crate::storage::page::BPlusTreePage;
10use crate::storage::tuple::Tuple;
11
12pub fn pretty_format_tuples(tuples: &Vec<Tuple>) -> comfy_table::Table {
13 let mut table = comfy_table::Table::new();
14 table.load_preset("||--+-++| ++++++");
15
16 if tuples.is_empty() {
17 return table;
18 }
19
20 let schema = &tuples[0].schema;
21
22 let mut header = Vec::new();
23 for column in schema.columns.iter() {
24 header.push(Cell::new(column.name.clone()));
25 }
26 table.set_header(header);
27
28 for tuple in tuples {
29 let mut cells = Vec::new();
30 for value in tuple.data.iter() {
31 cells.push(Cell::new(format!("{value}")));
32 }
33 table.add_row(cells);
34 }
35
36 table
37}
38
39pub fn pretty_format_logical_plan(plan: &LogicalPlan) -> String {
40 pretty_format_logical_plan_recursively(plan, 0)
41}
42
43fn pretty_format_logical_plan_recursively(plan: &LogicalPlan, indent: usize) -> String {
44 let mut result = format!("{:indent$}{}", "", plan);
45
46 for input in plan.inputs() {
47 result.push('\n');
48 result.push_str(&pretty_format_logical_plan_recursively(input, indent + 2));
49 }
50 result
51}
52
53pub fn pretty_format_physical_plan(plan: &PhysicalPlan) -> String {
54 pretty_format_physical_plan_recursively(plan, 0)
55}
56
57fn pretty_format_physical_plan_recursively(plan: &PhysicalPlan, indent: usize) -> String {
58 let mut result = format!("{:indent$}{}", "", plan);
59
60 for input in plan.inputs() {
61 result.push('\n');
62 result.push_str(&pretty_format_physical_plan_recursively(input, indent + 2));
63 }
64 result
65}
66
67pub fn page_bytes_to_array(bytes: &[u8]) -> [u8; PAGE_SIZE] {
68 let mut data = [0u8; PAGE_SIZE];
69 data.copy_from_slice(bytes);
70 data
71}
72
73#[allow(dead_code)]
74pub(crate) fn pretty_format_index_tree(index: &BPlusTreeIndex) -> QuillSQLResult<String> {
75 let mut display = String::new();
76
77 if index.is_empty()? {
78 display.push_str("Empty tree.");
79 return Ok(display);
80 }
81 let mut curr_queue = VecDeque::new();
83 curr_queue.push_back(index.get_root_page_id()?);
84
85 let mut level_index = 1;
86 loop {
87 if curr_queue.is_empty() {
88 return Ok(display);
89 }
90 let mut next_queue = VecDeque::new();
91
92 display.push_str(&format!("B+ Tree Level No.{}:\n", level_index));
94
95 let mut level_table = comfy_table::Table::new();
96 level_table.load_preset("||--+-++| ++++++");
97 let mut level_header = vec![];
98 let mut level_row = vec![];
99
100 while let Some(page_id) = curr_queue.pop_front() {
101 let (_, curr_page) = index
102 .buffer_pool
103 .fetch_tree_page(page_id, index.key_schema.clone())?;
104
105 match curr_page {
106 BPlusTreePage::Internal(internal_page) => {
107 let mut page_table = comfy_table::Table::new();
109 page_table.load_preset("||--+-++| ++++++");
110 let mut page_header = Vec::new();
111 let mut page_row = Vec::new();
112 for (tuple, page_id) in internal_page.array.iter() {
113 page_header.push(Cell::new(
114 tuple
115 .data
116 .iter()
117 .map(|v| format!("{v}"))
118 .collect::<Vec<_>>()
119 .join(", "),
120 ));
121 page_row.push(Cell::new(page_id));
122 }
123 page_table.set_header(page_header);
124 page_table.add_row(page_row);
125
126 level_header.push(Cell::new(format!(
127 "page_id={}, size: {}/{}",
128 page_id, internal_page.header.current_size, internal_page.header.max_size
129 )));
130 level_row.push(Cell::new(page_table));
131
132 next_queue.extend(internal_page.values());
133 }
134 BPlusTreePage::Leaf(leaf_page) => {
135 let mut page_table = comfy_table::Table::new();
136 page_table.load_preset("||--+-++| ++++++");
137 let mut page_header = Vec::new();
138 let mut page_row = Vec::new();
139 for (tuple, rid) in leaf_page.array.iter() {
140 page_header.push(Cell::new(
141 tuple
142 .data
143 .iter()
144 .map(|v| format!("{v}"))
145 .collect::<Vec<_>>()
146 .join(", "),
147 ));
148 page_row.push(Cell::new(format!("{}-{}", rid.page_id, rid.slot_num)));
149 }
150 page_table.set_header(page_header);
151 page_table.add_row(page_row);
152
153 level_header.push(Cell::new(format!(
154 "page_id={}, size: {}/{}, next_page_id={}",
155 page_id,
156 leaf_page.header.current_size,
157 leaf_page.header.max_size,
158 leaf_page.header.next_page_id
159 )));
160 level_row.push(Cell::new(page_table));
161 }
162 }
163 }
164 level_table.set_header(level_header);
165 level_table.add_row(level_row);
166 display.push_str(&format!("{level_table}\n"));
167
168 level_index += 1;
169 curr_queue = next_queue;
170 }
171}
172
173pub fn time() -> u128 {
174 std::time::SystemTime::now()
175 .duration_since(std::time::UNIX_EPOCH)
176 .unwrap()
177 .as_nanos()
178}
179
180pub fn find_contiguous_diff(a: &[u8], b: &[u8]) -> Option<(usize, usize)> {
183 if a.len() != b.len() {
184 return None;
185 }
186 let mut start = 0usize;
187 while start < a.len() && a[start] == b[start] {
188 start += 1;
189 }
190 if start == a.len() {
191 return None;
192 }
193 let mut end = a.len();
194 while end > start && a[end - 1] == b[end - 1] {
195 end -= 1;
196 }
197 Some((start, end))
198}
199
200pub fn apply_delta_checked(dst: &mut [u8], offset: usize, data: &[u8]) -> bool {
202 if offset >= dst.len() {
203 return false;
204 }
205 match offset.checked_add(data.len()) {
206 Some(end) if end <= dst.len() => {
207 dst[offset..end].copy_from_slice(data);
208 true
209 }
210 _ => false,
211 }
212}
213
214pub fn extract_id_from_filename(entry: &std::path::PathBuf) -> QuillSQLResult<u128> {
215 entry
216 .extension()
217 .ok_or_else(|| {
218 QuillSQLError::Internal(format!("Missing extension (ie. not in format: data.<id>)"))
219 })?
220 .to_str()
221 .ok_or_else(|| {
222 QuillSQLError::Internal("Failed to convert extension to string".to_string())
223 })?
224 .parse::<u128>()
225 .map_err(|e| QuillSQLError::Internal(format!("Failed to parse id: {}", e)))
226}