use crate::{
InternalValue,
comparator::SharedComparator,
key::InternalKey,
memtable::Memtable,
merge_operator::MergeOperator,
merge_source::{CoherentIterSource, CoherentMergeSource, IterItem, MergeSource},
mvcc_stream::MvccStream,
range_tombstone::RangeTombstone,
range_tombstone_filter::RangeTombstoneFilter,
reseek::{ReseekCtx, Reseekable},
run_reader::RunReader,
seeking_merger::SeekingMerger,
skip_key::SkipKey,
value::{SeqNo, UserKey},
version::{Run, SuperVersion},
};
use alloc::sync::Arc;
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use core::ops::{Bound, RangeBounds};
use self_cell::self_cell;
#[must_use]
pub fn seqno_filter(item_seqno: SeqNo, seqno: SeqNo) -> bool {
item_seqno < seqno
}
trait ScanSource: DoubleEndedIterator<Item = crate::Result<InternalValue>> + SkipKey + Send {}
impl<T: DoubleEndedIterator<Item = crate::Result<InternalValue>> + SkipKey + Send> ScanSource
for T
{
}
type ScanBox<'a> = Box<dyn ScanSource + 'a>;
fn build_seeking<'a>(
iters: Vec<ScanBox<'a>>,
comparator: SharedComparator,
) -> SeekingMerger<CoherentIterSource<ScanBox<'a>>, SharedComparator> {
let sources: Vec<CoherentIterSource<ScanBox<'a>>> =
iters.into_iter().map(CoherentIterSource::new).collect();
SeekingMerger::new(sources, comparator)
}
struct Visible<I> {
inner: I,
seqno: SeqNo,
}
impl<I: Iterator<Item = crate::Result<InternalValue>>> Iterator for Visible<I> {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.inner.next()? {
Ok(item) if !seqno_filter(item.key.seqno, self.seqno) => {}
other => return Some(other),
}
}
}
}
impl<I: DoubleEndedIterator<Item = crate::Result<InternalValue>>> DoubleEndedIterator
for Visible<I>
{
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.inner.next_back()? {
Ok(item) if !seqno_filter(item.key.seqno, self.seqno) => {}
other => return Some(other),
}
}
}
}
impl<I: SkipKey> SkipKey for Visible<I> {
fn skip_front_past(&mut self, key: &[u8]) -> crate::Result<bool> {
self.inner.skip_front_past(key)
}
fn skip_back_to_newest(&mut self, key: &[u8], seqno: SeqNo) -> crate::Result<bool> {
self.inner.skip_back_to_newest(key, seqno.min(self.seqno))
}
}
struct MemtableRows<'a>(crate::memtable::skiplist::Range<'a>);
impl Iterator for MemtableRows<'_> {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|entry| {
Ok(InternalValue {
key: entry.key(),
value: entry.value(),
})
})
}
}
impl DoubleEndedIterator for MemtableRows<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|entry| {
Ok(InternalValue {
key: entry.key(),
value: entry.value(),
})
})
}
}
impl SkipKey for MemtableRows<'_> {
fn skip_front_past(&mut self, key: &[u8]) -> crate::Result<bool> {
self.0.skip_front_past(key);
Ok(true)
}
fn skip_back_to_newest(&mut self, key: &[u8], seqno: SeqNo) -> crate::Result<bool> {
Ok(self.0.skip_back_to_newest(key, seqno))
}
}
fn memtable_source(
memtable: &Memtable,
range: (Bound<InternalKey>, Bound<InternalKey>),
seqno: SeqNo,
) -> ScanBox<'_> {
Box::new(Visible {
inner: MemtableRows(memtable.items.range(range)),
seqno,
})
}
pub(crate) fn prefix_upper_range(prefix: &[u8]) -> Bound<UserKey> {
use core::ops::Bound::{Excluded, Unbounded};
assert!(!prefix.is_empty(), "prefix may not be empty");
let mut end = prefix.to_vec();
let len = end.len();
for (idx, byte) in end.iter_mut().rev().enumerate() {
let idx = len - 1 - idx;
if *byte < 255 {
*byte += 1;
end.truncate(idx + 1);
return Excluded(end.into());
}
}
Unbounded
}
#[must_use]
#[expect(clippy::module_name_repetitions)]
pub fn prefix_to_range(prefix: &[u8]) -> (Bound<UserKey>, Bound<UserKey>) {
use core::ops::Bound::{Included, Unbounded};
if prefix.is_empty() {
return (Unbounded, Unbounded);
}
(Included(prefix.into()), prefix_upper_range(prefix))
}
#[derive(Clone)]
pub struct IterState {
pub(crate) version: SuperVersion,
pub(crate) ephemeral: Option<(Arc<Memtable>, SeqNo)>,
pub(crate) merge_operator: Option<Arc<dyn MergeOperator>>,
pub(crate) blob_source: Option<crate::blob_tree::BlobSource>,
pub(crate) comparator: crate::comparator::SharedComparator,
pub(crate) prefix_hash: Option<u64>,
#[cfg(feature = "metrics")]
pub(crate) metrics: Option<Arc<crate::Metrics>>,
}
impl IterState {
fn value_log(&self) -> Option<crate::mvcc_stream::ValueLog<'_>> {
self.blob_source
.as_ref()
.map(|source| crate::mvcc_stream::ValueLog {
source,
version: &self.version.version,
})
}
fn filter_query(&self) -> FilterQuery<'_> {
FilterQuery {
prefix_hash: self.prefix_hash,
key_hash: None,
bloom_key: None,
#[cfg(feature = "metrics")]
metrics: self.metrics.as_ref(),
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct FilterQuery<'a> {
pub(crate) prefix_hash: Option<u64>,
pub(crate) key_hash: Option<u64>,
pub(crate) bloom_key: Option<&'a [u8]>,
#[cfg(feature = "metrics")]
pub(crate) metrics: Option<&'a Arc<crate::Metrics>>,
}
type BoxedMerge<'a> = Box<dyn DoubleEndedIterator<Item = crate::Result<InternalValue>> + Send + 'a>;
self_cell!(
pub struct TreeIter {
owner: IterState,
#[covariant]
dependent: BoxedMerge,
}
);
impl Iterator for TreeIter {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
self.with_dependent_mut(|_, iter| iter.next())
}
}
impl DoubleEndedIterator for TreeIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.with_dependent_mut(|_, iter| iter.next_back())
}
}
pub(crate) fn range_tombstone_overlaps_bounds(
rt: &RangeTombstone,
bounds: &(Bound<UserKey>, Bound<UserKey>),
comparator: &dyn crate::comparator::UserComparator,
) -> bool {
let overlaps_lo = match &bounds.0 {
Bound::Included(key) | Bound::Excluded(key) => {
comparator.compare(&rt.end, key) == core::cmp::Ordering::Greater
}
Bound::Unbounded => true,
};
let overlaps_hi = match &bounds.1 {
Bound::Included(key) => comparator.compare(&rt.start, key) != core::cmp::Ordering::Greater,
Bound::Excluded(key) => comparator.compare(&rt.start, key) == core::cmp::Ordering::Less,
Bound::Unbounded => true,
};
overlaps_lo && overlaps_hi
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum FilterAnswer {
Absent,
KeyPassed,
PrefixPassed,
Unanswered,
}
impl FilterAnswer {
fn passed(self) -> bool {
matches!(self, Self::KeyPassed | Self::PrefixPassed)
}
}
fn bloom_passes(state: &IterState, table: &crate::table::Table) -> bool {
filter_answer(state.filter_query(), table) != FilterAnswer::Absent
}
fn filter_answer(query: FilterQuery<'_>, table: &crate::table::Table) -> FilterAnswer {
let mut answer = FilterAnswer::Unanswered;
if let Some(prefix_hash) = query.prefix_hash
&& table.has_prefix_filter()
{
use crate::table::probe_stats::ProbeCounts;
match table.maybe_contains_prefix(prefix_hash) {
Ok(false) => {
table.count_probes(ProbeCounts {
probes: 1,
negatives: 1,
});
#[cfg(feature = "metrics")]
if let Some(m) = query.metrics {
m.prefix_bloom_skips
.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
return FilterAnswer::Absent;
}
Ok(true) if table.key_check_consults_filter(false) => {
table.count_probes(ProbeCounts {
probes: 1,
negatives: 0,
});
answer = FilterAnswer::PrefixPassed;
}
Err(e) => {
log::debug!("prefix bloom check failed for table {:?}: {e}", table.id());
}
Ok(true) => {}
}
}
debug_assert!(
query.bloom_key.is_none() || query.key_hash.is_some(),
"bloom_key requires key_hash to be set"
);
if let Some(key_hash) = query.key_hash {
use crate::table::KeyFilterAnswer;
let result = if let Some(bloom_key) = query.bloom_key {
table.key_filter_answer(bloom_key, key_hash)
} else {
table.bloom_may_contain_key_hash(key_hash).map(|may| {
if may {
KeyFilterAnswer::MayContain
} else {
KeyFilterAnswer::Absent
}
})
};
use crate::table::probe_stats::ProbeCounts;
match result {
Ok(KeyFilterAnswer::Absent) => {
table.count_probes(ProbeCounts {
probes: 1,
negatives: 1,
});
return FilterAnswer::Absent;
}
Ok(KeyFilterAnswer::PastPartitions) => return FilterAnswer::Absent,
Ok(KeyFilterAnswer::MayContain)
if table.key_check_consults_filter(query.bloom_key.is_some()) =>
{
table.count_probes(ProbeCounts {
probes: 1,
negatives: 0,
});
return FilterAnswer::KeyPassed;
}
Err(e) => {
log::debug!("key bloom check failed for table {:?}: {e}", table.id());
}
Ok(KeyFilterAnswer::MayContain) => {}
}
}
answer
}
struct FilterPassReader<I, T> {
inner: I,
table: T,
settled: bool,
}
impl<I, T> FilterPassReader<I, T>
where
I: DoubleEndedIterator<Item = crate::Result<InternalValue>>,
T: core::borrow::Borrow<crate::table::Table>,
{
fn settle(
&mut self,
item: Option<crate::Result<InternalValue>>,
) -> Option<crate::Result<InternalValue>> {
if !self.settled {
self.settled = true;
if item.is_none() {
self.table
.borrow()
.count_probes(crate::table::probe_stats::ProbeCounts {
probes: 0,
negatives: 1,
});
}
}
item
}
}
impl<I, T> Iterator for FilterPassReader<I, T>
where
I: DoubleEndedIterator<Item = crate::Result<InternalValue>>,
T: core::borrow::Borrow<crate::table::Table>,
{
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
let item = self.inner.next();
self.settle(item)
}
}
impl<I, T> DoubleEndedIterator for FilterPassReader<I, T>
where
I: DoubleEndedIterator<Item = crate::Result<InternalValue>>,
T: core::borrow::Borrow<crate::table::Table>,
{
fn next_back(&mut self) -> Option<Self::Item> {
let item = self.inner.next_back();
self.settle(item)
}
}
impl<I: SkipKey, T> SkipKey for FilterPassReader<I, T> {
fn skip_front_past(&mut self, key: &[u8]) -> crate::Result<bool> {
self.inner.skip_front_past(key)
}
fn skip_back_to_newest(&mut self, key: &[u8], seqno: SeqNo) -> crate::Result<bool> {
self.inner.skip_back_to_newest(key, seqno)
}
}
fn table_reader<'a, T>(
answer: FilterAnswer,
table: T,
user_range: (Bound<UserKey>, Bound<UserKey>),
seqno: SeqNo,
) -> ScanBox<'a>
where
T: core::borrow::Borrow<crate::table::Table> + Send + 'a,
{
let reader = table.borrow().range_iter(user_range);
let counts = answer.passed() && table.borrow().probe_stats().is_some();
if counts {
Box::new(Visible {
inner: FilterPassReader {
inner: reader,
table,
settled: false,
},
seqno,
})
} else {
Box::new(Visible {
inner: reader,
seqno,
})
}
}
enum Source<'a> {
Table(crate::table::Table, FilterAnswer),
Boxed(ScanBox<'a>),
}
impl<'a> Source<'a> {
fn into_boxed(self, user_range: &UserBounds, seqno: SeqNo) -> ScanBox<'a> {
match self {
Self::Table(table, answer) => table_reader(answer, table, user_range.clone(), seqno),
Self::Boxed(iter) => iter,
}
}
}
fn lone_table_scan<'a>(
answer: FilterAnswer,
table: crate::table::Table,
user_range: UserBounds,
seqno: SeqNo,
range_tombstones: Vec<(RangeTombstone, SeqNo)>,
comparator: SharedComparator,
) -> BoxedMerge<'a> {
let reader = table.range(user_range);
let counts = answer.passed() && table.probe_stats().is_some();
let handed_out = move |item: &crate::Result<InternalValue>| match item {
Ok(item) => seqno_filter(item.key.seqno, seqno) && !item.key.is_tombstone(),
Err(_) => true,
};
if counts {
finish_scan(
FilterPassReader {
inner: reader,
table,
settled: false,
}
.filter(handed_out),
range_tombstones,
comparator,
)
} else {
finish_scan(reader.filter(handed_out), range_tombstones, comparator)
}
}
impl TreeIter {
#[expect(
clippy::too_many_lines,
reason = "create_range wires up multiple iterator sources, filters, and tombstone handling; splitting further would reduce clarity"
)]
pub fn create_range<K: AsRef<[u8]>, R: RangeBounds<K>>(
guard: IterState,
range: R,
seqno: SeqNo,
) -> Self {
Self::new(guard, |lock| {
let user_range = (
match range.start_bound() {
Bound::Included(key) => Bound::Included(UserKey::from(key.as_ref())),
Bound::Excluded(key) => Bound::Excluded(UserKey::from(key.as_ref())),
Bound::Unbounded => Bound::Unbounded,
},
match range.end_bound() {
Bound::Included(key) => Bound::Included(UserKey::from(key.as_ref())),
Bound::Excluded(key) => Bound::Excluded(UserKey::from(key.as_ref())),
Bound::Unbounded => Bound::Unbounded,
},
);
let range = user_to_internal_bounds(&user_range);
let mut iters: Vec<Source<'_>> = Vec::with_capacity(5);
let mut one_version: Vec<bool> = Vec::with_capacity(5);
let mut all_range_tombstones: Vec<(RangeTombstone, SeqNo)> = Vec::new();
let mut candidates: Vec<Vec<crate::table::Table>> = Vec::new();
let mut multi_runs = Vec::new();
for run in lock
.version
.version
.iter_levels()
.flat_map(|lvl| lvl.iter())
{
match run.len() {
0 => {
}
1 => {
#[expect(clippy::expect_used, reason = "we checked for length")]
let table = run.first().expect("should exist");
all_range_tombstones.extend(
table
.range_tombstones()
.iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(
rt,
&user_range,
lock.comparator.as_ref(),
)
})
.map(|rt| (rt.clone(), seqno)),
);
if table.check_key_range_overlap_cmp(
&(
user_range.0.as_ref().map(core::convert::AsRef::as_ref),
user_range.1.as_ref().map(core::convert::AsRef::as_ref),
),
lock.comparator.as_ref(),
) {
candidates.push(alloc::vec![table.clone()]);
}
}
_ => {
for table in run.iter() {
all_range_tombstones.extend(
table
.range_tombstones()
.iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(
rt,
&user_range,
lock.comparator.as_ref(),
)
})
.map(|rt| (rt.clone(), seqno)),
);
}
if lock.prefix_hash.is_some() {
let bounds = (
user_range.0.as_ref().map(core::convert::AsRef::as_ref),
user_range.1.as_ref().map(core::convert::AsRef::as_ref),
);
candidates.push(
run.iter()
.filter(|table| {
table.check_key_range_overlap_cmp(
&bounds,
lock.comparator.as_ref(),
)
})
.cloned()
.collect(),
);
} else {
multi_runs.push(run.clone());
}
}
}
}
all_range_tombstones
.sort_unstable_by(|(a, _), (b, _)| lock.comparator.compare(&a.start, &b.start));
let is_covered = |table: &crate::table::Table| {
let table_min: &[u8] = table.metadata.key_range.min().as_ref();
let table_max: &[u8] = table.metadata.key_range.max().as_ref();
let table_kv_seqno = table.get_highest_kv_seqno();
let candidate_end = all_range_tombstones.partition_point(|(rt, _)| {
lock.comparator.compare(&rt.start, table_min) != core::cmp::Ordering::Greater
});
all_range_tombstones
.iter()
.take(candidate_end)
.any(|(rt, cutoff)| {
rt.visible_at(*cutoff)
&& rt.fully_covers_with(table_min, table_max, lock.comparator.as_ref())
&& rt.seqno > table_kv_seqno
})
};
lock.version.version.read_mix().note_scan();
for run in candidates {
let mut surviving: Vec<(crate::table::Table, FilterAnswer)> = run
.into_iter()
.filter(|table| !is_covered(table))
.filter_map(|table| {
let answer = filter_answer(lock.filter_query(), &table);
(answer != FilterAnswer::Absent).then_some((table, answer))
})
.collect();
if surviving.is_empty() {
continue;
}
let (first, answer) = surviving.remove(0);
first.note_scan();
one_version.push(holds_one_version_per_key(&first));
iters.push(Source::Table(first, answer));
match surviving.len() {
0 => {}
1 => {
if let Some((table, answer)) = surviving.pop() {
table.note_scan();
one_version.push(holds_one_version_per_key(&table));
iters.push(Source::Table(table, answer));
}
}
_ => {
let rest = surviving.into_iter().map(|(table, _)| table).collect();
#[expect(
clippy::expect_used,
reason = "Run::new returns None only for empty vecs"
)]
let new_run = Run::new(rest).expect("non-empty surviving tables");
multi_runs.push(Arc::new(new_run));
}
}
}
for run in multi_runs {
let run_one_version = run.iter().all(holds_one_version_per_key);
if let Some(reader) =
RunReader::new_cmp(run, user_range.clone(), lock.comparator.as_ref())
.map(RunReader::counting_scans)
{
one_version.push(run_one_version);
iters.push(Source::Boxed(Box::new(Visible {
inner: reader,
seqno,
})));
}
}
let table_source_count = iters.len();
for memtable in lock.version.sealed_memtables.iter() {
all_range_tombstones.extend(
memtable
.range_tombstones_sorted()
.into_iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(
rt,
&user_range,
lock.comparator.as_ref(),
)
})
.map(|rt| (rt, seqno)),
);
if memtable.items.is_empty() {
continue;
}
one_version.push(false);
iters.push(Source::Boxed(memtable_source(
memtable,
range.clone(),
seqno,
)));
}
{
all_range_tombstones.extend(
lock.version
.active_memtable
.range_tombstones_sorted()
.into_iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(
rt,
&user_range,
lock.comparator.as_ref(),
)
})
.map(|rt| (rt, seqno)),
);
if !lock.version.active_memtable.items.is_empty() {
one_version.push(false);
iters.push(Source::Boxed(memtable_source(
&lock.version.active_memtable,
range.clone(),
seqno,
)));
}
}
if let Some((mt, eph_seqno)) = &lock.ephemeral {
all_range_tombstones.extend(
mt.range_tombstones_sorted()
.into_iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(
rt,
&user_range,
lock.comparator.as_ref(),
)
})
.map(|rt| (rt, *eph_seqno)),
);
if !mt.items.is_empty() {
one_version.push(false);
iters.push(Source::Boxed(memtable_source(mt, range, *eph_seqno)));
}
}
if let Some(memtable_segment) = iters.get_mut(table_source_count..) {
memtable_segment.reverse();
}
iters.rotate_left(table_source_count);
let lone = (iters.len() == 1).then(|| iters.pop()).flatten();
let lone_one_version = one_version.len() == 1 && one_version.first() == Some(&true);
let groups_versions = !(lone_one_version && lock.merge_operator.is_none());
let mvcc_tombstones = if lone.is_some() && !groups_versions {
Vec::new()
} else {
all_range_tombstones.clone()
};
all_range_tombstones
.sort_by(|a, b| a.0.cmp_with_comparator(&b.0, lock.comparator.as_ref()));
all_range_tombstones.dedup_by(|a, b| {
if a.0 == b.0 {
b.1 = b.1.max(a.1);
true
} else {
false
}
});
let comparator = lock.comparator.clone();
match lone {
Some(Source::Table(table, answer)) if !groups_versions => lone_table_scan(
answer,
table,
user_range,
seqno,
all_range_tombstones,
comparator,
),
Some(source) if !groups_versions => finish_scan(
source.into_boxed(&user_range, seqno).filter(is_live),
all_range_tombstones,
comparator,
),
Some(source) => finish_scan(
MvccStream::new_with_comparator(
source.into_boxed(&user_range, seqno),
lock.merge_operator.clone(),
lock.comparator.clone(),
)
.jumping()
.with_value_log(lock.value_log())
.with_range_tombstones(mvcc_tombstones)
.filter(is_live),
all_range_tombstones,
comparator,
),
None => finish_scan(
MvccStream::new_with_comparator(
build_seeking(
iters
.into_iter()
.map(|source| source.into_boxed(&user_range, seqno))
.collect(),
lock.comparator.clone(),
),
lock.merge_operator.clone(),
lock.comparator.clone(),
)
.jumping()
.with_value_log(lock.value_log())
.with_range_tombstones(mvcc_tombstones)
.filter(is_live),
all_range_tombstones,
comparator,
),
}
})
}
}
fn is_live(item: &crate::Result<InternalValue>) -> bool {
match item {
Ok(value) => !value.key.is_tombstone(),
Err(_) => true,
}
}
fn holds_one_version_per_key(table: &crate::table::Table) -> bool {
table.metadata.key_count == Some(table.metadata.item_count)
}
fn finish_scan<'a, I>(
iter: I,
range_tombstones: Vec<(RangeTombstone, SeqNo)>,
comparator: SharedComparator,
) -> BoxedMerge<'a>
where
I: DoubleEndedIterator<Item = crate::Result<InternalValue>> + Send + 'a,
{
if range_tombstones
.iter()
.all(|(rt, cutoff)| !rt.visible_at(*cutoff))
{
Box::new(iter)
} else {
Box::new(RangeTombstoneFilter::new_with_comparator(
iter,
range_tombstones,
comparator,
))
}
}
type UserBounds = (Bound<UserKey>, Bound<UserKey>);
pub(crate) fn user_to_internal_bounds(
user: &UserBounds,
) -> (Bound<InternalKey>, Bound<InternalKey>) {
internal_bounds(&user.0, &user.1)
}
pub(crate) fn internal_bounds(
lo: &Bound<UserKey>,
hi: &Bound<UserKey>,
) -> (Bound<InternalKey>, Bound<InternalKey>) {
let lo = match lo {
Bound::Included(key) => Bound::Included(InternalKey::bound(
key.clone(),
SeqNo::MAX,
crate::ValueType::Tombstone,
)),
Bound::Excluded(key) => Bound::Excluded(InternalKey::bound(
key.clone(),
0,
crate::ValueType::Tombstone,
)),
Bound::Unbounded => Bound::Unbounded,
};
let hi = match hi {
Bound::Included(key) => {
Bound::Included(InternalKey::bound(key.clone(), 0, crate::ValueType::Value))
}
Bound::Excluded(key) => Bound::Excluded(InternalKey::bound(
key.clone(),
SeqNo::MAX,
crate::ValueType::Value,
)),
Bound::Unbounded => Bound::Unbounded,
};
(lo, hi)
}
#[derive(Clone)]
struct CollectedSources {
single_tables: Vec<crate::table::Table>,
multi_runs: Vec<Arc<Run<crate::table::Table>>>,
range_tombstones: Vec<(RangeTombstone, SeqNo)>,
union: UserBounds,
}
fn collect_sources(state: &IterState, union: UserBounds, seqno: SeqNo) -> CollectedSources {
let mut single_tables: Vec<crate::table::Table> = Vec::new();
let mut multi_runs: Vec<Arc<Run<crate::table::Table>>> = Vec::new();
let mut rts: Vec<(RangeTombstone, SeqNo)> = Vec::new();
let bounds_ref = (
union.0.as_ref().map(core::convert::AsRef::as_ref),
union.1.as_ref().map(core::convert::AsRef::as_ref),
);
for run in state
.version
.version
.iter_levels()
.flat_map(|lvl| lvl.iter())
{
match run.len() {
0 => {}
1 => {
#[expect(clippy::expect_used, reason = "len checked")]
let table = run.first().expect("len == 1");
rts.extend(
table
.range_tombstones()
.iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(rt, &union, state.comparator.as_ref())
})
.map(|rt| (rt.clone(), seqno)),
);
if table.check_key_range_overlap_cmp(&bounds_ref, state.comparator.as_ref())
&& bloom_passes(state, table)
{
single_tables.push(table.clone());
}
}
_ => {
for table in run.iter() {
rts.extend(
table
.range_tombstones()
.iter()
.filter(|rt| {
range_tombstone_overlaps_bounds(
rt,
&union,
state.comparator.as_ref(),
)
})
.map(|rt| (rt.clone(), seqno)),
);
}
multi_runs.push(run.clone());
}
}
}
let mut collect_mt_rts = |iter: alloc::vec::Vec<RangeTombstone>, cutoff: SeqNo| {
rts.extend(
iter.into_iter()
.filter(|rt| range_tombstone_overlaps_bounds(rt, &union, state.comparator.as_ref()))
.map(|rt| (rt, cutoff)),
);
};
for memtable in state.version.sealed_memtables.iter() {
collect_mt_rts(memtable.range_tombstones_sorted(), seqno);
}
collect_mt_rts(
state.version.active_memtable.range_tombstones_sorted(),
seqno,
);
if let Some((mt, eph_seqno)) = &state.ephemeral {
collect_mt_rts(mt.range_tombstones_sorted(), *eph_seqno);
}
rts.sort_by(|a, b| a.0.cmp_with_comparator(&b.0, state.comparator.as_ref()));
rts.dedup_by(|a, b| {
if a.0 == b.0 {
b.1 = b.1.max(a.1);
true
} else {
false
}
});
CollectedSources {
single_tables,
multi_runs,
range_tombstones: rts,
union,
}
}
struct TombstoneSkip<I> {
inner: I,
}
impl<I: Iterator<Item = crate::Result<InternalValue>>> Iterator for TombstoneSkip<I> {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.inner.next()? {
Ok(value) if value.key.is_tombstone() => {}
other => return Some(other),
}
}
}
}
impl<I: DoubleEndedIterator<Item = crate::Result<InternalValue>>> DoubleEndedIterator
for TombstoneSkip<I>
{
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.inner.next_back()? {
Ok(value) if value.key.is_tombstone() => {}
other => return Some(other),
}
}
}
}
impl<I: Reseekable> Reseekable for TombstoneSkip<I> {
fn reseek(&mut self, ctx: &ReseekCtx) {
self.inner.reseek(ctx);
}
}
struct TableLeaf {
table: crate::table::Table,
iter: crate::table::iter::Iter,
seqno: SeqNo,
noted: bool,
}
impl TableLeaf {
fn new(table: crate::table::Table, user_range: UserBounds, seqno: SeqNo) -> Self {
let iter = table.range_iter(user_range);
Self {
table,
iter,
seqno,
noted: false,
}
}
fn note_read(&mut self) {
if !self.noted {
self.table.note_scan();
self.noted = true;
}
}
fn next_filtered(&mut self) -> Option<IterItem> {
loop {
match self.iter.next()? {
Ok(value) => {
self.note_read();
if seqno_filter(value.key.seqno, self.seqno) {
return Some(Ok(value));
}
}
Err(e) => return Some(Err(e)),
}
}
}
fn next_back_filtered(&mut self) -> Option<IterItem> {
loop {
match self.iter.next_back()? {
Ok(value) => {
self.note_read();
if seqno_filter(value.key.seqno, self.seqno) {
return Some(Ok(value));
}
}
Err(e) => return Some(Err(e)),
}
}
}
}
struct RunLeaf {
reader: RunReader,
comparator: SharedComparator,
seqno: SeqNo,
}
impl RunLeaf {
fn new(
run: Arc<Run<crate::table::Table>>,
user_range: UserBounds,
seqno: SeqNo,
comparator: SharedComparator,
) -> Option<Self> {
let reader =
RunReader::new_cmp(run, user_range, comparator.as_ref())?.counting_each_table_once();
Some(Self {
reader,
comparator,
seqno,
})
}
fn next_filtered(&mut self) -> Option<IterItem> {
loop {
match self.reader.next()? {
Ok(value) if seqno_filter(value.key.seqno, self.seqno) => return Some(Ok(value)),
Ok(_) => {}
Err(e) => return Some(Err(e)),
}
}
}
fn next_back_filtered(&mut self) -> Option<IterItem> {
loop {
match self.reader.next_back()? {
Ok(value) if seqno_filter(value.key.seqno, self.seqno) => return Some(Ok(value)),
Ok(_) => {}
Err(e) => return Some(Err(e)),
}
}
}
}
struct MemtableLeaf<'a> {
mt: &'a Memtable,
range: crate::memtable::skiplist::Range<'a>,
seqno: SeqNo,
}
impl<'a> MemtableLeaf<'a> {
fn new(
mt: &'a Memtable,
internal: (Bound<InternalKey>, Bound<InternalKey>),
seqno: SeqNo,
) -> Self {
let range = mt.items.range(internal);
Self { mt, range, seqno }
}
fn next_filtered(&mut self) -> Option<IterItem> {
loop {
let entry = self.range.next()?;
let value = InternalValue {
key: entry.key(),
value: entry.value(),
};
if seqno_filter(value.key.seqno, self.seqno) {
return Some(Ok(value));
}
}
}
fn next_back_filtered(&mut self) -> Option<IterItem> {
loop {
let entry = self.range.next_back()?;
let value = InternalValue {
key: entry.key(),
value: entry.value(),
};
if seqno_filter(value.key.seqno, self.seqno) {
return Some(Ok(value));
}
}
}
}
enum SeekableLeaf<'a> {
Table(Box<TableLeaf>),
Run(Box<RunLeaf>),
Memtable(MemtableLeaf<'a>),
}
impl MergeSource for SeekableLeaf<'_> {
fn next(&mut self) -> Option<IterItem> {
match self {
SeekableLeaf::Table(l) => l.next_filtered(),
SeekableLeaf::Run(l) => l.next_filtered(),
SeekableLeaf::Memtable(l) => l.next_filtered(),
}
}
fn next_back(&mut self) -> Option<IterItem> {
match self {
SeekableLeaf::Table(l) => l.next_back_filtered(),
SeekableLeaf::Run(l) => l.next_back_filtered(),
SeekableLeaf::Memtable(l) => l.next_back_filtered(),
}
}
fn seek(&mut self, _target: &InternalKey) -> crate::Result<()> {
Ok(())
}
}
impl CoherentMergeSource for SeekableLeaf<'_> {}
impl SkipKey for SeekableLeaf<'_> {
fn skip_front_past(&mut self, key: &[u8]) -> crate::Result<bool> {
match self {
SeekableLeaf::Table(l) => l.iter.skip_past(key).map(|_| true),
SeekableLeaf::Run(l) => l.reader.skip_front_past(key),
SeekableLeaf::Memtable(l) => {
l.range.skip_front_past(key);
Ok(true)
}
}
}
fn skip_back_to_newest(&mut self, key: &[u8], seqno: SeqNo) -> crate::Result<bool> {
match self {
SeekableLeaf::Table(l) => l.iter.skip_back_to_newest(key, seqno.min(l.seqno)),
SeekableLeaf::Run(l) => l.reader.skip_back_to_newest(key, seqno.min(l.seqno)),
SeekableLeaf::Memtable(l) => Ok(l.range.skip_back_to_newest(key, seqno.min(l.seqno))),
}
}
}
impl Reseekable for SeekableLeaf<'_> {
fn reseek(&mut self, ctx: &ReseekCtx) {
match self {
SeekableLeaf::Table(l) => l.table.reseek_range(&mut l.iter, ctx.user.clone()),
SeekableLeaf::Run(l) => l.reader.reseek(ctx.user.clone(), l.comparator.as_ref()),
SeekableLeaf::Memtable(l) => {
l.range = l.mt.items.range(ctx.internal.clone());
}
}
}
}
type SeekPipeline<'a> = RangeTombstoneFilter<
TombstoneSkip<
MvccStream<
SeekingMerger<SeekableLeaf<'a>, SharedComparator>,
Option<crate::mvcc_stream::ValueLog<'a>>,
crate::mvcc_stream::Jump,
>,
>,
>;
fn build_seek_pipeline<'a>(
state: &'a IterState,
collected: &CollectedSources,
lower: Bound<UserKey>,
upper: Bound<UserKey>,
seqno: SeqNo,
) -> SeekPipeline<'a> {
let user_range: UserBounds = (lower, upper);
let internal = user_to_internal_bounds(&user_range);
let mut sources: Vec<SeekableLeaf<'a>> =
Vec::with_capacity(collected.single_tables.len() + collected.multi_runs.len() + 3);
if let Some((mt, eph_seqno)) = &state.ephemeral {
sources.push(SeekableLeaf::Memtable(MemtableLeaf::new(
mt,
internal.clone(),
*eph_seqno,
)));
}
sources.push(SeekableLeaf::Memtable(MemtableLeaf::new(
&state.version.active_memtable,
internal.clone(),
seqno,
)));
for memtable in state.version.sealed_memtables.iter().rev() {
sources.push(SeekableLeaf::Memtable(MemtableLeaf::new(
memtable,
internal.clone(),
seqno,
)));
}
state.version.version.read_mix().note_scan();
for table in &collected.single_tables {
sources.push(SeekableLeaf::Table(Box::new(TableLeaf::new(
table.clone(),
user_range.clone(),
seqno,
))));
}
for run in &collected.multi_runs {
if let Some(leaf) = RunLeaf::new(
run.clone(),
user_range.clone(),
seqno,
state.comparator.clone(),
) {
sources.push(SeekableLeaf::Run(Box::new(leaf)));
}
}
let merged = SeekingMerger::new(sources, state.comparator.clone());
let mvcc = MvccStream::new_with_comparator(
merged,
state.merge_operator.clone(),
state.comparator.clone(),
)
.jumping()
.with_value_log(state.value_log())
.with_range_tombstones(collected.range_tombstones.clone());
let skip = TombstoneSkip { inner: mvcc };
RangeTombstoneFilter::new_with_comparator(
skip,
collected.range_tombstones.clone(),
state.comparator.clone(),
)
}
pub struct SeekableOwner {
state: IterState,
collected: Arc<CollectedSources>,
seqno: SeqNo,
lower: Bound<UserKey>,
upper: Bound<UserKey>,
}
self_cell!(
struct SeekableCell {
owner: SeekableOwner,
#[covariant]
dependent: SeekPipeline,
}
);
impl SeekableCell {
fn build(owner: SeekableOwner) -> Self {
Self::new(owner, |o| {
build_seek_pipeline(
&o.state,
&o.collected,
o.lower.clone(),
o.upper.clone(),
o.seqno,
)
})
}
fn reseek(&mut self, ctx: &ReseekCtx) {
self.with_dependent_mut(|_, pipeline| pipeline.reseek(ctx));
}
}
impl Iterator for SeekableCell {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
self.with_dependent_mut(|_, iter| iter.next())
}
}
impl DoubleEndedIterator for SeekableCell {
fn next_back(&mut self) -> Option<Self::Item> {
self.with_dependent_mut(|_, iter| iter.next_back())
}
}
pub struct SeekableTreeIter {
cell: SeekableCell,
#[expect(
clippy::option_option,
reason = "std `Peekable` buffer shape: outer None = not yet peeked, \
inner None = peeked past the end, Some(Some) = buffered item"
)]
peeked: Option<Option<crate::Result<InternalValue>>>,
}
impl SeekableTreeIter {
#[must_use]
pub fn create(
state: IterState,
union_lower: Bound<UserKey>,
union_upper: Bound<UserKey>,
seqno: SeqNo,
) -> Self {
let collected = Arc::new(collect_sources(
&state,
(union_lower.clone(), union_upper.clone()),
seqno,
));
Self {
cell: SeekableCell::build(SeekableOwner {
state,
collected,
seqno,
lower: union_lower,
upper: union_upper,
}),
peeked: None,
}
}
pub(crate) fn reposition(&mut self, lower: Bound<UserKey>, upper: Bound<UserKey>) {
let user_range: UserBounds = (lower, upper);
let internal = user_to_internal_bounds(&user_range);
let ctx = ReseekCtx {
user: user_range,
internal,
};
self.cell.reseek(&ctx);
self.peeked = None;
}
pub fn seek_to(&mut self, key: &[u8]) {
let (lower, upper) = {
let union = &self.cell.borrow_owner().collected.union;
let lower = match &union.0 {
Bound::Included(floor) if floor.as_ref() > key => union.0.clone(),
Bound::Excluded(floor) if floor.as_ref() >= key => union.0.clone(),
_ => Bound::Included(UserKey::from(key)),
};
(lower, union.1.clone())
};
self.reposition(lower, upper);
}
pub fn seek_to_for_prev(&mut self, key: &[u8]) {
let (lower, upper) = {
let union = &self.cell.borrow_owner().collected.union;
let upper = match &union.1 {
Bound::Included(ceil) if ceil.as_ref() < key => union.1.clone(),
Bound::Excluded(ceil) if ceil.as_ref() <= key => union.1.clone(),
_ => Bound::Included(UserKey::from(key)),
};
(union.0.clone(), upper)
};
self.reposition(lower, upper);
}
#[must_use]
pub fn version(&self) -> crate::version::Version {
self.cell.borrow_owner().state.version.version.clone()
}
pub fn peek_key(&mut self) -> Option<crate::Result<UserKey>> {
if self.peeked.is_none() {
self.peeked = Some(self.cell.next());
}
if matches!(self.peeked, Some(Some(Err(_)))) {
return match self.peeked.take() {
Some(Some(Err(e))) => Some(Err(e)),
_ => None,
};
}
match &self.peeked {
Some(Some(Ok(item))) => Some(Ok(item.key.user_key.clone())),
_ => None,
}
}
}
impl Iterator for SeekableTreeIter {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
match self.peeked.take() {
Some(buffered) => buffered,
None => self.cell.next(),
}
}
}
impl DoubleEndedIterator for SeekableTreeIter {
fn next_back(&mut self) -> Option<Self::Item> {
match self.cell.next_back() {
Some(item) => Some(item),
None => self.peeked.take().flatten(),
}
}
}
pub struct BatchRangeScan<I> {
iter: SeekableTreeIter,
intervals: I,
primed: bool,
}
impl<I: Iterator<Item = (Bound<UserKey>, Bound<UserKey>)>> BatchRangeScan<I> {
pub fn new(iter: SeekableTreeIter, intervals: I) -> Self {
Self {
iter,
intervals,
primed: false,
}
}
}
impl<I: Iterator<Item = (Bound<UserKey>, Bound<UserKey>)>> Iterator for BatchRangeScan<I> {
type Item = crate::Result<InternalValue>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.primed {
if let Some(item) = self.iter.next() {
return Some(item);
}
self.primed = false;
}
let (lower, upper) = self.intervals.next()?;
self.iter.reposition(lower, upper);
self.primed = true;
}
}
}
mod point_merge;
pub(crate) use point_merge::{MergeRead, MergeTable, MergeWalk, TableFilter, resolve_point_merge};
#[cfg(test)]
mod tests;