use nodedb_columnar::{
SegmentReader, SegmentWriter, memtable::ColumnarMemtable, predicate::ScanPredicate,
reader::DecodedColumn,
};
use nodedb_types::{
columnar::{ColumnDef, ColumnType, ColumnarSchema},
value::Value,
};
const TARGET: i64 = 9_223_372_036_854_775_000;
const PROBE: i64 = 9_223_372_036_854_775_000 - 600;
fn large_i64_schema() -> ColumnarSchema {
ColumnarSchema::new(vec![
ColumnDef::required("id", ColumnType::Int64).with_primary_key(),
ColumnDef::required("val", ColumnType::Int64),
])
.expect("valid schema")
}
fn write_two_block_segment() -> Vec<u8> {
let schema = large_i64_schema();
let mut mt = ColumnarMemtable::new(&schema);
for i in 0i64..1024 {
mt.append_row(&[Value::Integer(i), Value::Integer(i)])
.unwrap();
}
for i in 0i64..1024 {
let v = TARGET - 500 + i;
let id = 1024 + i;
mt.append_row(&[Value::Integer(id), Value::Integer(v)])
.unwrap();
}
let (schema, columns, row_count) = mt.drain();
SegmentWriter::plain()
.write_segment(&schema, &columns, row_count, None)
.expect("write segment")
}
#[test]
fn precondition_target_and_probe_share_same_f64() {
assert_eq!(
TARGET as f64, PROBE as f64,
"TARGET and PROBE must round to the same f64 for this test to be meaningful"
);
let safe_limit: i64 = 1 << 53;
assert!(
TARGET.abs() > safe_limit,
"TARGET must be outside ±2^53 to exercise the i64 exact path"
);
assert!(
PROBE.abs() > safe_limit,
"PROBE must be outside ±2^53 to exercise the i64 exact path"
);
assert_ne!(
TARGET, PROBE,
"TARGET and PROBE must be distinct i64 values"
);
}
#[test]
fn large_i64_predicate_pushdown_does_not_skip_block() {
let segment_bytes = write_two_block_segment();
let reader = SegmentReader::open(&segment_bytes).unwrap();
assert_eq!(reader.row_count(), 2048, "expected 2 full blocks");
let predicate = ScanPredicate::eq_i64(1, TARGET); let col = reader
.read_column_filtered(1, &[predicate])
.expect("read val column");
let (values, valid) = match col {
DecodedColumn::Int64 { values, valid } => (values, valid),
other => panic!("expected Int64, got {other:?}"),
};
let matching: Vec<usize> = values
.iter()
.zip(valid.iter())
.enumerate()
.filter(|(_, (v, ok))| **ok && **v == TARGET)
.map(|(i, _)| i)
.collect();
assert_eq!(
matching.len(),
1,
"exactly one row must match val == TARGET; found {}: {:?}",
matching.len(),
matching,
);
assert!(
matching[0] >= 1024,
"matching row {} is in block 0 (row < 1024), wrong block",
matching[0]
);
}
#[test]
fn large_i64_distinct_value_no_false_positive() {
let probe_outside_range = PROBE;
assert!(
probe_outside_range < TARGET - 500,
"probe must be outside block 1 range for a clean negative test"
);
assert_eq!(
probe_outside_range as f64, TARGET as f64,
"probe must share f64 repr with TARGET"
);
let segment_bytes = write_two_block_segment();
let reader = SegmentReader::open(&segment_bytes).unwrap();
let predicate = ScanPredicate::eq_i64(1, probe_outside_range);
let col = reader
.read_column_filtered(1, &[predicate])
.expect("read val column with probe predicate");
let (values, valid) = match col {
DecodedColumn::Int64 { values, valid } => (values, valid),
other => panic!("expected Int64, got {other:?}"),
};
let false_positives: Vec<usize> = values
.iter()
.zip(valid.iter())
.enumerate()
.filter(|(_, (v, ok))| **ok && **v == probe_outside_range)
.map(|(i, _)| i)
.collect();
assert_eq!(
false_positives.len(),
0,
"probe value {probe_outside_range} must not match any row; got false positives at indices: {false_positives:?}"
);
}