1use crate::sql::parser::ast::{SelectStatement, SqlExpression};
2use crate::sql::parser::walk;
3
4pub struct IntoClauseRemover;
23
24impl IntoClauseRemover {
25 pub fn remove_into_clause(statement: SelectStatement) -> SelectStatement {
36 Self::remove_from_statement(statement)
37 }
38
39 fn remove_from_statement(mut statement: SelectStatement) -> SelectStatement {
41 statement.into_table = None;
43
44 statement.map_from_subquery(Self::remove_from_statement);
46
47 statement.joins = statement
49 .joins
50 .into_iter()
51 .map(|mut join| {
52 if let crate::sql::parser::ast::TableSource::DerivedTable { query, alias } =
53 join.table
54 {
55 join.table = crate::sql::parser::ast::TableSource::DerivedTable {
56 query: Box::new(Self::remove_from_statement(*query)),
57 alias,
58 };
59 }
60 join
61 })
62 .collect();
63
64 statement.select_items = statement
66 .select_items
67 .into_iter()
68 .map(|item| Self::remove_from_select_item(item))
69 .collect();
70
71 if let Some(mut where_clause) = statement.where_clause.take() {
73 for condition in &mut where_clause.conditions {
74 condition.expr = Self::remove_from_expression(condition.expr.clone());
75 }
76 statement.where_clause = Some(where_clause);
77 }
78
79 statement.set_operations = statement
81 .set_operations
82 .into_iter()
83 .map(|(op, query)| (op, Box::new(Self::remove_from_statement(*query))))
84 .collect();
85
86 statement
87 }
88
89 fn remove_from_select_item(
91 item: crate::sql::parser::ast::SelectItem,
92 ) -> crate::sql::parser::ast::SelectItem {
93 match item {
94 crate::sql::parser::ast::SelectItem::Expression {
95 expr,
96 alias,
97 leading_comments,
98 trailing_comment,
99 } => crate::sql::parser::ast::SelectItem::Expression {
100 expr: Self::remove_from_expression(expr),
101 alias,
102 leading_comments,
103 trailing_comment,
104 },
105 other => other,
106 }
107 }
108
109 fn remove_from_expression(expr: SqlExpression) -> SqlExpression {
119 walk::map_children_crossing(
120 expr,
121 &mut (),
122 |_, e| Self::remove_from_expression(e),
123 |_, stmt| Box::new(Self::remove_from_statement(*stmt)),
124 )
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::sql::parser::ast::IntoTable;
132
133 #[test]
140 fn removes_into_inside_tuple_subquery() {
141 use crate::sql::recursive_parser::Parser;
142
143 let stmt = Parser::new("SELECT a FROM t WHERE (a, b) IN (SELECT x, y FROM u INTO #inner)")
144 .parse()
145 .expect("query should parse");
146
147 let inner_into = |s: &SelectStatement| match &s.where_clause {
149 Some(w) => match &w.conditions[0].expr {
150 SqlExpression::InSubqueryTuple { subquery, .. } => subquery.into_table.clone(),
151 other => panic!("expected a tuple IN subquery, got {other:?}"),
152 },
153 None => panic!("expected a where clause"),
154 };
155 assert!(
156 inner_into(&stmt).is_some(),
157 "test is meaningless unless the inner query starts with an INTO"
158 );
159
160 let result = IntoClauseRemover::remove_into_clause(stmt);
161 assert!(
162 inner_into(&result).is_none(),
163 "INTO must be removed from inside a tuple subquery"
164 );
165 }
166
167 #[test]
168 fn test_remove_simple_into() {
169 let stmt = SelectStatement {
170 distinct: false,
171 columns: vec!["col1".to_string()],
172 select_items: vec![],
173 from_source: None,
174 #[allow(deprecated)]
175 from_table: Some("table1".to_string()),
176 #[allow(deprecated)]
177 from_subquery: None,
178 #[allow(deprecated)]
179 from_function: None,
180 #[allow(deprecated)]
181 from_alias: None,
182 joins: vec![],
183 where_clause: None,
184 order_by: None,
185 group_by: None,
186 having: None,
187 qualify: None,
188 limit: None,
189 offset: None,
190 ctes: vec![],
191 into_table: Some(IntoTable {
192 name: "#temp".to_string(),
193 }),
194 set_operations: vec![],
195 leading_comments: vec![],
196 trailing_comment: None,
197 };
198
199 let result = IntoClauseRemover::remove_into_clause(stmt);
200 assert!(result.into_table.is_none());
201 assert_eq!(result.from_table, Some("table1".to_string()));
202 }
203
204 #[test]
205 fn test_remove_into_from_subquery() {
206 let subquery = SelectStatement {
207 distinct: false,
208 columns: vec![],
209 select_items: vec![],
210 from_source: None,
211 #[allow(deprecated)]
212 from_table: Some("inner_table".to_string()),
213 #[allow(deprecated)]
214 from_subquery: None,
215 #[allow(deprecated)]
216 from_function: None,
217 #[allow(deprecated)]
218 from_alias: None,
219 joins: vec![],
220 where_clause: None,
221 order_by: None,
222 group_by: None,
223 having: None,
224 qualify: None,
225 limit: None,
226 offset: None,
227 ctes: vec![],
228 into_table: Some(IntoTable {
229 name: "#inner_temp".to_string(),
230 }),
231 set_operations: vec![],
232 leading_comments: vec![],
233 trailing_comment: None,
234 };
235
236 let stmt = SelectStatement {
237 distinct: false,
238 columns: vec![],
239 select_items: vec![],
240 from_source: None,
241 #[allow(deprecated)]
242 from_table: None,
243 #[allow(deprecated)]
244 from_subquery: Some(Box::new(subquery)),
245 #[allow(deprecated)]
246 from_function: None,
247 #[allow(deprecated)]
248 from_alias: Some("subq".to_string()),
249 joins: vec![],
250 where_clause: None,
251 order_by: None,
252 group_by: None,
253 having: None,
254 qualify: None,
255 limit: None,
256 offset: None,
257 ctes: vec![],
258 into_table: Some(IntoTable {
259 name: "#outer_temp".to_string(),
260 }),
261 set_operations: vec![],
262 leading_comments: vec![],
263 trailing_comment: None,
264 };
265
266 let result = IntoClauseRemover::remove_into_clause(stmt);
267
268 assert!(result.into_table.is_none());
270 assert!(result.from_subquery.as_ref().unwrap().into_table.is_none());
271 }
272}