use super::*;
use crate::schema::{SchemaColumn, SchemaDescriptor, TypeCode};
fn schema_of(cols: &[(TypeCode, bool)], pk: &[u32]) -> SchemaDescriptor {
let cols: Vec<SchemaColumn> = cols
.iter()
.map(|&(tc, nullable)| SchemaColumn::new(tc, nullable))
.collect();
SchemaDescriptor::new(&cols, pk)
}
#[test]
fn batch_builder_writes_payload_around_a_non_leading_compound_pk() {
use TypeCode::*;
let schema = schema_of(&[(U8, false), (U64, false), (U32, false), (I16, false)], &[1, 2]);
let mut bb = BatchBuilder::new(&schema);
for (k, a, b) in [(1u128, 0xAB_u128, -5i16), (2, 0xCD, 300)] {
bb.begin_row_natives(&[k, k * 10], 1);
bb.put_int(a);
bb.put_int(b as u128);
bb.end_row();
}
let batch = bb.finish();
for (row, (a, b)) in [(0xABu8, -5i16), (0xCD, 300)].into_iter().enumerate() {
assert_eq!(batch.get_col_ptr(row, 0, 1), &[a], "row {row} slot 0");
assert_eq!(batch.get_col_ptr(row, 1, 2), &b.to_le_bytes(), "row {row} slot 1");
}
}
#[test]
fn batch_builder_writes_string_cells_and_nulls() {
let schema = schema_of(
&[
(TypeCode::U64, false),
(TypeCode::String, true),
(TypeCode::String, true),
],
&[0],
);
type Row<'a> = (Option<&'a [u8]>, Option<&'a [u8]>);
let cases: &[Row] = &[
(Some(b"Alice"), Some(b"short")),
(Some(b"Bob has a long name!"), Some(b"Also quite a long description")),
(Some(b""), Some(b"nonempty")),
(None, Some(b"another long one for blob storage")),
];
let mut bb = BatchBuilder::new(&schema);
for (pk, &(a, b)) in cases.iter().enumerate() {
bb.begin_row(pk as u128, 1);
for cell in [a, b] {
match cell {
Some(v) => bb.put_blob(v),
None => bb.put_null(),
}
}
bb.end_row();
}
let batch = bb.finish();
assert_eq!(batch.count, cases.len());
for (i, &(a, b)) in cases.iter().enumerate() {
for (col, want) in [(0usize, a), (1, b)] {
assert_eq!(
batch.get_null_word(i) >> col & 1,
u64::from(want.is_none()),
"row {i} col {col} null bit"
);
match want {
Some(v) => assert_eq!(gnitz_wire::payload_bytes(&batch, i, col), v, "row {i} col {col}"),
None => assert!(
batch.get_col_ptr(i, col, 16).iter().all(|&b| b == 0),
"row {i} col {col}: a null cell must be zeroed"
),
}
}
}
}
#[test]
fn batch_builder_writes_every_payload_type_at_its_own_width() {
let schema = schema_of(
&[
(TypeCode::U64, false), (TypeCode::U8, false), (TypeCode::I8, false), (TypeCode::U16, false), (TypeCode::I16, false), (TypeCode::U32, false), (TypeCode::I32, false), (TypeCode::F32, false), (TypeCode::U64, false), (TypeCode::I64, false), (TypeCode::F64, false), (TypeCode::String, false), (TypeCode::I128, false), ],
&[0],
);
let i128_val: i128 = -0x0123_4567_89AB_CDEF_1122_3344_5566_7788;
let mut bb = BatchBuilder::new(&schema);
bb.begin_row(100, 1);
for v in [42i128, -7, 1000, -500, 70000, -12345] {
bb.put_int(v as u128);
}
bb.put_float(1.5);
bb.put_int(0x1234_5678_9ABC_DEF0u128);
bb.put_int(-99999i128 as u128);
bb.put_float(-2.25);
bb.put_blob(b"hello world!");
bb.put_int(i128_val as u128);
bb.end_row();
let batch = bb.finish();
assert_eq!(batch.count, 1);
assert_eq!(batch.get_col_ptr(0, 0, 1), &[42]);
assert_eq!(batch.get_col_ptr(0, 1, 1), &[(-7i8) as u8]);
assert_eq!(batch.get_col_ptr(0, 2, 2), &1000u16.to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 3, 2), &(-500i16).to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 4, 4), &70000u32.to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 5, 4), &(-12345i32).to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 6, 4), &1.5f32.to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 7, 8), &0x1234_5678_9ABC_DEF0u64.to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 8, 8), &(-99999i64).to_le_bytes());
assert_eq!(batch.get_col_ptr(0, 9, 8), &(-2.25f64).to_le_bytes());
assert_eq!(gnitz_wire::payload_bytes(&batch, 0, 10), b"hello world!");
let got = i128::from_le_bytes(batch.get_col_ptr(0, 11, 16).try_into().unwrap());
assert_eq!(got, i128_val, "a 16-byte payload round-trips at its full width");
}