storage_manager/backend/executor/
seq_scan.rs

1use std::fs::File;
2use std::io::{self};
3
4use crate::catalog::types::Catalog;
5use crate::disk::read_page;
6use crate::page::{ITEM_ID_SIZE, PAGE_HEADER_SIZE, Page};
7use crate::table::page_count;
8use crate::types::DataValue;
9
10pub fn show_tuples(
11    catalog: &Catalog,
12    db_name: &str,
13    table_name: &str,
14    file: &mut File,
15) -> io::Result<()> {
16    // 1. Get schema from catalog
17    let db = catalog.databases.get(db_name).ok_or_else(|| {
18        io::Error::new(
19            io::ErrorKind::NotFound,
20            format!("Database '{}' not found", db_name),
21        )
22    })?;
23
24    let table = db.tables.get(table_name).ok_or_else(|| {
25        io::Error::new(
26            io::ErrorKind::NotFound,
27            format!("Table '{}' not found", table_name),
28        )
29    })?;
30
31    let columns = &table.columns;
32
33    // 2. Read total number of pages
34    let total_pages = page_count(file)?;
35
36    println!("\n=== Tuples in '{}.{}' ===", db_name, table_name);
37    println!("Total pages: {}", total_pages);
38
39    // Print column header
40    let header: Vec<String> = columns
41        .iter()
42        .map(|c| format!("{} ({})", c.name, c.data_type))
43        .collect();
44    println!("{}", header.join(" | "));
45
46    // 3. Loop through each data page (skip page 0 = table header)
47    for page_num in 1..total_pages {
48        let mut page = Page::new();
49        read_page(file, &mut page, page_num)?;
50
51        let lower = u32::from_le_bytes(page.data[0..4].try_into().unwrap());
52        let num_items = (lower - PAGE_HEADER_SIZE) / ITEM_ID_SIZE;
53
54        // 4. For each tuple
55        for i in 0..num_items {
56            let base = (PAGE_HEADER_SIZE + i * ITEM_ID_SIZE) as usize;
57            let offset = u32::from_le_bytes(page.data[base..base + 4].try_into().unwrap());
58            let length = u32::from_le_bytes(page.data[base + 4..base + 8].try_into().unwrap());
59            let tuple_data = &page.data[offset as usize..(offset + length) as usize];
60
61            print!("Tuple {}: ", i + 1);
62
63            // 5. Decode each column using its DataType
64            let mut cursor = 0usize;
65            for col in columns {
66                let field = &tuple_data[cursor..];
67                let sz = match col.data_type.encoded_len(field) {
68                    Ok(sz) => sz,
69                    Err(_) => {
70                        print!("{}=<truncated> ", col.name);
71                        break;
72                    }
73                };
74
75                if cursor + sz <= tuple_data.len() {
76                    match DataValue::from_bytes(&col.data_type, &tuple_data[cursor..cursor + sz]) {
77                        Ok(val) => print!("{}={} ", col.name, val),
78                        Err(_) => {
79                            print!("{}=<decode-error> ", col.name);
80                            break;
81                        }
82                    }
83                    cursor += sz;
84                } else {
85                    print!("{}=<truncated> ", col.name);
86                    break;
87                }
88            }
89            println!();
90        }
91    }
92
93    println!("\n=== End of tuples ===\n");
94    Ok(())
95}