qail_core/transformer/clauses/
mod.rs

1//! SQL clause extractors
2
3use sqlparser::ast::{Expr, OrderByExpr, Select, SelectItem, TableFactor};
4
5use crate::transformer::traits::*;
6
7/// Extract table name from SELECT
8pub fn extract_table(select: &Select) -> Result<String, ExtractError> {
9    select
10        .from
11        .first()
12        .map(|f| extract_table_from_factor(&f.relation))
13        .transpose()?
14        .ok_or_else(|| ExtractError {
15            message: "No FROM clause found".to_string(),
16        })
17}
18
19/// Extract table name from TableFactor
20pub fn extract_table_from_factor(factor: &TableFactor) -> Result<String, ExtractError> {
21    match factor {
22        TableFactor::Table { name, .. } => Ok(name.to_string()),
23        _ => Err(ExtractError {
24            message: "Complex table expression not supported".to_string(),
25        }),
26    }
27}
28
29/// Extract column names from SELECT
30pub fn extract_columns(select: &Select) -> Vec<String> {
31    select
32        .projection
33        .iter()
34        .map(|item| match item {
35            SelectItem::UnnamedExpr(expr) => expr_to_string(expr),
36            SelectItem::ExprWithAlias { alias, .. } => alias.value.clone(),
37            SelectItem::Wildcard(_) => "*".to_string(),
38            SelectItem::QualifiedWildcard(name, _) => format!("{}.*", name),
39        })
40        .collect()
41}
42
43/// Extract filter from WHERE clause
44pub fn extract_filter(expr: &Expr) -> Result<FilterData, ExtractError> {
45    match expr {
46        Expr::BinaryOp { left, op, right } => {
47            let column = expr_to_string(left);
48            let operator = match op {
49                sqlparser::ast::BinaryOperator::Eq => "Operator::Eq",
50                sqlparser::ast::BinaryOperator::NotEq => "Operator::NotEq",
51                sqlparser::ast::BinaryOperator::Lt => "Operator::Lt",
52                sqlparser::ast::BinaryOperator::LtEq => "Operator::LtEq",
53                sqlparser::ast::BinaryOperator::Gt => "Operator::Gt",
54                sqlparser::ast::BinaryOperator::GtEq => "Operator::GtEq",
55                _ => "Operator::Eq",
56            };
57            let value = expr_to_value(right);
58
59            Ok(FilterData {
60                column,
61                operator: operator.to_string(),
62                value,
63            })
64        }
65        _ => Err(ExtractError {
66            message: "Complex filter expression not supported".to_string(),
67        }),
68    }
69}
70
71/// Extract ORDER BY clause
72pub fn extract_order_by(exprs: &[OrderByExpr]) -> Vec<OrderByData> {
73    exprs
74        .iter()
75        .map(|o| OrderByData {
76            column: expr_to_string(&o.expr),
77            descending: o.options.asc == Some(false),
78        })
79        .collect()
80}
81
82/// Extract LIMIT value
83pub fn extract_limit(expr: &Expr) -> Option<u64> {
84    match expr {
85        Expr::Value(value_with_span) => {
86            let s = value_with_span.to_string();
87            s.parse().ok()
88        }
89        _ => None,
90    }
91}
92
93/// Convert expression to string
94pub fn expr_to_string(expr: &Expr) -> String {
95    match expr {
96        Expr::Identifier(ident) => ident.value.clone(),
97        Expr::CompoundIdentifier(parts) => parts.iter().map(|i| i.value.clone()).collect::<Vec<_>>().join("."),
98        _ => expr.to_string(),
99    }
100}
101
102/// Convert expression to ValueData
103pub fn expr_to_value(expr: &Expr) -> ValueData {
104    match expr {
105        Expr::Value(v) => {
106            let s = v.to_string();
107            if let Some(stripped) = s.strip_prefix('$')
108                && let Ok(n) = stripped.parse::<usize>()
109            {
110                return ValueData::Param(n);
111            }
112            if s == "NULL" {
113                ValueData::Null
114            } else {
115                ValueData::Literal(s)
116            }
117        }
118        Expr::Identifier(ident) => ValueData::Column(ident.value.clone()),
119        _ => ValueData::Literal(expr.to_string()),
120    }
121}