treat parameters as "constant" within a query

This commit is contained in:
Nikita Sivukhin
2025-11-12 02:30:15 +04:00
parent c4d89662a8
commit b3380bc398
2 changed files with 47 additions and 3 deletions

View File

@@ -1071,7 +1071,7 @@ impl Optimizable for ast::Expr {
Expr::Register(..) => false, // Register values can be null
}
}
/// Returns true if the expression is a constant i.e. does not depend on variables or columns etc.
/// Returns true if the expression is a constant i.e. does not depend on columns and can be evaluated only once during the execution
fn is_constant(&self, resolver: &Resolver<'_>) -> bool {
match self {
Expr::SubqueryResult { .. } => false,
@@ -1142,8 +1142,8 @@ impl Optimizable for ast::Expr {
Expr::Raise(_, expr) => expr.as_ref().is_none_or(|expr| expr.is_constant(resolver)),
Expr::Subquery(_) => false,
Expr::Unary(_, expr) => expr.is_constant(resolver),
Expr::Variable(_) => false,
Expr::Register(_) => false, // Register values are not constants
Expr::Variable(_) => true,
Expr::Register(_) => true,
}
}
/// Returns true if the expression is a constant expression that, when evaluated as a condition, is always true or false

View File

@@ -1018,3 +1018,47 @@ fn test_many_columns() {
]]
);
}
#[test]
fn test_eval_param_only_once() {
let tmp_db = TempDatabase::new("test_eval_param_only_once");
let conn = tmp_db.connect_limbo();
conn.execute("CREATE TABLE t(x)").unwrap();
conn.execute("INSERT INTO t SELECT value FROM generate_series(1, 10000)")
.unwrap();
let mut stmt = conn
.query("SELECT COUNT(*) FROM t WHERE LENGTH(zeroblob(?)) = ?")
.unwrap()
.unwrap();
stmt.bind_at(
1.try_into().unwrap(),
turso_core::Value::Integer(100_000_000),
);
stmt.bind_at(
2.try_into().unwrap(),
turso_core::Value::Integer(100_000_000),
);
let start_time = std::time::Instant::now();
loop {
match stmt.step().unwrap() {
StepResult::IO => {
stmt.run_once().unwrap();
}
StepResult::Done => break,
StepResult::Row => {
let values = stmt
.row()
.unwrap()
.get_values()
.cloned()
.collect::<Vec<_>>();
assert_eq!(values, vec![turso_core::Value::Integer(10000)]);
}
_ => unreachable!(),
}
}
let end_time = std::time::Instant::now();
let elapsed = end_time.duration_since(start_time);
// the test will allocate 10^8 * 10^4 bytes in case if parameter will be evaluated for every row
assert!(elapsed < std::time::Duration::from_millis(100));
}