AST: Add Expr::SubqueryResult variant and enum SubqueryType

This commit is contained in:
Jussi Saurio
2025-10-27 14:12:23 +02:00
parent e7aa7ee2ff
commit 5eb74ce8e6
4 changed files with 52 additions and 0 deletions

View File

@@ -288,6 +288,7 @@ pub fn translate_condition_expr(
resolver: &Resolver,
) -> Result<()> {
match expr {
ast::Expr::SubqueryResult { .. } => unimplemented!(), // Will be implemented in a future commit
ast::Expr::Register(_) => {
crate::bail_parse_error!("Register in WHERE clause is currently unused. Consider removing Resolver::expr_to_reg_cache and using Expr::Register instead");
}
@@ -593,6 +594,7 @@ pub fn translate_expr(
}
match expr {
ast::Expr::SubqueryResult { .. } => unimplemented!(), // Will be implemented in a future commit
ast::Expr::Between { .. } => {
unreachable!("expression should have been rewritten in optmizer")
}
@@ -3184,6 +3186,11 @@ where
match func(expr)? {
WalkControl::Continue => {
match expr {
ast::Expr::SubqueryResult { lhs, .. } => {
if let Some(lhs) = lhs {
walk_expr(lhs, func)?;
}
}
ast::Expr::Between {
lhs, start, end, ..
} => {
@@ -3712,6 +3719,11 @@ where
match func(expr)? {
WalkControl::Continue => {
match expr {
ast::Expr::SubqueryResult { lhs, .. } => {
if let Some(lhs) = lhs {
walk_expr_mut(lhs, func)?;
}
}
ast::Expr::Between {
lhs, start, end, ..
} => {

View File

@@ -766,6 +766,7 @@ impl Optimizable for ast::Expr {
/// by writing more complex code.
fn is_nonnull(&self, tables: &TableReferences) -> bool {
match self {
Expr::SubqueryResult { .. } => false,
Expr::Between {
lhs, start, end, ..
} => lhs.is_nonnull(tables) && start.is_nonnull(tables) && end.is_nonnull(tables),
@@ -843,6 +844,7 @@ impl Optimizable for ast::Expr {
/// Returns true if the expression is a constant i.e. does not depend on variables or columns etc.
fn is_constant(&self, resolver: &Resolver<'_>) -> bool {
match self {
Expr::SubqueryResult { .. } => false,
Expr::Between {
lhs, start, end, ..
} => {

View File

@@ -472,6 +472,40 @@ pub enum Expr {
Unary(UnaryOperator, Box<Expr>),
/// Parameters
Variable(String),
/// Subqueries from e.g. the WHERE clause are planned separately
/// and their results will be placed in registers or in an ephemeral index
/// pointed to by this type.
SubqueryResult {
/// Internal "opaque" identifier for the subquery. When the translator encounters
/// a [Expr::SubqueryResult], it needs to know which subquery in the corresponding
/// query plan it references.
subquery_id: TableInternalId,
/// Left-hand side expression for IN subqueries.
/// This property plus 'not_in' are only relevant for IN subqueries,
/// and the reason they are not included in the [SubqueryType] enum is so that
/// we don't have to clone this Box.
lhs: Option<Box<Expr>>,
/// Whether the IN subquery is a NOT IN subquery.
not_in: bool,
/// The type of subquery.
query_type: SubqueryType,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SubqueryType {
/// EXISTS subquery; result is stored in a single register.
Exists { result_reg: usize },
/// Row value subquery; result is stored in a range of registers.
/// Example: x = (SELECT ...) or (x, y) = (SELECT ...)
RowValue {
result_reg_start: usize,
num_regs: usize,
},
/// IN subquery; result is stored in an ephemeral index.
/// Example: x <NOT> IN (SELECT ...)
In { cursor_id: usize },
}
impl Expr {

View File

@@ -686,6 +686,10 @@ impl ToTokens for Expr {
context: &C,
) -> Result<(), S::Error> {
match self {
Self::SubqueryResult { .. } => {
// FIXME: what to put here? This is a highly "artificial" AST node that has no meaning when stringified.
Ok(())
}
Self::Between {
lhs,
not,