sql_cli/query_plan/
qualify_to_where_transformer.rs1use crate::query_plan::pipeline::ASTTransformer;
54use crate::sql::parser::ast::{Condition, LogicalOp, SelectStatement, WhereClause};
55use anyhow::Result;
56use tracing::debug;
57
58pub struct QualifyToWhereTransformer;
60
61impl QualifyToWhereTransformer {
62 pub fn new() -> Self {
63 Self
64 }
65}
66
67impl Default for QualifyToWhereTransformer {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73impl ASTTransformer for QualifyToWhereTransformer {
74 fn name(&self) -> &str {
75 "QualifyToWhereTransformer"
76 }
77
78 fn description(&self) -> &str {
79 "Converts QUALIFY clauses to WHERE clauses after window function lifting"
80 }
81
82 fn transform(&mut self, mut stmt: SelectStatement) -> Result<SelectStatement> {
83 if stmt.qualify.is_none() {
85 return Ok(stmt);
86 }
87
88 let qualify_expr = stmt.qualify.take().unwrap();
89
90 debug!("Converting QUALIFY clause to WHERE");
91
92 if let Some(mut where_clause) = stmt.where_clause.take() {
94 debug!("Combining QUALIFY with existing WHERE using AND");
95
96 if let Some(last_condition) = where_clause.conditions.last_mut() {
98 last_condition.connector = Some(LogicalOp::And);
99 }
100
101 where_clause.conditions.push(Condition {
103 expr: qualify_expr,
104 connector: None,
105 });
106
107 stmt.where_clause = Some(where_clause);
108 } else {
109 debug!("Creating new WHERE clause from QUALIFY");
111
112 stmt.where_clause = Some(WhereClause {
113 conditions: vec![Condition {
114 expr: qualify_expr,
115 connector: None,
116 }],
117 });
118 }
119
120 Ok(stmt)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::sql::parser::ast::{ColumnRef, QuoteStyle, SqlExpression};
128
129 #[test]
130 fn test_qualify_to_where_simple() {
131 let mut stmt = SelectStatement::default();
132
133 stmt.qualify = Some(SqlExpression::BinaryOp {
135 left: Box::new(SqlExpression::Column(ColumnRef::unquoted("rn".to_string()))),
136 op: "<=".to_string(),
137 right: Box::new(SqlExpression::NumberLiteral("3".to_string())),
138 });
139
140 let mut transformer = QualifyToWhereTransformer::new();
141 let result = transformer.transform(stmt).unwrap();
142
143 assert!(result.qualify.is_none());
145
146 assert!(result.where_clause.is_some());
148
149 let where_clause = result.where_clause.unwrap();
150 assert_eq!(where_clause.conditions.len(), 1);
151 }
152
153 #[test]
154 fn test_qualify_combined_with_existing_where() {
155 let mut stmt = SelectStatement::default();
156
157 stmt.where_clause = Some(WhereClause {
159 conditions: vec![Condition {
160 expr: SqlExpression::BinaryOp {
161 left: Box::new(SqlExpression::Column(ColumnRef::unquoted(
162 "region".to_string(),
163 ))),
164 op: "=".to_string(),
165 right: Box::new(SqlExpression::StringLiteral("North".to_string())),
166 },
167 connector: None,
168 }],
169 });
170
171 stmt.qualify = Some(SqlExpression::BinaryOp {
173 left: Box::new(SqlExpression::Column(ColumnRef::unquoted("rn".to_string()))),
174 op: "<=".to_string(),
175 right: Box::new(SqlExpression::NumberLiteral("3".to_string())),
176 });
177
178 let mut transformer = QualifyToWhereTransformer::new();
179 let result = transformer.transform(stmt).unwrap();
180
181 assert!(result.qualify.is_none());
183
184 let where_clause = result.where_clause.unwrap();
186 assert_eq!(where_clause.conditions.len(), 2);
187 assert!(matches!(
188 where_clause.conditions[0].connector,
189 Some(LogicalOp::And)
190 ));
191 }
192}