quill_sql/storage/page/
meta_page.rs

1use crate::buffer::{PageId, INVALID_PAGE_ID, PAGE_SIZE};
2use crate::error::{QuillSQLError, QuillSQLResult};
3use crate::storage::codec::CommonCodec;
4use std::sync::LazyLock;
5
6pub static EMPTY_META_PAGE: MetaPage = MetaPage {
7    major_version: 0,
8    minor_version: 0,
9    freelist_page_id: 0,
10    information_schema_schemas_first_page_id: 0,
11    information_schema_tables_first_page_id: 0,
12    information_schema_columns_first_page_id: 0,
13    information_schema_indexes_first_page_id: 0,
14};
15
16pub static META_PAGE_SIZE: LazyLock<usize> = LazyLock::new(|| PAGE_SIZE);
17
18#[derive(Debug, Eq, PartialEq)]
19pub struct MetaPage {
20    pub major_version: u32,
21    pub minor_version: u32,
22    pub freelist_page_id: PageId,
23    pub information_schema_schemas_first_page_id: PageId,
24    pub information_schema_tables_first_page_id: PageId,
25    pub information_schema_columns_first_page_id: PageId,
26    pub information_schema_indexes_first_page_id: PageId,
27}
28
29impl MetaPage {
30    pub fn try_new() -> QuillSQLResult<Self> {
31        let version_str = env!("CARGO_PKG_VERSION");
32        let version_arr = version_str.split('.').collect::<Vec<&str>>();
33        if version_arr.len() < 2 {
34            return Err(QuillSQLError::Storage(format!(
35                "Package version is not xx.xx {}",
36                version_str
37            )));
38        }
39        let major_version = version_arr[0].parse::<u32>().map_err(|_| {
40            QuillSQLError::Storage(format!("Failed to parse major version {}", version_arr[0]))
41        })?;
42        let minor_version = version_arr[1].parse::<u32>().map_err(|_| {
43            QuillSQLError::Storage(format!("Failed to parse minor version {}", version_arr[1]))
44        })?;
45
46        Ok(Self {
47            major_version,
48            minor_version,
49            freelist_page_id: INVALID_PAGE_ID,
50            information_schema_schemas_first_page_id: INVALID_PAGE_ID,
51            information_schema_tables_first_page_id: INVALID_PAGE_ID,
52            information_schema_columns_first_page_id: INVALID_PAGE_ID,
53            information_schema_indexes_first_page_id: INVALID_PAGE_ID,
54        })
55    }
56}
57
58pub fn encode_meta_page(page: &MetaPage) -> Vec<u8> {
59    let mut bytes = Vec::with_capacity(PAGE_SIZE);
60    bytes.extend(CommonCodec::encode_u32(page.major_version));
61    bytes.extend(CommonCodec::encode_u32(page.minor_version));
62    bytes.extend(CommonCodec::encode_u32(page.freelist_page_id));
63    bytes.extend(CommonCodec::encode_u32(
64        page.information_schema_schemas_first_page_id,
65    ));
66    bytes.extend(CommonCodec::encode_u32(
67        page.information_schema_tables_first_page_id,
68    ));
69    bytes.extend(CommonCodec::encode_u32(
70        page.information_schema_columns_first_page_id,
71    ));
72    bytes.extend(CommonCodec::encode_u32(
73        page.information_schema_indexes_first_page_id,
74    ));
75    bytes.resize(PAGE_SIZE, 0);
76    bytes
77}
78
79pub fn decode_meta_page(bytes: &[u8]) -> QuillSQLResult<(MetaPage, usize)> {
80    if bytes.len() != PAGE_SIZE {
81        return Err(QuillSQLError::Storage(format!(
82            "Meta page size is not {} instead of {}",
83            PAGE_SIZE,
84            bytes.len()
85        )));
86    }
87
88    let mut left_bytes = bytes;
89
90    let (major_version, offset) = CommonCodec::decode_u32(left_bytes)?;
91    left_bytes = &left_bytes[offset..];
92    let (minor_version, offset) = CommonCodec::decode_u32(left_bytes)?;
93    left_bytes = &left_bytes[offset..];
94    let (freelist_page_id, offset) = CommonCodec::decode_u32(left_bytes)?;
95    left_bytes = &left_bytes[offset..];
96    let (information_schema_schemas_first_page_id, offset) = CommonCodec::decode_u32(left_bytes)?;
97    left_bytes = &left_bytes[offset..];
98    let (information_schema_tables_first_page_id, offset) = CommonCodec::decode_u32(left_bytes)?;
99    left_bytes = &left_bytes[offset..];
100    let (information_schema_columns_first_page_id, offset) = CommonCodec::decode_u32(left_bytes)?;
101    left_bytes = &left_bytes[offset..];
102    let (information_schema_indexes_first_page_id, offset) = CommonCodec::decode_u32(left_bytes)?;
103    left_bytes = &left_bytes[offset..];
104
105    Ok((
106        MetaPage {
107            major_version,
108            minor_version,
109            freelist_page_id,
110            information_schema_schemas_first_page_id,
111            information_schema_tables_first_page_id,
112            information_schema_columns_first_page_id,
113            information_schema_indexes_first_page_id,
114        },
115        PAGE_SIZE - left_bytes.len(),
116    ))
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn meta_page_codec_roundtrip() {
125        let page = MetaPage::try_new().unwrap();
126        let (decoded, _) = decode_meta_page(&encode_meta_page(&page)).unwrap();
127        assert_eq!(page, decoded);
128    }
129}