I modify some parts that I did yesterday.

Table in Database should be stored in HashMap. So checking the table is convenient.

!FILENAME component/database.rs

pub struct Database {
    pub name: String,
    pub tables: HashMap<String, Table>,
}

I decide to store all value in String. For the reasons that, (1) the Field has defined the DataType, so we can format the value if we want, (2) DBMS will not use the real value so frequently (only when processing where, but that’s fine to deal in String), and (3) loading and saving data are all in String (actually, string to binary).

Therefore I only remain DataType:

!FILENAME component/datatype.rs

pub enum DataType {
    Char(u8),
    Double,
    Float,
    Int,
    Varchar(u8),
}

Also, I forget that attributes could have default values. So I add a default in Field. Also, I remove DataValue, because Field is only the definition of a table. Therefore I also update table.

!FILENAME component/field.rs

pub struct Field {
    pub name: String,
    pub datatype: DataType,
    pub not_null: bool,
    pub default: Option<String>,
    pub check: Checker,
}

I also update Table a lot. A Table should be able to store rows, and it might be just a part of data from a huge set of table files. So, it needs to know where is the data from, including which page and which range by the cursors.

!FILENAME component/table.rs

pub struct Table {
    /* definition */
    pub name: String,
    pub fields: HashMap<String, Field>, // aka attributes
    pub primary_key: Vec<String>,
    pub foreign_key: Vec<String>,
    pub reference_table: Option<String>,

    /* value */
    pub rows: Vec<Row>,

    /* storage */
    pub page: u64, // which page of this table
    pub cursors: (u64, u64), // cursors of range in a page
}

I cannot design very well at the moment, so I would find more that should be modified as time goes by. In real practice, I believe I am on the right way.