diff --git a/core/storage/btree.rs b/core/storage/btree.rs index 081c229ed..b7dcaa5b2 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -2198,7 +2198,6 @@ impl BTreeCursor { SeekOp::LE { eq_only: false } => cmp.is_le(), SeekOp::LT => cmp.is_lt(), }; - (cmp, found) } diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index be98f6476..fd943a18f 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -1375,6 +1375,15 @@ fn emit_seek( } } let num_regs = seek_def.size(&seek_def.start); + + program.emit_insn(Insn::Affinity { + start_reg, + count: std::num::NonZeroUsize::new(num_regs).unwrap(), + affinities: seek_def + .iter_affinity(&seek_def.start) + .map(|affinity| affinity.aff_mask()) + .collect(), + }); match seek_def.start.op { SeekOp::GE { eq_only } => program.emit_insn(Insn::SeekGE { is_index, diff --git a/core/translate/optimizer/constraints.rs b/core/translate/optimizer/constraints.rs index 21fe6407a..80afeac68 100644 --- a/core/translate/optimizer/constraints.rs +++ b/core/translate/optimizer/constraints.rs @@ -8,11 +8,12 @@ use crate::{ schema::{Column, Index}, translate::{ collate::get_collseq_from_expr, - expr::as_binary_components, + expr::{as_binary_components, comparison_affinity}, plan::{JoinOrderMember, NonFromClauseSubquery, TableReferences, WhereTerm}, planner::{table_mask_from_expr, TableMask}, }, util::exprs_are_equivalent, + vdbe::affinity::Affinity, Result, }; use turso_ext::{ConstraintInfo, ConstraintOp}; @@ -68,16 +69,24 @@ pub enum BinaryExprSide { impl Constraint { /// Get the constraining expression and operator, e.g. ('>=', '2+3') from 't.x >= 2+3' - pub fn get_constraining_expr(&self, where_clause: &[WhereTerm]) -> (ast::Operator, ast::Expr) { + pub fn get_constraining_expr( + &self, + where_clause: &[WhereTerm], + referenced_tables: Option<&TableReferences>, + ) -> (ast::Operator, ast::Expr, Affinity) { let (idx, side) = self.where_clause_pos; let where_term = &where_clause[idx]; - let Ok(Some((lhs, _, rhs))) = as_binary_components(&where_term.expr) else { + let Ok(Some((lhs, op, rhs))) = as_binary_components(&where_term.expr) else { panic!("Expected a valid binary expression"); }; + let mut affinity = Affinity::Blob; + if op.is_comparison() { + affinity = comparison_affinity(lhs, rhs, referenced_tables); + } if side == BinaryExprSide::Lhs { - (self.operator, lhs.clone()) + (self.operator, lhs.clone(), affinity) } else { - (self.operator, rhs.clone()) + (self.operator, rhs.clone(), affinity) } } @@ -416,13 +425,13 @@ pub struct RangeConstraintRef { /// Represent seek range which can be used in query planning to emit range scan over table or index pub struct SeekRangeConstraint { pub sort_order: SortOrder, - pub eq: Option<(ast::Operator, ast::Expr)>, - pub lower_bound: Option<(ast::Operator, ast::Expr)>, - pub upper_bound: Option<(ast::Operator, ast::Expr)>, + pub eq: Option<(ast::Operator, ast::Expr, Affinity)>, + pub lower_bound: Option<(ast::Operator, ast::Expr, Affinity)>, + pub upper_bound: Option<(ast::Operator, ast::Expr, Affinity)>, } impl SeekRangeConstraint { - pub fn new_eq(sort_order: SortOrder, eq: (ast::Operator, ast::Expr)) -> Self { + pub fn new_eq(sort_order: SortOrder, eq: (ast::Operator, ast::Expr, Affinity)) -> Self { Self { sort_order, eq: Some(eq), @@ -432,8 +441,8 @@ impl SeekRangeConstraint { } pub fn new_range( sort_order: SortOrder, - lower_bound: Option<(ast::Operator, ast::Expr)>, - upper_bound: Option<(ast::Operator, ast::Expr)>, + lower_bound: Option<(ast::Operator, ast::Expr, Affinity)>, + upper_bound: Option<(ast::Operator, ast::Expr, Affinity)>, ) -> Self { assert!(lower_bound.is_some() || upper_bound.is_some()); Self { @@ -451,19 +460,20 @@ impl RangeConstraintRef { &self, constraints: &[Constraint], where_clause: &[WhereTerm], + referenced_tables: Option<&TableReferences>, ) -> SeekRangeConstraint { if let Some(eq) = self.eq { return SeekRangeConstraint::new_eq( self.sort_order, - constraints[eq].get_constraining_expr(where_clause), + constraints[eq].get_constraining_expr(where_clause, referenced_tables), ); } SeekRangeConstraint::new_range( self.sort_order, self.lower_bound - .map(|x| constraints[x].get_constraining_expr(where_clause)), + .map(|x| constraints[x].get_constraining_expr(where_clause, referenced_tables)), self.upper_bound - .map(|x| constraints[x].get_constraining_expr(where_clause)), + .map(|x| constraints[x].get_constraining_expr(where_clause, referenced_tables)), ) } } diff --git a/core/translate/optimizer/mod.rs b/core/translate/optimizer/mod.rs index 6c36cb2ac..2b79c1463 100644 --- a/core/translate/optimizer/mod.rs +++ b/core/translate/optimizer/mod.rs @@ -32,7 +32,10 @@ use crate::{ util::{ exprs_are_equivalent, simple_bind_expr, try_capture_parameters, try_substitute_parameters, }, - vdbe::builder::{CursorKey, CursorType, ProgramBuilder}, + vdbe::{ + affinity::Affinity, + builder::{CursorKey, CursorType, ProgramBuilder}, + }, LimboError, Result, }; @@ -611,8 +614,6 @@ fn optimize_table_access( best_ordered_plan, } = best_join_order_result; - let joined_tables = table_references.joined_tables_mut(); - // See if best_ordered_plan is better than the overall best_plan if we add a sorting penalty // to the unordered plan's cost. let best_plan = if let Some(best_ordered_plan) = best_ordered_plan { @@ -635,7 +636,7 @@ fn optimize_table_access( let satisfies_order_target = plan_satisfies_order_target( &best_plan, &access_methods_arena, - joined_tables, + table_references.joined_tables_mut(), &order_target, ); if satisfies_order_target { @@ -662,9 +663,9 @@ fn optimize_table_access( let best_join_order: Vec = best_table_numbers .into_iter() .map(|table_number| JoinOrderMember { - table_id: joined_tables[table_number].internal_id, + table_id: table_references.joined_tables_mut()[table_number].internal_id, original_idx: table_number, - is_outer: joined_tables[table_number] + is_outer: table_references.joined_tables_mut()[table_number] .join_info .as_ref() .is_some_and(|join_info| join_info.outer), @@ -675,7 +676,6 @@ fn optimize_table_access( for (i, join_order_member) in best_join_order.iter().enumerate() { let table_idx = join_order_member.original_idx; let access_method = &access_methods_arena.borrow()[best_access_methods[i]]; - match &access_method.params { AccessMethodParams::BTreeTable { iter_dir, @@ -692,10 +692,11 @@ fn optimize_table_access( }; if !try_to_build_ephemeral_index { - joined_tables[table_idx].op = Operation::Scan(Scan::BTreeTable { - iter_dir: *iter_dir, - index: index.clone(), - }); + table_references.joined_tables_mut()[table_idx].op = + Operation::Scan(Scan::BTreeTable { + iter_dir: *iter_dir, + index: index.clone(), + }); continue; } // This branch means we have a full table scan for a non-outermost table. @@ -704,10 +705,11 @@ fn optimize_table_access( .iter() .find(|c| c.table_id == join_order_member.table_id); let Some(table_constraints) = table_constraints else { - joined_tables[table_idx].op = Operation::Scan(Scan::BTreeTable { - iter_dir: *iter_dir, - index: index.clone(), - }); + table_references.joined_tables_mut()[table_idx].op = + Operation::Scan(Scan::BTreeTable { + iter_dir: *iter_dir, + index: index.clone(), + }); continue; }; let usable_constraints = table_constraints @@ -731,26 +733,31 @@ fn optimize_table_access( &best_join_order[..=i], ); if usable_constraint_refs.is_empty() { - joined_tables[table_idx].op = Operation::Scan(Scan::BTreeTable { - iter_dir: *iter_dir, - index: index.clone(), - }); + table_references.joined_tables_mut()[table_idx].op = + Operation::Scan(Scan::BTreeTable { + iter_dir: *iter_dir, + index: index.clone(), + }); continue; } - let ephemeral_index = - ephemeral_index_build(&joined_tables[table_idx], &usable_constraint_refs); + let ephemeral_index = ephemeral_index_build( + &table_references.joined_tables_mut()[table_idx], + &usable_constraint_refs, + ); let ephemeral_index = Arc::new(ephemeral_index); - joined_tables[table_idx].op = Operation::Search(Search::Seek { - index: Some(ephemeral_index), - seek_def: build_seek_def_from_constraints( - &table_constraints.constraints, - &usable_constraint_refs, - *iter_dir, - where_clause, - )?, - }); + table_references.joined_tables_mut()[table_idx].op = + Operation::Search(Search::Seek { + index: Some(ephemeral_index), + seek_def: build_seek_def_from_constraints( + &table_constraints.constraints, + &usable_constraint_refs, + *iter_dir, + where_clause, + Some(table_references), + )?, + }); } else { - let is_outer_join = joined_tables[table_idx] + let is_outer_join = table_references.joined_tables_mut()[table_idx] .join_info .as_ref() .is_some_and(|join_info| join_info.outer); @@ -780,38 +787,42 @@ fn optimize_table_access( } } if let Some(index) = &index { - joined_tables[table_idx].op = Operation::Search(Search::Seek { - index: Some(index.clone()), - seek_def: build_seek_def_from_constraints( - &constraints_per_table[table_idx].constraints, - constraint_refs, - *iter_dir, - where_clause, - )?, - }); + table_references.joined_tables_mut()[table_idx].op = + Operation::Search(Search::Seek { + index: Some(index.clone()), + seek_def: build_seek_def_from_constraints( + &constraints_per_table[table_idx].constraints, + constraint_refs, + *iter_dir, + where_clause, + Some(table_references), + )?, + }); continue; } assert!( constraint_refs.len() == 1, "expected exactly one constraint for rowid seek, got {constraint_refs:?}" ); - joined_tables[table_idx].op = if let Some(eq) = constraint_refs[0].eq { - Operation::Search(Search::RowidEq { - cmp_expr: constraints_per_table[table_idx].constraints[eq] - .get_constraining_expr(where_clause) - .1, - }) - } else { - Operation::Search(Search::Seek { - index: None, - seek_def: build_seek_def_from_constraints( - &constraints_per_table[table_idx].constraints, - constraint_refs, - *iter_dir, - where_clause, - )?, - }) - }; + table_references.joined_tables_mut()[table_idx].op = + if let Some(eq) = constraint_refs[0].eq { + Operation::Search(Search::RowidEq { + cmp_expr: constraints_per_table[table_idx].constraints[eq] + .get_constraining_expr(where_clause, Some(table_references)) + .1, + }) + } else { + Operation::Search(Search::Seek { + index: None, + seek_def: build_seek_def_from_constraints( + &constraints_per_table[table_idx].constraints, + constraint_refs, + *iter_dir, + where_clause, + Some(table_references), + )?, + }) + }; } } AccessMethodParams::VirtualTable { @@ -820,17 +831,19 @@ fn optimize_table_access( constraints, constraint_usages, } => { - joined_tables[table_idx].op = build_vtab_scan_op( + table_references.joined_tables_mut()[table_idx].op = build_vtab_scan_op( where_clause, &constraints_per_table[table_idx], idx_num, idx_str, constraints, constraint_usages, + Some(table_references), )?; } AccessMethodParams::Subquery => { - joined_tables[table_idx].op = Operation::Scan(Scan::Subquery); + table_references.joined_tables_mut()[table_idx].op = + Operation::Scan(Scan::Subquery); } } } @@ -845,6 +858,7 @@ fn build_vtab_scan_op( idx_str: &Option, vtab_constraints: &[ConstraintInfo], constraint_usages: &[ConstraintUsage], + referenced_tables: Option<&TableReferences>, ) -> Result { if constraint_usages.len() != vtab_constraints.len() { return Err(LimboError::ExtensionError(format!( @@ -882,7 +896,7 @@ fn build_vtab_scan_op( if usage.omit { where_clause[constraint.where_clause_pos.0].consumed = true; } - let (_, expr) = constraint.get_constraining_expr(where_clause); + let (_, expr, _) = constraint.get_constraining_expr(where_clause, referenced_tables); constraints[zero_based_argv_index] = Some(expr); arg_count += 1; } @@ -1306,6 +1320,7 @@ pub fn build_seek_def_from_constraints( constraint_refs: &[RangeConstraintRef], iter_dir: IterationDirection, where_clause: &[WhereTerm], + referenced_tables: Option<&TableReferences>, ) -> Result { assert!( !constraint_refs.is_empty(), @@ -1314,7 +1329,7 @@ pub fn build_seek_def_from_constraints( // Extract the key values and operators let key = constraint_refs .iter() - .map(|cref| cref.as_seek_range_constraint(constraints, where_clause)) + .map(|cref| cref.as_seek_range_constraint(constraints, where_clause, referenced_tables)) .collect(); let seek_def = build_seek_def(iter_dir, key)?; @@ -1365,10 +1380,12 @@ fn build_seek_def( start: SeekKey { last_component: SeekKeyComponent::None, op: start_op, + affinity: Affinity::Blob, }, end: SeekKey { last_component: SeekKeyComponent::None, op: end_op, + affinity: Affinity::Blob, }, }); } @@ -1392,46 +1409,52 @@ fn build_seek_def( let start = match last.lower_bound { // Forwards, Asc, GT: (x=10 AND y>20) // Start key: start from the first GT(x:10, y:20) - Some((ast::Operator::Greater, bound)) => SeekKey { + Some((ast::Operator::Greater, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GT, + affinity, }, // Forwards, Asc, GE: (x=10 AND y>=20) // Start key: start from the first GE(x:10, y:20) - Some((ast::Operator::GreaterEquals, bound)) => SeekKey { + Some((ast::Operator::GreaterEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GE { eq_only: false }, + affinity, }, // Forwards, Asc, None, (x=10 AND y<30) // Start key: start from the first GE(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::GE { eq_only: false }, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; let end = match last.upper_bound { // Forwards, Asc, LT, (x=10 AND y<30) // End key: end at first GE(x:10, y:30) - Some((ast::Operator::Less, bound)) => SeekKey { + Some((ast::Operator::Less, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GE { eq_only: false }, + affinity, }, // Forwards, Asc, LE, (x=10 AND y<=30) // End key: end at first GT(x:10, y:30) - Some((ast::Operator::LessEquals, bound)) => SeekKey { + Some((ast::Operator::LessEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GT, + affinity, }, // Forwards, Asc, None, (x=10 AND y>20) // End key: end at first GT(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::GT, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; @@ -1441,46 +1464,52 @@ fn build_seek_def( let start = match last.upper_bound { // Forwards, Desc, LT: (x=10 AND y<30) // Start key: start from the first GT(x:10, y:30) - Some((ast::Operator::Less, bound)) => SeekKey { + Some((ast::Operator::Less, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GT, + affinity, }, // Forwards, Desc, LE: (x=10 AND y<=30) // Start key: start from the first GE(x:10, y:30) - Some((ast::Operator::LessEquals, bound)) => SeekKey { + Some((ast::Operator::LessEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GE { eq_only: false }, + affinity, }, // Forwards, Desc, None: (x=10 AND y>20) // Start key: start from the first GE(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::GE { eq_only: false }, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; let end = match last.lower_bound { // Forwards, Asc, GT, (x=10 AND y>20) // End key: end at first GE(x:10, y:20) - Some((ast::Operator::Greater, bound)) => SeekKey { + Some((ast::Operator::Greater, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GE { eq_only: false }, + affinity, }, // Forwards, Asc, GE, (x=10 AND y>=20) // End key: end at first GT(x:10, y:20) - Some((ast::Operator::GreaterEquals, bound)) => SeekKey { + Some((ast::Operator::GreaterEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::GT, + affinity, }, // Forwards, Asc, None, (x=10 AND y<30) // End key: end at first GT(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::GT, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; @@ -1500,46 +1529,52 @@ fn build_seek_def( let start = match last.upper_bound { // Backwards, Asc, LT: (x=10 AND y<30) // Start key: start from the first LT(x:10, y:30) - Some((ast::Operator::Less, bound)) => SeekKey { + Some((ast::Operator::Less, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LT, + affinity, }, // Backwards, Asc, LT: (x=10 AND y<=30) // Start key: start from the first LE(x:10, y:30) - Some((ast::Operator::LessEquals, bound)) => SeekKey { + Some((ast::Operator::LessEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LE { eq_only: false }, + affinity, }, // Backwards, Asc, None: (x=10 AND y>20) // Start key: start from the first LE(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::LE { eq_only: false }, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op) } }; let end = match last.lower_bound { // Backwards, Asc, GT, (x=10 AND y>20) // End key: end at first LE(x:10, y:20) - Some((ast::Operator::Greater, bound)) => SeekKey { + Some((ast::Operator::Greater, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LE { eq_only: false }, + affinity, }, // Backwards, Asc, GT, (x=10 AND y>=20) // End key: end at first LT(x:10, y:20) - Some((ast::Operator::GreaterEquals, bound)) => SeekKey { + Some((ast::Operator::GreaterEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LT, + affinity, }, // Backwards, Asc, None, (x=10 AND y<30) // End key: end at first LT(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::LT, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; @@ -1549,46 +1584,52 @@ fn build_seek_def( let start = match last.lower_bound { // Backwards, Desc, LT: (x=10 AND y>20) // Start key: start from the first LT(x:10, y:20) - Some((ast::Operator::Greater, bound)) => SeekKey { + Some((ast::Operator::Greater, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LT, + affinity, }, // Backwards, Desc, LE: (x=10 AND y>=20) // Start key: start from the first LE(x:10, y:20) - Some((ast::Operator::GreaterEquals, bound)) => SeekKey { + Some((ast::Operator::GreaterEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LE { eq_only: false }, + affinity, }, // Backwards, Desc, LE: (x=10 AND y<30) // Start key: start from the first LE(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::LE { eq_only: false }, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; let end = match last.upper_bound { // Backwards, Desc, LT, (x=10 AND y<30) // End key: end at first LE(x:10, y:30) - Some((ast::Operator::Less, bound)) => SeekKey { + Some((ast::Operator::Less, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LE { eq_only: false }, + affinity, }, // Backwards, Desc, LT, (x=10 AND y<=30) // End key: end at first LT(x:10, y:30) - Some((ast::Operator::LessEquals, bound)) => SeekKey { + Some((ast::Operator::LessEquals, bound, affinity)) => SeekKey { last_component: SeekKeyComponent::Expr(bound), op: SeekOp::LT, + affinity, }, // Backwards, Desc, LT, (x=10 AND y>20) // End key: end at first LT(x:10) None => SeekKey { last_component: SeekKeyComponent::None, op: SeekOp::LT, + affinity: Affinity::Blob, }, - Some((op, _)) => { + Some((op, _, _)) => { crate::bail_parse_error!("build_seek_def: invalid operator: {:?}", op,) } }; diff --git a/core/translate/plan.rs b/core/translate/plan.rs index 7a18b63a6..775bdfa67 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, collections::HashMap, sync::Arc}; +use std::{cmp::Ordering, collections::HashMap, marker::PhantomData, sync::Arc}; use turso_parser::ast::{ self, FrameBound, FrameClause, FrameExclude, FrameMode, SortOrder, SubqueryType, }; @@ -11,6 +11,7 @@ use crate::{ optimizer::constraints::SeekRangeConstraint, }, vdbe::{ + affinity::Affinity, builder::{CursorKey, CursorType, ProgramBuilder}, insn::{IdxInsertFlags, Insn}, BranchOffset, CursorID, @@ -1128,13 +1129,14 @@ pub struct SeekDef { pub iter_dir: IterationDirection, } -pub struct SeekDefKeyIterator<'a> { +pub struct SeekDefKeyIterator<'a, T> { seek_def: &'a SeekDef, seek_key: &'a SeekKey, pos: usize, + _t: PhantomData, } -impl<'a> Iterator for SeekDefKeyIterator<'a> { +impl<'a> Iterator for SeekDefKeyIterator<'a, SeekKeyComponent<&'a ast::Expr>> { type Item = SeekKeyComponent<&'a ast::Expr>; fn next(&mut self) -> Option { @@ -1155,6 +1157,25 @@ impl<'a> Iterator for SeekDefKeyIterator<'a> { } } +impl<'a> Iterator for SeekDefKeyIterator<'a, Affinity> { + type Item = Affinity; + + fn next(&mut self) -> Option { + let result = if self.pos < self.seek_def.prefix.len() { + Some(self.seek_def.prefix[self.pos].eq.as_ref().unwrap().2) + } else if self.pos == self.seek_def.prefix.len() { + match &self.seek_key.last_component { + SeekKeyComponent::Expr(..) => Some(self.seek_key.affinity), + SeekKeyComponent::None => None, + } + } else { + None + }; + self.pos += 1; + result + } +} + impl SeekDef { /// returns amount of values in the given seek key /// - so, for SELECT * FROM t WHERE x = 10 AND y = 20 AND y >= 30 there will be 3 values (10, 20, 30) @@ -1166,11 +1187,25 @@ impl SeekDef { } } /// iterate over value expressions in the given seek key - pub fn iter<'a>(&'a self, key: &'a SeekKey) -> SeekDefKeyIterator<'a> { + pub fn iter<'a>( + &'a self, + key: &'a SeekKey, + ) -> SeekDefKeyIterator<'a, SeekKeyComponent<&'a ast::Expr>> { SeekDefKeyIterator { seek_def: self, seek_key: key, pos: 0, + _t: PhantomData, + } + } + + /// iterate over affinity in the given seek key + pub fn iter_affinity<'a>(&'a self, key: &'a SeekKey) -> SeekDefKeyIterator<'a, Affinity> { + SeekDefKeyIterator { + seek_def: self, + seek_key: key, + pos: 0, + _t: PhantomData, } } } @@ -1196,6 +1231,9 @@ pub struct SeekKey { /// The comparison operator to use when seeking. pub op: SeekOp, + + /// Affinity of the comparison + pub affinity: Affinity, } /// Represents the type of table scan performed during query execution.