tablestg 0.4.3

Storage for database tables
Documentation
#[cfg(test)]
mod testinner {

use crate::*;

use std::hash::Hasher;

#[test]
fn test_main() {
    use page_store::*;

    let limits = Limits::default();

    // Construct BlockPageStg.
    let file = atom_file::MultiFileStorage::new("test.db");
    let upd = atom_file::FastFileStorage::new("test.upd");
    let af = atom_file::AtomicFile::new_with_limits(file, upd, &limits.af_lim);
    let ps = BlockPageStg::new(af, &limits);
    let is_new = ps.is_new();

    let spd = SharedPagedData::new_from_ps(ps);

    if false {
       let psi = &spd.psi;
       println!("max page size={}", psi.max_size_page());
       for i in 0..psi.sizes()
       {
          println!( "page size={}", psi.size(1+i) );
       }
    }

    let mut ps = PageSet::new(spd.new_writer());

    if false {
        let (root, len) = if is_new { (ps.new_page(), 0) } else { (3, 410) };
        test_tv(root, len, &mut ps);
    }

    if false {
        test_table(&mut ps);
    }

    if false {
        test_varval(&mut ps);
    }

    test_cust(&mut ps);

    // ps.save();

    spd.shutdown();
}

struct CustKey<'a> {
    rid: i64,
    cdt: &'a DataType,
}

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

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

struct CustEmailKey<'a> {
    email: &'a str,
    cdt: &'a DataType,
}

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

impl<'a> Key<VarValAddr> for CustEmailKey<'a> {
    fn ok(&self, addr: VarValAddr, ps: &mut PageSet) -> Option<(VarValAddr,Value)> {
        let dt = DataType::IList;

        // Get the list of ids.
        let list = addr.get(&dt, ps);

        // Check the email of first element in the list.
        let rid = list.ilist()[0];
        let key = CustKey { rid, cdt: self.cdt };

        // Use the HashMap to retrieve the cust record.
        let hms = ps.cust_hashmap;
        let mut hm = HashMap::restore(hms, ps);
        let (_,cr) = hm.get(&key).unwrap();

        // Check the email address in the customer record matches self.email
        let email = cr.list()[2].string();

        if *self.email == **email {
            return Some((addr,list));
        }
        None
    }
}

fn init(ps: &mut PageSet) {
    let hm = HashMap::<VarValAddr>::new(ps, 1);
    ps.cust_hashmap = hm.save();

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

fn cust_dt() -> DataType {
    use pstd::veca;

    let optint = DataType::Enum(veca![
        (LString::from("None"), DataType::Empty),
        (LString::from("Some"), DataType::Int),
    ]);
    
    DataType::Struct(veca![
        (LString::from("Id"), DataType::Int),
        (LString::from("Name"), DataType::String),
        (LString::from("Email"), DataType::String),
        (LString::from("Postal"), DataType::String),
        (LString::from("HPass"), DataType::Binary),
        (LString::from("Age"), optint),
    ])
}

fn make_cust(ps: &mut PageSet, cdt: &DataType, rid: i64, name: &str, email: &str, postal: &str) {
    use pstd::veca;

    // Set up a customer record in memory (must match cdt).
    let cr = Value::List(veca![
        Value::Int(rid),
        Value::String(LString::from(name)),
        Value::String(LString::from(email)),
        Value::String(LString::from(postal)),
        Value::Binary(LVec::from([1,2,3,4])),
        Value::Enum(1, LBox::new(Value::Int(68))),
    ]);

    // Store the customer record ( which has variable length ).
    let addr = VarValAddr::new(cdt, &cr, ps);

    println!("make_cust addr={:?} cr={:?}", &addr, &cr);

    {
        // Insert the mapping rid -> addr into cust_hashmap.
        let key = CustKey { rid, cdt };
        let mut hm = HashMap::restore(ps.cust_hashmap, ps);
        hm.insert(&key, addr);
        if hm.root_changed() {
            ps.cust_hashmap = hm.save();
        }
    }

    let emkey = CustEmailKey { email, cdt };

    // Get list of ids associated with cust email.
    let av = {
        let mut hm = HashMap::restore(ps.cust_by_email, ps);
        let av = hm.remove(&emkey);
        if hm.root_changed() {
            ps.cust_by_email = hm.save();
        }
        av
    };

    let dtil = DataType::IList;
    let addr = if let Some((addr,mut list)) = av {
        // List already exists, append to it.
        list.ilist_mut().push(rid);
        addr.update(&dtil, &list, ps)
    } else {
        // Create list with single element and store it.
        let list = Value::IList(veca![rid]);
        VarValAddr::new(&dtil, &list, ps)
    };

    // Associate addr with emkey.
    let mut hm = HashMap::restore(ps.cust_by_email, ps);
    hm.insert(&emkey, addr);
    if hm.root_changed() {
        ps.cust_by_email = hm.save();
    }

}

fn list_cust_ids_with_email(ps: &mut PageSet, cdt: &DataType, email: &str) {
    // Print customer ids with specified email address.

    let key = CustEmailKey { email, cdt };
    let mut hm = HashMap::restore(ps.cust_by_email, ps);

    if let Some((_,list)) = hm.get(&key) {
        println!("List of cust ids for {} = {:?}", email, list.ilist());
    } else {
        println!("No cust ids found for email {}", email);
    }
}

fn list_cust_all(ps: &mut PageSet, cdt: &DataType) {
   let mut hm = HashMap::<VarValAddr>::restore(ps.cust_hashmap, ps);
   for (addr,_) in hm.all()
   {
       let cr = addr.get( cdt, ps );
       println!("list_cust_all addr={:?} cr={:?}", addr, cr );
   }
}
  

fn test_cust(ps: &mut PageSet) {
    init(ps);
    let cdt = cust_dt();

    make_cust(
        ps,
        &cdt,
        99,
        "George Barwood",
        "george@gmail.com",
        "33 Sandpipe Close, GL2 4LZ",
    );

    // Make a duplicate
    make_cust(
        ps,
        &cdt,
        100,
        "George Barwood",
        "george@gmail.com",
        "33 Sandpipe Close, GL2 4LZ",
    );

    // Another duplicate
    make_cust(
        ps,
        &cdt,
        101,
        "George Barwood",
        "george@gmail.com",
        "33 Sandpipe Close, GL2 4LZ",
    );

    make_cust(
        ps,
        &cdt,
        102,
        "Marilyn Barwood",
        "maz.barwood@gmail.com",
        "33 Sandpipe Close, GL2 4LZ",
    );

    list_cust_ids_with_email(ps, &cdt, "george@gmail.com");
    list_cust_ids_with_email(ps, &cdt, "maz.barwood@gmail.com");
    list_cust_ids_with_email(ps, &cdt, "mary@gmail.com");

    list_cust_all(ps, &cdt);
}

}