add affinity of the expr in the seek key, and emit affinity instruction before seeking

This commit is contained in:
pedrocarlo
2025-11-06 12:49:06 -03:00
committed by Jussi Saurio
parent e929c252b4
commit 27e234f949
5 changed files with 201 additions and 104 deletions

View File

@@ -2198,7 +2198,6 @@ impl BTreeCursor {
SeekOp::LE { eq_only: false } => cmp.is_le(),
SeekOp::LT => cmp.is_lt(),
};
(cmp, found)
}

View File

@@ -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,

View File

@@ -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)),
)
}
}

View File

@@ -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<JoinOrderMember> = 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<String>,
vtab_constraints: &[ConstraintInfo],
constraint_usages: &[ConstraintUsage],
referenced_tables: Option<&TableReferences>,
) -> Result<Operation> {
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<SeekDef> {
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,)
}
};

View File

@@ -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<T>,
}
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<Self::Item> {
@@ -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<Self::Item> {
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.