From 8942bb7474969628563fb6e7d2442cfb6bd42ca2 Mon Sep 17 00:00:00 2001 From: Ihor Andrianov Date: Sat, 28 Jun 2025 18:53:44 +0300 Subject: [PATCH 1/3] make find_cell use binary search --- core/storage/btree.rs | 81 +++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/core/storage/btree.rs b/core/storage/btree.rs index 08f7e9a33..a2590df05 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -3846,30 +3846,42 @@ impl BTreeCursor { /// Find the index of the cell in the page that contains the given rowid. fn find_cell(&mut self, page: &PageContent, key: &BTreeKey) -> Result> { - if self.find_cell_state.0.is_none() { - self.find_cell_state.set(0); - } let cell_count = page.cell_count(); - while self.find_cell_state.get_cell_idx() < cell_count as isize { - assert!(self.find_cell_state.get_cell_idx() >= 0); - let cell_idx = self.find_cell_state.get_cell_idx() as usize; - match page - .cell_get( - cell_idx, - payload_overflow_threshold_max(page.page_type(), self.usable_space() as u16), - payload_overflow_threshold_min(page.page_type(), self.usable_space() as u16), - self.usable_space(), - ) - .unwrap() - { + let mut low = 0; + let mut high = if cell_count > 0 { cell_count - 1 } else { 0 }; + let mut result_index = cell_count; + + if self.find_cell_state.0.is_some() { + low = self.find_cell_state.get_cell_idx() as usize; + } + + while low <= high && cell_count > 0 { + let mid = low + (high - low) / 2; + self.find_cell_state.set(mid as isize); + + let cell = match page.cell_get( + mid, + payload_overflow_threshold_max(page.page_type(), self.usable_space() as u16), + payload_overflow_threshold_min(page.page_type(), self.usable_space() as u16), + self.usable_space(), + ) { + Ok(c) => c, + Err(e) => return Err(e), + }; + + let comparison_result = match cell { BTreeCell::TableLeafCell(cell) => { if key.to_rowid() <= cell._rowid { - break; + Ordering::Less + } else { + Ordering::Greater } } BTreeCell::TableInteriorCell(cell) => { if key.to_rowid() <= cell._rowid { - break; + Ordering::Less + } else { + Ordering::Greater } } BTreeCell::IndexInteriorCell(IndexInteriorCell { @@ -3882,41 +3894,44 @@ impl BTreeCursor { payload, first_overflow_page, payload_size, + .. }) => { // TODO: implement efficient comparison of records // e.g. https://github.com/sqlite/sqlite/blob/master/src/vdbeaux.c#L4719 return_if_io!(self.read_record_w_possible_overflow( payload, first_overflow_page, - payload_size, + payload_size )); + let key_values = key.to_index_key_values(); let record = self.get_immutable_record(); let record = record.as_ref().unwrap(); let record_same_number_cols = &record.get_values()[..key_values.len()]; - let order = compare_immutable( + compare_immutable( key_values, record_same_number_cols, self.key_sort_order(), &self.collations, - ); - match order { - Ordering::Less | Ordering::Equal => { - break; - } - Ordering::Greater => {} - } + ) } + }; + + if comparison_result == Ordering::Greater { + low = mid + 1; + } else { + result_index = mid; + if mid == 0 { + break; + } + high = mid - 1; } - let cell_idx = self.find_cell_state.get_cell_idx(); - self.find_cell_state.set(cell_idx + 1); } - let cell_idx = self.find_cell_state.get_cell_idx(); - assert!(cell_idx >= 0); - let cell_idx = cell_idx as usize; - assert!(cell_idx <= cell_count); + self.find_cell_state.reset(); - Ok(CursorResult::Ok(cell_idx)) + assert!(result_index <= cell_count); + + Ok(CursorResult::Ok(result_index)) } pub fn seek_end(&mut self) -> Result> { From 40c14f705fe1bf6d2b16afc49778da730a532d9d Mon Sep 17 00:00:00 2001 From: Ihor Andrianov Date: Sat, 28 Jun 2025 19:51:23 +0300 Subject: [PATCH 2/3] fix equal handling --- core/storage/btree.rs | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/core/storage/btree.rs b/core/storage/btree.rs index a2590df05..a4fda08d4 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -3850,7 +3850,6 @@ impl BTreeCursor { let mut low = 0; let mut high = if cell_count > 0 { cell_count - 1 } else { 0 }; let mut result_index = cell_count; - if self.find_cell_state.0.is_some() { low = self.find_cell_state.get_cell_idx() as usize; } @@ -3870,20 +3869,8 @@ impl BTreeCursor { }; let comparison_result = match cell { - BTreeCell::TableLeafCell(cell) => { - if key.to_rowid() <= cell._rowid { - Ordering::Less - } else { - Ordering::Greater - } - } - BTreeCell::TableInteriorCell(cell) => { - if key.to_rowid() <= cell._rowid { - Ordering::Less - } else { - Ordering::Greater - } - } + BTreeCell::TableLeafCell(cell) => key.to_rowid().cmp(&cell._rowid), + BTreeCell::TableInteriorCell(cell) => key.to_rowid().cmp(&cell._rowid), BTreeCell::IndexInteriorCell(IndexInteriorCell { payload, first_overflow_page, @@ -3917,14 +3904,21 @@ impl BTreeCursor { } }; - if comparison_result == Ordering::Greater { - low = mid + 1; - } else { - result_index = mid; - if mid == 0 { + match comparison_result { + Ordering::Equal => { + result_index = mid; break; } - high = mid - 1; + Ordering::Greater => { + low = mid + 1; + } + Ordering::Less => { + result_index = mid; + if mid == 0 { + break; + } + high = mid - 1; + } } } From 650c85ccd79b1fedae62cc2322efcd7d9760fe50 Mon Sep 17 00:00:00 2001 From: Ihor Andrianov Date: Thu, 3 Jul 2025 15:08:16 +0300 Subject: [PATCH 3/3] save binary search state for reentrant execution --- core/storage/btree.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/storage/btree.rs b/core/storage/btree.rs index a4fda08d4..4d380dbad 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -444,16 +444,16 @@ pub enum CursorSeekState { } #[derive(Debug)] -struct FindCellState(Option); +struct FindCellState(Option<(usize, usize)>); // low, high impl FindCellState { #[inline] - fn set(&mut self, cell_idx: isize) { - self.0 = Some(cell_idx) + fn set(&mut self, lowhigh: (usize, usize)) { + self.0 = Some(lowhigh); } #[inline] - fn get_cell_idx(&mut self) -> isize { + fn get_state(&mut self) -> (usize, usize) { self.0.expect("get can only be called after a set") } @@ -3851,13 +3851,12 @@ impl BTreeCursor { let mut high = if cell_count > 0 { cell_count - 1 } else { 0 }; let mut result_index = cell_count; if self.find_cell_state.0.is_some() { - low = self.find_cell_state.get_cell_idx() as usize; + (low, high) = self.find_cell_state.get_state(); } while low <= high && cell_count > 0 { let mid = low + (high - low) / 2; - self.find_cell_state.set(mid as isize); - + self.find_cell_state.set((low, high)); let cell = match page.cell_get( mid, payload_overflow_threshold_max(page.page_type(), self.usable_space() as u16),