mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-22 09:24:26 +01:00
add affinity of the expr in the seek key, and emit affinity instruction before seeking
This commit is contained in:
@@ -2198,7 +2198,6 @@ impl BTreeCursor {
|
||||
SeekOp::LE { eq_only: false } => cmp.is_le(),
|
||||
SeekOp::LT => cmp.is_lt(),
|
||||
};
|
||||
|
||||
(cmp, found)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user