1use sqlite_decoder::btree;
2use std::collections::HashMap;
3use std::ops::RangeInclusive;
4
5pub type Schemas = HashMap<String, Schema>;
6
7#[derive(Debug)]
8pub enum Schema {
9 Table(Table),
10 Index(Index),
11}
12
13#[derive(Debug, Clone)]
14pub struct Table {
15 pub name: String,
16 pub sql: String,
17 pub root_page: u32,
18}
19
20#[derive(Debug)]
21pub struct PageWithRowidRange {
22 pub range: RangeInclusive<u64>,
23 pub index: u32,
24}
25
26impl Table {
27 pub fn list_pages(&self, db: &sqlite_types::Db) -> Vec<PageWithRowidRange> {
28 let page = db.pages.get(&self.root_page).unwrap();
29 let res = sqlite_decoder::btree::decode(&db.header.text_encoding, page).unwrap();
30
31 let mut page_list = Vec::new();
32
33 let mut prev_rowid = None;
34 for cell in res.cells {
35 match cell {
36 sqlite_decoder::btree::Cell::TableBTreeInteriorCell(cell) => {
37 let start = prev_rowid.unwrap_or_default();
38 let end = cell.rowid;
39
40 page_list.push(PageWithRowidRange {
41 range: RangeInclusive::new(start, end),
42 index: cell.left_child_page,
43 });
44 prev_rowid = Some(end);
47 }
48 _ => unimplemented!(),
49 }
50 }
51
52 page_list
53 }
54}
55
56#[derive(Debug)]
57pub struct Index {
58 pub name: String,
59 pub sql: String,
60 pub tbl_name: String,
61 pub root_page: u32,
62}
63
64pub fn find_table_by_root(rootpage: usize, schemas: &Schemas) -> Option<Table> {
65 let mut table = None;
66
67 for (_, schema) in schemas {
68 match schema {
69 Schema::Table(schema) => {
70 if schema.root_page as usize == rootpage {
71 table = Some(schema.clone());
72 }
73 }
74 _ => {}
75 }
76 }
77
78 table
79}
80
81pub fn decode_sqlite_schema(db: &sqlite_types::Db) -> Schemas {
84 let page = db.pages.get(&1).unwrap();
85
86 let enc = &db.header.text_encoding;
87 let btree = btree::decode_first_page(enc, page).unwrap();
88
89 let mut schemas = HashMap::new();
90
91 for cell in btree.cells {
92 match cell {
93 btree::Cell::TableBTreeLeafCell(leaf) => {
94 let record_type = leaf.records[0].as_string();
95 let name = leaf.records[1].as_string();
96 let tbl_name = leaf.records[2].as_string();
97 let root_page = leaf.records[3].as_int() as u32;
98 let sql = leaf.records[4].as_string();
99
100 let schema = if record_type == "table" {
101 Schema::Table(Table {
102 name: name.clone(),
103 root_page,
104 sql,
105 })
106 } else {
107 Schema::Index(Index {
108 name: name.clone(),
109 root_page,
110 sql,
111 tbl_name,
112 })
113 };
114
115 schemas.insert(name, schema);
116 }
117 _ => unreachable!(),
118 }
119 }
120
121 schemas
122}