From fce3ce846ff207b5ddf104416b8610bdea868ee3 Mon Sep 17 00:00:00 2001 From: Ayman Elkfrawy Date: Wed, 9 Apr 2025 14:14:24 -0700 Subject: [PATCH] add passthrough query as a tablefactor --- src/ast/query.rs | 13 +++++++++++++ src/ast/spans.rs | 2 ++ src/parser/mod.rs | 3 ++- tests/sqlparser_common.rs | 23 +++++++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index abc115a0d..b2156f58f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -1135,6 +1135,12 @@ pub enum TableFactor { subquery: Box, alias: Option, }, + /// A pass-through query string that is not parsed. + /// This is useful while building/rewriting queries with a known valid SQL string and to avoid parsing it. + PassThroughQuery { + query: String, + alias: Option, + }, /// `TABLE()[ AS ]` TableFunction { expr: Expr, @@ -1767,6 +1773,13 @@ impl fmt::Display for TableFactor { } Ok(()) } + TableFactor::PassThroughQuery { query, alias } => { + write!(f, "({query})")?; + if let Some(alias) = alias { + write!(f, " AS {alias}")?; + } + Ok(()) + } TableFactor::Function { lateral, name, diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 718c66472..31084ca27 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1876,6 +1876,8 @@ impl Spanned for TableFactor { } => subquery .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), + // This is usually created at runtime, so we don't have a span for it + TableFactor::PassThroughQuery { query: _, alias: _ } => Span::empty(), TableFactor::TableFunction { expr, alias } => expr .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 705e3b379..4c0105079 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -11971,7 +11971,8 @@ impl<'a> Parser<'a> { | TableFactor::Pivot { alias, .. } | TableFactor::Unpivot { alias, .. } | TableFactor::MatchRecognize { alias, .. } - | TableFactor::NestedJoin { alias, .. } => { + | TableFactor::NestedJoin { alias, .. } + | TableFactor::PassThroughQuery { alias, .. } => { // but not `FROM (mytable AS alias1) AS alias2`. if let Some(inner_alias) = alias { return Err(ParserError::ParserError(format!( diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 2e57f025f..02d609478 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -14124,6 +14124,29 @@ fn parse_select_without_projection() { dialects.verified_stmt("SELECT FROM users"); } +#[test] +fn ast_with_pass_through_query() { + let sql = "SELECT * FROM t1 AS t2"; + let mut ast = all_dialects().verified_stmt(sql); + let Statement::Query(ref mut query) = ast else { + panic!("Expected Query"); + }; + let SetExpr::Select(ref mut select) = *query.body else { + panic!("Expected SetExpr::Select"); + }; + let from = select.from.get_mut(0).unwrap(); + from.relation = TableFactor::PassThroughQuery { + query: "SELECT * FROM tx".to_string(), + alias: Some(TableAlias { + name: Ident::new("ty"), + columns: vec![], + }), + }; + + // After modifying the AST, the SQL representation should be different + assert_eq!(ast.to_string(), "SELECT * FROM (SELECT * FROM tx) AS ty"); +} + #[test] fn parse_update_from_before_select() { verified_stmt("UPDATE t1 FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 SET name = t2.name WHERE t1.id = t2.id");