tablestg 0.4.7

Storage for database tables
Documentation
use crate::*;
use pstd::veca;
use std::hash::Hasher;

// obj inner enum values
const SCHEMA: usize = 0;
const TABLE: usize = 1;

// obj field numbers
const OBJ_ID: usize = 0;
const _OBJ_SCHEMA: usize = 1;
const OBJ_NAME: usize = 2;
const OBJ_INNER: usize = 3;

/// DataType for sys object ( Schema, Table, Index, Function etc. )
pub fn make_obj_dt() -> DataType {
    use pstd::veca;

    let objenum = DataType::Enum(veca![
        (LString::from("Schema"), DataType::Empty),
        (LString::from("Table"), table_dt()),
    ]);

    DataType::Struct(veca![
        (LString::from("Id"), DataType::Int),
        (LString::from("Schema"), DataType::Int),
        (LString::from("Name"), DataType::String),
        (LString::from("Inner"), objenum),
    ])
}

/// Can be used for any record which has id as first elem of list.
pub struct IdKey<'a> {
    pub rid: i64,
    pub dt: &'a DataType,
}

impl <'a> IdKey<'a> {
    pub fn insert(&self, r: &Value, main: &mut HashMapInfo, ps: &mut PageSet ) {
        // Store the record.
        let addr = VarValAddr::new(self.dt, r, ps);

        // Associate the id with the addr.
        let mut hm = HashMap::restore(*main, ps);
        hm.insert(self, addr);
        if hm.root_changed() {
           *main = hm.save();
        }
    }
}

impl<'a> Hash for IdKey<'a> {
    fn hash<H: Hasher>(&self, h: &mut H) {
        self.rid.hash(h);
    }
}

impl<'a> Key<VarValAddr> for IdKey<'a> {
    fn ok(&self, addr: VarValAddr, ps: &mut PageSet) -> Option<(VarValAddr, Value)> {
        let v = addr.get(self.dt, ps);
        let rid = v.list()[0].int();
        if rid == self.rid {
            return Some((addr, v));
        }
        None
    }
}

/// Get obj address and value from specified id.
pub fn get_obj(rid: i64, ps: &mut PageSet) -> Option<(VarValAddr, Value)> {
    let dt = ps.obj_dt.clone();
    let key = IdKey { rid, dt: &dt };
    HashMap::restore(ps.obj_map, ps).get(&key)
}

#[derive(Hash)]
pub struct ObjNameKey<'a> {
    name: &'a str,
}

impl<'a> Key<i64> for ObjNameKey<'a> {
    fn ok(&self, addr: i64, ps: &mut PageSet) -> Option<(i64, Value)> {
        let (_, v) = get_obj(addr, ps).unwrap();
        let name = v.list()[OBJ_NAME].string();
        if self.name == name {
            return Some((addr, v));
        }
        None
    }
}

pub fn list_obj_all(ps: &mut PageSet) {
    let mut hm = HashMap::<VarValAddr>::restore(ps.obj_map, ps);

    let mut x = hm.all_addr();

    x.sort();

    let dt = ps.obj_dt.clone();

    for addr in x {
        let r = addr.get(&dt, ps);
        println!("list_obj_all addr={:?} r={:?}", addr, r);
    }
}

/// Save new object and save the address in primary object map.
fn create_obj_inner(rid: i64, r: Value, ps: &mut PageSet) {
    let dt = ps.obj_dt.clone();

    let addr = VarValAddr::new(&dt, &r, ps);

    let key = IdKey { rid, dt: &dt };

    let mut hm = HashMap::restore(ps.obj_map, ps);
    hm.insert(&key, addr);
    if hm.root_changed() {
        ps.obj_map = hm.save();
    }
}

/// Map the given object name to the object id.
fn index_obj_by_name(id: i64, name: &str, ps: &mut PageSet) {
    let key = ObjNameKey { name };
    let mut hm = HashMap::restore(ps.obj_by_name, ps);
    hm.insert(&key, id);
    if hm.root_changed() {
        ps.obj_by_name = hm.save();
    }
}

/// Create obj with specified id, name and inner value.
pub fn create_obj(rid: i64, name: &str, inner: Value, ps: &mut PageSet) {
    let r = Value::List(veca![
        Value::Int(rid), // obj id
        Value::Int(0),   // obj schema
        Value::String(LString::from(name)),
        inner
    ]);

    create_obj_inner(rid, r, ps);
    index_obj_by_name(rid, name, ps);
}

/// Get the object id and Value for the supplied object name.
pub fn get_obj_from_name(name: &str, ps: &mut PageSet) -> Option<(i64, Value)> {
    let key = ObjNameKey { name };

    let mut hm = HashMap::restore(ps.obj_by_name, ps);
    if let Some(x) = hm.get(&key) {
        return Some(x);
    }
    None
}

