From 8acbe3de663b64190f5200a80a7906a876d4b07d Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Mon, 27 Oct 2025 23:07:37 +0400 Subject: [PATCH] make query_start method to return bool - if result will have some rows or not --- core/index_method/mod.rs | 4 +++- core/index_method/toy_vector_sparse_ivf.rs | 21 ++++++++------------- core/translate/main_loop.rs | 5 +---- core/vdbe/builder.rs | 3 +++ core/vdbe/execute.rs | 9 +++++++-- core/vdbe/insn.rs | 1 + 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/core/index_method/mod.rs b/core/index_method/mod.rs index 38b47084a..8f3b5097d 100644 --- a/core/index_method/mod.rs +++ b/core/index_method/mod.rs @@ -87,7 +87,9 @@ pub trait IndexMethodCursor { /// For example, for 2 patterns ["SELECT * FROM {table} LIMIT ?", "SELECT * FROM {table} WHERE x = ?"], query_start(...) call can have following arguments: /// - [Integer(0), Integer(10)] - pattern "SELECT * FROM {table} LIMIT ?" was chosen with LIMIT parameter equals to 10 /// - [Integer(1), Text("turso")] - pattern "SELECT * FROM {table} WHERE x = ?" was chosen with equality comparison equals to "turso" - fn query_start(&mut self, values: &[Register]) -> Result>; + /// + /// Returns false if query will produce no rows (similar to VFilter/Rewind op codes) + fn query_start(&mut self, values: &[Register]) -> Result>; /// Moves cursor to the next response row /// Returns false if query exhausted all rows diff --git a/core/index_method/toy_vector_sparse_ivf.rs b/core/index_method/toy_vector_sparse_ivf.rs index 87fa18121..2e169ddc6 100644 --- a/core/index_method/toy_vector_sparse_ivf.rs +++ b/core/index_method/toy_vector_sparse_ivf.rs @@ -151,7 +151,6 @@ pub struct VectorSparseInvertedIndexMethodCursor { delete_state: VectorSparseInvertedIndexDeleteState, search_state: VectorSparseInvertedIndexSearchState, search_result: VecDeque<(i64, f64)>, - search_row: Option<(i64, f64)>, } impl IndexMethod for VectorSparseInvertedIndexMethod { @@ -199,7 +198,6 @@ impl VectorSparseInvertedIndexMethodCursor { scratch_cursor: None, main_btree: None, search_result: VecDeque::new(), - search_row: None, create_state: VectorSparseInvertedIndexCreateState::Init, insert_state: VectorSparseInvertedIndexInsertState::Init, delete_state: VectorSparseInvertedIndexDeleteState::Init, @@ -459,7 +457,7 @@ impl IndexMethodCursor for VectorSparseInvertedIndexMethodCursor { } } - fn query_start(&mut self, values: &[Register]) -> Result> { + fn query_start(&mut self, values: &[Register]) -> Result> { let Some(scratch) = &mut self.scratch_cursor else { return Err(LimboError::InternalError( "cursor must be opened".to_string(), @@ -521,7 +519,7 @@ impl IndexMethodCursor for VectorSparseInvertedIndexMethodCursor { continue; } let position = p[*idx]; - let key = ImmutableRecord::from_values(&[Value::Integer(position as i64)], 2); + let key = ImmutableRecord::from_values(&[Value::Integer(position as i64)], 1); self.search_state = VectorSparseInvertedIndexSearchState::Seek { collected: collected.take(), positions: positions.take(), @@ -551,7 +549,7 @@ impl IndexMethodCursor for VectorSparseInvertedIndexMethodCursor { limit: *limit, }; } - SeekResult::TryAdvance => { + SeekResult::TryAdvance | SeekResult::NotFound => { self.search_state = VectorSparseInvertedIndexSearchState::Next { collected: collected.take(), positions: positions.take(), @@ -560,9 +558,6 @@ impl IndexMethodCursor for VectorSparseInvertedIndexMethodCursor { limit: *limit, }; } - SeekResult::NotFound => { - return Err(LimboError::Corrupt("inverted index corrupted".to_string())) - } } } VectorSparseInvertedIndexSearchState::Read { @@ -637,7 +632,7 @@ impl IndexMethodCursor for VectorSparseInvertedIndexMethodCursor { let Some(rowid) = rowids.as_ref().unwrap().last() else { let distances = distances.take().unwrap(); self.search_result = distances.iter().map(|(d, i)| (*i, d.0)).collect(); - return Ok(IOResult::Done(())); + return Ok(IOResult::Done(!self.search_result.is_empty())); }; let result = return_if_io!( main.seek(SeekKey::TableRowId(*rowid), SeekOp::GE { eq_only: true }) @@ -709,17 +704,17 @@ impl IndexMethodCursor for VectorSparseInvertedIndexMethodCursor { } fn query_rowid(&mut self) -> Result>> { - let result = self.search_row.as_ref().unwrap(); + let result = self.search_result.front().unwrap(); Ok(IOResult::Done(Some(result.0))) } fn query_column(&mut self, _: usize) -> Result> { - let result = self.search_row.as_ref().unwrap(); + let result = self.search_result.front().unwrap(); Ok(IOResult::Done(Value::Float(result.1))) } fn query_next(&mut self) -> Result> { - self.search_row = self.search_result.pop_front(); - Ok(IOResult::Done(self.search_row.is_some())) + let _ = self.search_result.pop_front(); + Ok(IOResult::Done(!self.search_result.is_empty())) } } diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index 3f6a78a76..8cdedf6f8 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -720,10 +720,7 @@ pub fn open_loop( cursor_id: index_cursor_id.expect("IndexMethod requires a index cursor"), start_reg, count_reg: query.arguments.len() + 1, - }); - program.emit_insn(Insn::Next { - cursor_id: index_cursor_id.expect("IndexMethod requires a index cursor"), - pc_if_next: loop_end, + pc_if_empty: loop_end, }); program.preassign_label_to_next_insn(loop_start); if let Some(table_cursor_id) = table_cursor_id { diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs index ba8f1d200..7ca227198 100644 --- a/core/vdbe/builder.rs +++ b/core/vdbe/builder.rs @@ -819,6 +819,9 @@ impl ProgramBuilder { Insn::IdxLT { target_pc, .. } => { resolve(target_pc, "IdxLT"); } + Insn::IndexMethodQuery { pc_if_empty, .. } => { + resolve(pc_if_empty, "IndexMethodQuery"); + } Insn::IsNull { reg: _, target_pc } => { resolve(target_pc, "IsNull"); } diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 8050e6f2b..b2386ffa3 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -7248,6 +7248,7 @@ pub fn op_index_method_query( cursor_id, start_reg, count_reg, + pc_if_empty, }, insn ); @@ -7260,8 +7261,12 @@ pub fn op_index_method_query( } let cursor = state.cursors[*cursor_id].as_mut().unwrap(); let cursor = cursor.as_index_method_mut(); - return_if_io!(cursor.query_start(&state.registers[*start_reg..*start_reg + *count_reg])); - state.pc += 1; + let has_rows = return_if_io!(cursor.query_start(&state.registers[*start_reg..*start_reg + *count_reg])); + if !has_rows { + state.pc = pc_if_empty.as_offset_int(); + } else { + state.pc += 1; + } Ok(InsnFunctionStepResult::Step) } diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index b9f9cc622..98693d718 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -895,6 +895,7 @@ pub enum Insn { cursor_id: CursorID, start_reg: usize, count_reg: usize, + pc_if_empty: BranchOffset, }, /// Deletes an entire database table or index whose root page in the database file is given by P1.