use crate::sql::parser::ast::{SelectStatement, SqlExpression};
use crate::sql::parser::walk;
pub struct IntoClauseRemover;
impl IntoClauseRemover {
pub fn remove_into_clause(statement: SelectStatement) -> SelectStatement {
Self::remove_from_statement(statement)
}
fn remove_from_statement(mut statement: SelectStatement) -> SelectStatement {
statement.into_table = None;
statement.map_from_subquery(Self::remove_from_statement);
statement.joins = statement
.joins
.into_iter()
.map(|mut join| {
if let crate::sql::parser::ast::TableSource::DerivedTable { query, alias } =
join.table
{
join.table = crate::sql::parser::ast::TableSource::DerivedTable {
query: Box::new(Self::remove_from_statement(*query)),
alias,
};
}
join
})
.collect();
statement.select_items = statement
.select_items
.into_iter()
.map(|item| Self::remove_from_select_item(item))
.collect();
if let Some(mut where_clause) = statement.where_clause.take() {
for condition in &mut where_clause.conditions {
condition.expr = Self::remove_from_expression(condition.expr.clone());
}
statement.where_clause = Some(where_clause);
}
statement.set_operations = statement
.set_operations
.into_iter()
.map(|(op, query)| (op, Box::new(Self::remove_from_statement(*query))))
.collect();
statement
}
fn remove_from_select_item(
item: crate::sql::parser::ast::SelectItem,
) -> crate::sql::parser::ast::SelectItem {
match item {
crate::sql::parser::ast::SelectItem::Expression {
expr,
alias,
leading_comments,
trailing_comment,
} => crate::sql::parser::ast::SelectItem::Expression {
expr: Self::remove_from_expression(expr),
alias,
leading_comments,
trailing_comment,
},
other => other,
}
}
fn remove_from_expression(expr: SqlExpression) -> SqlExpression {
walk::map_children_crossing(
expr,
&mut (),
|_, e| Self::remove_from_expression(e),
|_, stmt| Box::new(Self::remove_from_statement(*stmt)),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::parser::ast::IntoTable;
#[test]
fn removes_into_inside_tuple_subquery() {
use crate::sql::recursive_parser::Parser;
let stmt = Parser::new("SELECT a FROM t WHERE (a, b) IN (SELECT x, y FROM u INTO #inner)")
.parse()
.expect("query should parse");
let inner_into = |s: &SelectStatement| match &s.where_clause {
Some(w) => match &w.conditions[0].expr {
SqlExpression::InSubqueryTuple { subquery, .. } => subquery.into_table.clone(),
other => panic!("expected a tuple IN subquery, got {other:?}"),
},
None => panic!("expected a where clause"),
};
assert!(
inner_into(&stmt).is_some(),
"test is meaningless unless the inner query starts with an INTO"
);
let result = IntoClauseRemover::remove_into_clause(stmt);
assert!(
inner_into(&result).is_none(),
"INTO must be removed from inside a tuple subquery"
);
}
#[test]
fn test_remove_simple_into() {
let stmt = SelectStatement {
distinct: false,
columns: vec!["col1".to_string()],
select_items: vec![],
from_source: None,
#[allow(deprecated)]
from_table: Some("table1".to_string()),
#[allow(deprecated)]
from_subquery: None,
#[allow(deprecated)]
from_function: None,
#[allow(deprecated)]
from_alias: None,
joins: vec![],
where_clause: None,
order_by: None,
group_by: None,
having: None,
qualify: None,
limit: None,
offset: None,
ctes: vec![],
into_table: Some(IntoTable {
name: "#temp".to_string(),
}),
set_operations: vec![],
leading_comments: vec![],
trailing_comment: None,
};
let result = IntoClauseRemover::remove_into_clause(stmt);
assert!(result.into_table.is_none());
assert_eq!(result.from_table, Some("table1".to_string()));
}
#[test]
fn test_remove_into_from_subquery() {
let subquery = SelectStatement {
distinct: false,
columns: vec![],
select_items: vec![],
from_source: None,
#[allow(deprecated)]
from_table: Some("inner_table".to_string()),
#[allow(deprecated)]
from_subquery: None,
#[allow(deprecated)]
from_function: None,
#[allow(deprecated)]
from_alias: None,
joins: vec![],
where_clause: None,
order_by: None,
group_by: None,
having: None,
qualify: None,
limit: None,
offset: None,
ctes: vec![],
into_table: Some(IntoTable {
name: "#inner_temp".to_string(),
}),
set_operations: vec![],
leading_comments: vec![],
trailing_comment: None,
};
let stmt = SelectStatement {
distinct: false,
columns: vec![],
select_items: vec![],
from_source: None,
#[allow(deprecated)]
from_table: None,
#[allow(deprecated)]
from_subquery: Some(Box::new(subquery)),
#[allow(deprecated)]
from_function: None,
#[allow(deprecated)]
from_alias: Some("subq".to_string()),
joins: vec![],
where_clause: None,
order_by: None,
group_by: None,
having: None,
qualify: None,
limit: None,
offset: None,
ctes: vec![],
into_table: Some(IntoTable {
name: "#outer_temp".to_string(),
}),
set_operations: vec![],
leading_comments: vec![],
trailing_comment: None,
};
let result = IntoClauseRemover::remove_into_clause(stmt);
assert!(result.into_table.is_none());
assert!(result.from_subquery.as_ref().unwrap().into_table.is_none());
}
}