tablestg 0.4.7

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 CustEmailKey<'a> {
        email: &'a str,
        dt: &'a DataType,
        map: &'a TabInfo,
    }

    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 = IdKey { rid, dt: self.dt };

            let mut hm = HashMap::restore(self.map.main, 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.obj_map = hm.save();

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

        let hm = HashMap::<VarValAddr>::new(ps, 1);
        ps.table_datatype_map = 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,
        dt: &DataType,
        map: &mut TabInfo,
        name: &str,
        email: &str,
        postal: &str,
    ) {
        use pstd::veca;

        let rid = map.alloc;
        map.alloc += 1;

        // 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))),
        ]);

        let key = IdKey{ rid, dt };
        key.insert( &cr, &mut map.main, ps );

        let av = {
            let emkey = CustEmailKey { email, dt, map };

            // Get list of ids associated with cust email.
            {
                let mut hm = HashMap::restore(map.index, ps);
                let av = hm.remove(&emkey);
                if hm.root_changed() {
                    map.index = 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 list addr with emkey.
        {
            let emkey = CustEmailKey { email, dt, map };

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

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

        let key = CustEmailKey { email, dt, map };
        let mut hm = HashMap::restore(map.index, 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, map: &TabInfo, cdt: &DataType) {
        let mut hm = HashMap::<VarValAddr>::restore(map.main, ps);

        let mut x = hm.all_addr();

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

        create_table("cust", cust_dt(), ps);

        let (tid, obj) = get_obj_from_name("cust", ps).unwrap();
        let mut cust_map = decode_table(obj);
        let cdt = get_table_datatype(tid, ps);

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

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

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

        for _ in 102..120 {
            make_cust(
                ps,
                &cdt,
                &mut cust_map,
                "Marilyn Barwood",
                "maz.barwood@gmail.com",
                "33 Sandpipe Close, GL2 4LZ",
            );
        }

        list_cust_all(ps, &cust_map, &cdt);

        list_cust_ids_with_email(ps, &cust_map, &cdt, "george@gmail.com");
        list_cust_ids_with_email(ps, &cust_map, &cdt, "maz.barwood@gmail.com");
        list_cust_ids_with_email(ps, &cust_map, &cdt, "mary@gmail.com");
    }
} // end mod