/// Save ( update ) the Value associated with the given address.
pub fn save_obj(addr: VarValAddr, obj: Value, ps: &mut PageSet) {
    let dt = ps.obj_dt.clone();
    let addr2 = addr.update(&dt, &obj, ps);
    if addr2 != addr {
        let rid = obj.list()[OBJ_ID].int();
        let key = IdKey { rid, dt: &dt };

        let mut hm = HashMap::restore(ps.obj_map, ps);
        hm.remove(&key);
        hm.insert(&key, addr2);
        if hm.root_changed() {
            ps.obj_map = hm.save();
        }
    }
}

/// ToDo
pub fn create_schema(name: &str, ps: &mut PageSet) -> Option<(i64, Value)> {
    // Check if obj already exists.
    let obj = get_obj_from_name(name, ps);

    if obj.is_some() {
        obj
    } else {
        let rid = ps.next_obj_id;
        ps.next_obj_id = rid + 1;
        let inner = Value::Enum(SCHEMA, LBox::new(Value::Empty));
        create_obj(rid, name, inner, ps);
        None
    }
}

fn table_dt() -> DataType {
    DataType::Struct(veca![
        (LString::from("NextId"), DataType::Int),
        (LString::from("Root"), DataType::Int),
        (LString::from("Buckets"), DataType::Int),
        (LString::from("IRoot"), DataType::Int),
        (LString::from("IBuckets"), DataType::Int),
    ])
}

fn set_table_datatype(rid: i64, tdt: DataType, ps: &mut PageSet) {
    let dt = DataType::Tuple(veca![DataType::Int, DataType::DataType]);
    let v = Value::List(veca![Value::Int(rid), Value::DataType(tdt)]);

    let addr = VarValAddr::new(&dt, &v, ps);
    println!("xxxxx addr={:?} v={:?}", addr, &v);

    let key = IdKey { rid, dt: &dt };
    let mut hm = HashMap::restore(ps.table_datatype_map, ps);
    hm.insert(&key, addr);
    if hm.root_changed() {
        ps.table_datatype_map = hm.save();
    }
}

pub fn get_table_datatype(rid: i64, ps: &mut PageSet) -> DataType {
    let dt = DataType::Tuple(veca![DataType::Int, DataType::DataType]);
    let key = IdKey { rid, dt: &dt };
    let mut hm = HashMap::restore(ps.table_datatype_map, ps);

    let mut av = hm.get(&key).unwrap();
    let dt = av.1.list_mut()[1].datatype_mut();
    std::mem::take(dt)
}

/// Create a table. If it already exists, return id and value.
pub fn create_table(name: &str, dt: DataType, ps: &mut PageSet) -> Option<(i64, Value)> {
    // Check if obj already exists.
    let obj = get_obj_from_name(name, ps);

    if obj.is_some() {
        obj
    } else {
        let rid = ps.next_obj_id;
        ps.next_obj_id = rid + 1;

        let hm = HashMap::<VarValAddr>::new(ps, 1);
        let i = hm.save();

        let hm = HashMap::<VarValAddr>::new(ps, 1);
        let x = hm.save();

        let tableinfo = Value::List(veca![
            Value::Int(0), // alloc
            Value::Int(i.root as i64),
            Value::Int(i.buckets as i64), // buckets
            Value::Int(x.root as i64),
            Value::Int(x.buckets as i64),
        ]);

        let inner = Value::Enum(TABLE, LBox::new(tableinfo));
        create_obj(rid, name, inner, ps);

        set_table_datatype(rid, dt, ps);

        None
    }
}

#[derive(Debug)]
pub struct TabInfo {
    pub alloc: i64,
    pub main: HashMapInfo,
    pub index: HashMapInfo,
}

pub fn decode_table(obj: Value) -> TabInfo {
    let (tag, tableinfo) = &obj.list()[OBJ_INNER].en();

    assert!(**tag == TABLE);
    let list = tableinfo.list();
    let alloc = list[0].int();
    let root = list[1].int() as u64;
    let buckets = list[2].int() as u64;
    let iroot = list[3].int() as u64;
    let ibuckets = list[4].int() as u64;

    TabInfo {
        alloc,
        main: HashMapInfo{ root, buckets },
        index: HashMapInfo{ root:iroot, buckets:ibuckets },
    }
}

/// Update table data for the specified id.
pub fn save_table(rid: i64, info: TabInfo, ps: &mut PageSet) {
    let (addr, mut obj) = get_obj(rid, ps).unwrap();
    let inner = &mut obj.list_mut()[OBJ_INNER];

    let (tag, tableinfo) = inner.en_mut();

    assert!(*tag == TABLE);
    let list = tableinfo.list_mut();
    list[0] = Value::Int(info.alloc);
    list[1] = Value::Int(info.main.root as i64);
    list[2] = Value::Int(info.main.buckets as i64);
    list[3] = Value::Int(info.index.root as i64);
    list[4] = Value::Int(info.index.buckets as i64);

    save_obj(addr, obj, ps);
}