Skip to content

Commit f57ab88

Browse files
committed
chore: make IndexIter stateful to enhance readability
1 parent 60526e1 commit f57ab88

File tree

2 files changed

+102
-86
lines changed

2 files changed

+102
-86
lines changed

src/storage/mod.rs

Lines changed: 100 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ use crate::types::{ColumnId, LogicalType};
1616
use crate::utils::lru::SharedLruCache;
1717
use bytes::Bytes;
1818
use itertools::Itertools;
19-
use std::collections::{Bound, VecDeque};
19+
use std::collections::Bound;
2020
use std::io::Cursor;
2121
use std::mem;
2222
use std::ops::SubAssign;
2323
use std::sync::Arc;
24+
use std::vec::IntoIter;
2425
use ulid::Generator;
2526

2627
pub(crate) type StatisticsMetaCache = SharedLruCache<(TableName, IndexId), StatisticsMeta>;
@@ -127,8 +128,8 @@ pub trait Transaction: Sized {
127128
tx: self,
128129
},
129130
inner,
130-
ranges: VecDeque::from(ranges),
131-
scope_iter: None,
131+
ranges: ranges.into_iter(),
132+
state: IndexIterState::Init,
132133
})
133134
}
134135

@@ -965,8 +966,14 @@ pub struct IndexIter<'a, T: Transaction> {
965966
params: IndexImplParams<'a, T>,
966967
inner: IndexImplEnum,
967968
// for buffering data
968-
ranges: VecDeque<Range>,
969-
scope_iter: Option<T::IterType<'a>>,
969+
ranges: IntoIter<Range>,
970+
state: IndexIterState<'a, T>,
971+
}
972+
973+
pub enum IndexIterState<'a, T: Transaction + 'a> {
974+
Init,
975+
Range(T::IterType<'a>),
976+
Over,
970977
}
971978

972979
impl<'a, T: Transaction + 'a> IndexIter<'a, T> {
@@ -985,102 +992,111 @@ impl<'a, T: Transaction + 'a> IndexIter<'a, T> {
985992
num.sub_assign(1);
986993
}
987994
}
988-
989-
fn is_empty(&self) -> bool {
990-
self.scope_iter.is_none() && self.ranges.is_empty()
991-
}
992995
}
993996

