tablestg 0.4.5

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);
        }

        if true {
            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);

        let mut x = hm.all_val();
        x.sort();

        for addr in x {
            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",
        );

        for id in 102..120 {
            make_cust(
                ps,
                &cdt,
                id,
                "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);
    }
}