diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 41de6e2f3..15be3da90 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -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, .. } => { diff --git a/core/translate/optimizer/mod.rs b/core/translate/optimizer/mod.rs index 798cd50f3..066b23567 100644 --- a/core/translate/optimizer/mod.rs +++ b/core/translate/optimizer/mod.rs @@ -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, .. } => { diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 5f4449afb..fc40cde48 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -472,6 +472,40 @@ pub enum Expr { Unary(UnaryOperator, Box), /// 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>, + /// 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 IN (SELECT ...) + In { cursor_id: usize }, } impl Expr { diff --git a/parser/src/ast/fmt.rs b/parser/src/ast/fmt.rs index ee3132880..2cdcf40c3 100644 --- a/parser/src/ast/fmt.rs +++ b/parser/src/ast/fmt.rs @@ -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,