994997
/// expression -> index value -> tuple
995998
impl<T: Transaction> Iter for IndexIter<'_, T> {
996999
fn next_tuple(&mut self) -> Result<Option<Tuple>, DatabaseError> {
997-
if matches!(self.limit, Some(0)) || self.is_empty() {
998-
self.scope_iter = None;
999-
self.ranges.clear();
1000+
if matches!(self.limit, Some(0)) {
1001+
self.state = IndexIterState::Over;
10001002

10011003
return Ok(None);
10021004
}
10031005

1004-
if let Some(iter) = &mut self.scope_iter {
1005-
while let Some((_, bytes)) = iter.try_next()? {
1006-
if Self::offset_move(&mut self.offset) {
1007-
continue;
1008-
}
1009-
Self::limit_sub(&mut self.limit);
1010-
let tuple = self
1011-
.inner
1012-
.index_lookup(&bytes, &mut self.id_builder, &self.params)?;
1013-
1014-
return Ok(Some(tuple));
1015-
}
1016-
self.scope_iter = None;
1017-
}
1018-
1019-
if let Some(binary) = self.ranges.pop_front() {
1020-
match binary {
1021-
Range::Scope { min, max } => {
1022-
let table_name = self.params.table_name;
1023-
let index_meta = &self.params.index_meta;
1024-
let bound_encode = |bound: Bound<DataValue>| -> Result<_, DatabaseError> {
1025-
match bound {
1026-
Bound::Included(mut val) => {
1027-
val = self.params.try_cast(val)?;
1028-
1029-
Ok(Bound::Included(self.inner.bound_key(&self.params, &val)?))
1030-
}
1031-
Bound::Excluded(mut val) => {
1032-
val = self.params.try_cast(val)?;
1033-
1034-
Ok(Bound::Excluded(self.inner.bound_key(&self.params, &val)?))
1035-
}
1036-
Bound::Unbounded => Ok(Bound::Unbounded),
1037-
}
1006+
loop {
1007+
match &mut self.state {
1008+
IndexIterState::Init => {
1009+
let Some(binary) = self.ranges.next() else {
1010+
self.state = IndexIterState::Over;
1011+
continue;
10381012
};
1039-
let (bound_min, bound_max) =
1040-
if matches!(index_meta.ty, IndexType::PrimaryKey { .. }) {
1041-
TableCodec::tuple_bound(table_name)
1042-
} else {
1043-
TableCodec::index_bound(table_name, &index_meta.id)?
1044-
};
1045-
let check_bound = |value: &mut Bound<Vec<u8>>, bound: Vec<u8>| {
1046-
if matches!(value, Bound::Unbounded) {
1047-
let _ = mem::replace(value, Bound::Included(bound));
1013+
match binary {
1014+
Range::Scope { min, max } => {
1015+
let table_name = self.params.table_name;
1016+
let index_meta = &self.params.index_meta;
1017+
let bound_encode =
1018+
|bound: Bound<DataValue>| -> Result<_, DatabaseError> {
1019+
match bound {
1020+
Bound::Included(mut val) => {
1021+
val = self.params.try_cast(val)?;
1022+
1023+
Ok(Bound::Included(
1024+
self.inner.bound_key(&self.params, &val)?,
1025+
))
1026+
}
1027+
Bound::Excluded(mut val) => {
1028+
val = self.params.try_cast(val)?;
1029+
1030+
Ok(Bound::Excluded(
1031+
self.inner.bound_key(&self.params, &val)?,
1032+
))
1033+
}
1034+
Bound::Unbounded => Ok(Bound::Unbounded),
1035+
}
1036+
};
1037+
let (bound_min, bound_max) =
1038+
if matches!(index_meta.ty, IndexType::PrimaryKey { .. }) {
1039+
TableCodec::tuple_bound(table_name)
1040+
} else {
1041+
TableCodec::index_bound(table_name, &index_meta.id)?
1042+
};
1043+
let check_bound = |value: &mut Bound<Vec<u8>>, bound: Vec<u8>| {
1044+
if matches!(value, Bound::Unbounded) {
1045+
let _ = mem::replace(value, Bound::Included(bound));
1046+
}
1047+
};
1048+
1049+
let mut encode_min = bound_encode(min)?;
1050+
check_bound(&mut encode_min, bound_min);
1051+
1052+
let mut encode_max = bound_encode(max)?;
1053+
check_bound(&mut encode_max, bound_max);
1054+
1055+
let iter = self.params.tx.range(
1056+
encode_min.as_ref().map(Vec::as_slice),
1057+
encode_max.as_ref().map(Vec::as_slice),
1058+
)?;
1059+
self.state = IndexIterState::Range(iter);
10481060
}
1049-
};
1050-
1051-
let mut encode_min = bound_encode(min)?;
1052-
check_bound(&mut encode_min, bound_min);
1053-
1054-
let mut encode_max = bound_encode(max)?;
1055-
check_bound(&mut encode_max, bound_max);
1056-
1057-
let iter = self.params.tx.range(
1058-
encode_min.as_ref().map(Vec::as_slice),
1059-
encode_max.as_ref().map(Vec::as_slice),
1060-
)?;
1061-
self.scope_iter = Some(iter);
1062-
}
1063-
Range::Eq(mut val) => {
1064-
val = self.params.try_cast(val)?;
1065-
1066-
match self
1067-
.inner
1068-
.eq_to_res(&val, &mut self.id_builder, &self.params)?
1069-
{
1070-
IndexResult::Tuple(tuple) => {
1071-
if Self::offset_move(&mut self.offset) {
1072-
return self.next_tuple();
1061+
Range::Eq(mut val) => {
1062+
val = self.params.try_cast(val)?;
1063+
1064+
match self
1065+
.inner
1066+
.eq_to_res(&val, &mut self.id_builder, &self.params)?
1067+
{
1068+
IndexResult::Tuple(tuple) => {
1069+
if Self::offset_move(&mut self.offset) {
1070+
continue;
1071+
}
1072+
Self::limit_sub(&mut self.limit);
1073+
return Ok(tuple);
1074+
}
1075+
IndexResult::Scope(iter) => {
1076+
self.state = IndexIterState::Range(iter);
1077+
}
10731078
}
1074-
Self::limit_sub(&mut self.limit);
1075-
return Ok(tuple);
10761079
}
1077-
IndexResult::Scope(iter) => self.scope_iter = Some(iter),
1080+
_ => (),
1081+
}
1082+
}
1083+
IndexIterState::Range(iter) => {
1084+
while let Some((_, bytes)) = iter.try_next()? {
1085+
if Self::offset_move(&mut self.offset) {
1086+
continue;
1087+
}
1088+
Self::limit_sub(&mut self.limit);
1089+
let tuple =
1090+
self.inner
1091+
.index_lookup(&bytes, &mut self.id_builder, &self.params)?;
1092+
1093+
return Ok(Some(tuple));
10781094
}
1095+
self.state = IndexIterState::Init;
10791096
}
1080-
_ => (),
1097+
IndexIterState::Over => return Ok(None),
10811098
}
10821099
}
1083-
self.next_tuple()
10841100
}
10851101
}
10861102

tpcc/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ use clap::Parser;
1010
use fnck_sql::db::{DBTransaction, DataBaseBuilder, Statement};
1111
use fnck_sql::errors::DatabaseError;
1212
use fnck_sql::storage::Storage;
13+
use fnck_sql::types::tuple::create_table;
1314
use rand::prelude::ThreadRng;
1415
use rand::Rng;
1516
use std::time::{Duration, Instant};
16-
use fnck_sql::types::tuple::create_table;
1717

1818
mod delivery;
1919
mod load;
@@ -320,7 +320,7 @@ pub enum TpccError {
320320
}
321321

322322
#[test]
323-
fn debug_tpcc() -> Result<(), DatabaseError> {
323+
fn explain_tpcc() -> Result<(), DatabaseError> {
324324
let database = DataBaseBuilder::path("./fnck_sql_tpcc").build()?;
325325
let mut tx = database.new_transaction()?;
326326

0 commit comments

Comments
 (0)