use std::{collections::btree_map, ops::Bound};
use vibesql_types::SqlValue;
pub struct OwnedStreamingRangeScan<'a> {
data: &'a std::collections::BTreeMap<Vec<SqlValue>, Vec<usize>>,
current_indices: Option<std::slice::Iter<'a, usize>>,
start_key: Option<Vec<SqlValue>>,
end_key: Option<Vec<SqlValue>>,
inclusive_start: bool,
inclusive_end: bool,
range_iter: Option<btree_map::Range<'a, Vec<SqlValue>, Vec<usize>>>,
initialized: bool,
}
impl<'a> OwnedStreamingRangeScan<'a> {
pub fn new(
data: &'a std::collections::BTreeMap<Vec<SqlValue>, Vec<usize>>,
start: Option<SqlValue>,
end: Option<SqlValue>,
inclusive_start: bool,
inclusive_end: bool,
) -> Option<Self> {
if let (Some(start_val), Some(end_val)) = (&start, &end) {
if start_val == end_val && (!inclusive_start || !inclusive_end) {
return None; }
if start_val > end_val {
return None; }
}
let start_key = start.map(|v| vec![v]);
let end_key = end.map(|v| vec![v]);
Some(Self {
data,
current_indices: None,
start_key,
end_key,
inclusive_start,
inclusive_end,
range_iter: None,
initialized: false,
})
}
fn ensure_initialized(&mut self) {
if self.initialized {
return;
}
self.initialized = true;
let start_bound: Bound<&[SqlValue]> = match &self.start_key {
Some(key) if self.inclusive_start => Bound::Included(key.as_slice()),
Some(key) => Bound::Excluded(key.as_slice()),
None => Bound::Unbounded,
};
let end_bound: Bound<&[SqlValue]> = match &self.end_key {
Some(key) if self.inclusive_end => Bound::Included(key.as_slice()),
Some(key) => Bound::Excluded(key.as_slice()),
None => Bound::Unbounded,
};
if let (Bound::Excluded(s), Bound::Excluded(e)) = (&start_bound, &end_bound) {
if s == e {
return; }
}
self.range_iter = Some(self.data.range::<[SqlValue], _>((start_bound, end_bound)));
}
}
impl Iterator for OwnedStreamingRangeScan<'_> {
type Item = usize;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.ensure_initialized();
loop {
if let Some(ref mut indices) = self.current_indices {
if let Some(&row_idx) = indices.next() {
return Some(row_idx);
}
}
let range_iter = self.range_iter.as_mut()?;
match range_iter.next() {
Some((_key, row_indices)) => {
self.current_indices = Some(row_indices.iter());
}
None => return None,
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, None)
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
#[test]
fn test_owned_streaming_range_scan_basic() {
let mut data: BTreeMap<Vec<SqlValue>, Vec<usize>> = BTreeMap::new();
data.insert(vec![SqlValue::Integer(1)], vec![0]);
data.insert(vec![SqlValue::Integer(2)], vec![1, 2]);
data.insert(vec![SqlValue::Integer(3)], vec![3]);
data.insert(vec![SqlValue::Integer(4)], vec![4, 5, 6]);
data.insert(vec![SqlValue::Integer(5)], vec![7]);
let iter = OwnedStreamingRangeScan::new(
&data,
Some(SqlValue::Integer(2)),
Some(SqlValue::Integer(4)),
true,
true,
)
.unwrap();
let results: Vec<usize> = iter.collect();
assert_eq!(results, vec![1, 2, 3, 4, 5, 6]);
}
#[test]
fn test_owned_streaming_range_scan_empty_range() {
let mut data: BTreeMap<Vec<SqlValue>, Vec<usize>> = BTreeMap::new();
data.insert(vec![SqlValue::Integer(1)], vec![0]);
data.insert(vec![SqlValue::Integer(5)], vec![4]);
let iter = OwnedStreamingRangeScan::new(
&data,
Some(SqlValue::Integer(3)),
Some(SqlValue::Integer(4)),
true,
true,
)
.unwrap();
let results: Vec<usize> = iter.collect();
let expected: Vec<usize> = vec![];
assert_eq!(results, expected);
}
#[test]
fn test_owned_streaming_range_scan_inverted_range() {
let mut data: BTreeMap<Vec<SqlValue>, Vec<usize>> = BTreeMap::new();
data.insert(vec![SqlValue::Integer(1)], vec![0]);
let result = OwnedStreamingRangeScan::new(
&data,
Some(SqlValue::Integer(5)),
Some(SqlValue::Integer(1)),
true,
true,
);
assert!(result.is_none());
}
#[test]
fn test_owned_streaming_range_scan_exclusive_bounds() {
let mut data: BTreeMap<Vec<SqlValue>, Vec<usize>> = BTreeMap::new();
data.insert(vec![SqlValue::Integer(1)], vec![0]);
data.insert(vec![SqlValue::Integer(2)], vec![1]);
data.insert(vec![SqlValue::Integer(3)], vec![2]);
data.insert(vec![SqlValue::Integer(4)], vec![3]);
data.insert(vec![SqlValue::Integer(5)], vec![4]);
let iter = OwnedStreamingRangeScan::new(
&data,
Some(SqlValue::Integer(2)),
Some(SqlValue::Integer(4)),
false,
false,
)
.unwrap();
let results: Vec<usize> = iter.collect();
assert_eq!(results, vec![2]); }
#[test]
fn test_owned_streaming_unbounded_start() {
let mut data: BTreeMap<Vec<SqlValue>, Vec<usize>> = BTreeMap::new();
data.insert(vec![SqlValue::Integer(1)], vec![0]);
data.insert(vec![SqlValue::Integer(2)], vec![1]);
data.insert(vec![SqlValue::Integer(3)], vec![2]);
let iter = OwnedStreamingRangeScan::new(
&data,
None,
Some(SqlValue::Integer(2)),
true,
true,
)
.unwrap();
let results: Vec<usize> = iter.collect();
assert_eq!(results, vec![0, 1]); }
#[test]
fn test_owned_streaming_unbounded_end() {
let mut data: BTreeMap<Vec<SqlValue>, Vec<usize>> = BTreeMap::new();
data.insert(vec![SqlValue::Integer(1)], vec![0]);
data.insert(vec![SqlValue::Integer(2)], vec![1]);
data.insert(vec![SqlValue::Integer(3)], vec![2]);
let iter = OwnedStreamingRangeScan::new(
&data,
Some(SqlValue::Integer(2)),
None,
true,
true,
)
.unwrap();
let results: Vec<usize> = iter.collect();
assert_eq!(results, vec![1, 2]); }
}