use crate::numeric_id::NumericId;
use rand::{Rng, rng};
use crate::{
common::{HashMap, ShardId, Value},
offsets::{RowId, SubsetRef},
row_buffer::TaggedRowBuffer,
table::{TableEntry, hash_code},
table_shortcuts::{fill_table, v},
table_spec::{ColumnId, Constraint, Offset, Table, WrappedTable},
};
use super::sharded_hash_table::ShardedHashTable;
fn dump_buf(buf: &TaggedRowBuffer) -> Vec<(RowId, Vec<Value>)> {
let mut res = Vec::new();
buf.iter()
.for_each(|(id, row)| res.push((id, row.to_vec())));
res
}
fn dump_subset(table: &impl Table, subset: SubsetRef) -> Vec<(RowId, Vec<Value>)> {
let mut res = Vec::new();
table.scan_generic(subset, |id, row| {
res.push((id, row.to_vec()));
});
res
}
#[test]
fn empty_key() {
empty_execution_state!(e);
let mut table = fill_table(
vec![vec![v(1), v(2)], vec![v(2), v(3)]],
0,
None,
|_, new| Some(new.to_vec()),
);
let row = table.get_row(&[]).expect("empty key should be present");
assert_eq!(*row.vals, vec![v(2), v(3)]);
table.new_buffer().stage_remove(&[]);
table.merge(&mut e);
assert!(table.get_row(&[]).is_none(), "empty key should be removed");
table.new_buffer().stage_insert(&[v(1), v(2)]);
table.merge(&mut e);
let row = table.get_row(&[]).expect("empty key should be present");
assert_eq!(*row.vals, vec![v(1), v(2)]);
}
#[test]
fn insert_scan() {
let table = fill_table(
vec![
vec![v(0), v(1), v(2)],
vec![v(1), v(2), v(3)],
vec![v(2), v(3), v(4)],
vec![v(3), v(4), v(5)],
vec![v(2), v(3), v(6)],
],
2,
None,
|_, new| Some(new.to_vec()),
);
let all = table.all();
let smaller = table.refine_one(
all,
&Constraint::GtConst {
col: ColumnId::new(2),
val: v(4),
},
);
let rows = dump_subset(&table, smaller.as_ref());
assert_eq!(
rows,
vec![
(RowId::new(3), vec![v(3), v(4), v(5)]),
(RowId::new(4), vec![v(2), v(3), v(6)])
]
);
let mut buf = TaggedRowBuffer::new(2);
let table = WrappedTable::new(table);
table.scan_project(
smaller.as_ref(),
&[ColumnId::new(1), ColumnId::new(0)],
Offset::new(0),
usize::MAX,
&[],
&mut buf,
);
let projection = dump_buf(&buf);
assert_eq!(
projection,
vec![
(RowId::new(3), vec![v(4), v(3)]),
(RowId::new(4), vec![v(3), v(2)])
]
);
}
#[test]
fn insert_scan_sorted() {
let table = fill_table(
vec![
vec![v(0), v(1), v(2)],
vec![v(1), v(2), v(3)],
vec![v(2), v(3), v(4)],
vec![v(3), v(4), v(5)],
vec![v(2), v(3), v(6)],
],
2,
Some(ColumnId::new(2)),
|_, new| Some(new.to_vec()),
);
let all = table.all();
let smaller = table.refine_one(
all,
&Constraint::LtConst {
col: ColumnId::new(1),
val: v(4),
},
);
let rows = dump_subset(&table, smaller.as_ref());
assert_eq!(
rows,
vec![
(RowId::new(0), vec![v(0), v(1), v(2)]),
(RowId::new(1), vec![v(1), v(2), v(3)]),
(RowId::new(4), vec![v(2), v(3), v(6)]),
]
);
let all = table.all();
let sorted_smaller = table.refine_one(
all,
&Constraint::LtConst {
col: ColumnId::new(2),
val: v(5),
},
);
let rows = dump_subset(&table, sorted_smaller.as_ref());
assert_eq!(
rows,
vec![
(RowId::new(0), vec![v(0), v(1), v(2)]),
(RowId::new(1), vec![v(1), v(2), v(3)]),
]
);
}
#[test]
fn shard_math() {
let mut table = ShardedHashTable::<TableEntry>::with_shards(14);
assert_eq!(table.mut_shards().len(), 16);
let mut rng = rng();
let mut hist = HashMap::default();
(0..100_000)
.map(|_| {
hash_code(
table.shard_data(),
&[
Value::new(rng.random()),
Value::new(rng.random()),
Value::new(rng.random()),
],
2,
)
.0
})
.for_each(|id| *hist.entry(id).or_insert(0) += 1);
assert!(hist.iter().all(|(_, count)| *count > 100), "{hist:?}");
assert!((0..100_000).all(|x| table.shard_data().shard_id(x as u64) == ShardId::new(0)));
}