1use crate::error::{QueryError, Result};
4use crate::parser::ast::*;
5use sqlparser::ast as sql_ast;
6use sqlparser::dialect::GenericDialect;
7use sqlparser::parser::Parser as SqlParser;
8
9pub fn parse_sql(sql: &str) -> Result<Statement> {
11 let dialect = GenericDialect {};
12 let statements = SqlParser::parse_sql(&dialect, sql)?;
13
14 if statements.is_empty() {
15 return Err(QueryError::semantic("Empty SQL statement"));
16 }
17
18 if statements.len() > 1 {
19 return Err(QueryError::semantic("Multiple statements not supported"));
20 }
21
22 convert_statement(&statements[0])
23}
24
25fn convert_statement(stmt: &sql_ast::Statement) -> Result<Statement> {
26 match stmt {
27 sql_ast::Statement::Query(query) => {
28 let select = convert_query(query)?;
29 Ok(Statement::Select(select))
30 }
31 _ => Err(QueryError::unsupported("Only SELECT statements supported")),
32 }
33}
34
35fn convert_query(query: &sql_ast::Query) -> Result<SelectStatement> {
36 if let sql_ast::SetExpr::Select(select) = &*query.body {
37 let mut stmt = SelectStatement {
38 projection: Vec::new(),
39 from: None,
40 selection: None,
41 group_by: Vec::new(),
42 having: None,
43 order_by: Vec::new(),
44 limit: None,
45 offset: None,
46 };
47
48 for item in &select.projection {
50 stmt.projection.push(convert_select_item(item)?);
51 }
52
53 if !select.from.is_empty() {
55 stmt.from = Some(convert_table_reference(&select.from[0])?);
56 }
57
58 if let Some(selection) = &select.selection {
60 stmt.selection = Some(convert_expr(selection)?);
61 }
62
63 match &select.group_by {
65 sql_ast::GroupByExpr::Expressions(exprs, _) => {
66 for expr in exprs {
67 stmt.group_by.push(convert_expr(expr)?);
68 }
69 }
70 sql_ast::GroupByExpr::All(_) => {
71 return Err(QueryError::unsupported("GROUP BY ALL not supported"));
72 }
73 }
74
75 if let Some(having) = &select.having {
77 stmt.having = Some(convert_expr(having)?);
78 }
79
80 if let Some(order_by) = &query.order_by {
82 if let sql_ast::OrderByKind::Expressions(exprs) = &order_by.kind {
83 for order_expr in exprs {
84 stmt.order_by.push(convert_order_by_expr(order_expr)?);
85 }
86 }
87 }
88
89 if let Some(limit_clause) = &query.limit_clause {
91 match limit_clause {
92 sql_ast::LimitClause::LimitOffset { limit, offset, .. } => {
93 if let Some(limit_expr) = limit {
94 stmt.limit = Some(convert_limit(limit_expr)?);
95 }
96 if let Some(offset_val) = offset {
97 stmt.offset = Some(convert_offset(offset_val)?);
98 }
99 }
100 sql_ast::LimitClause::OffsetCommaLimit { limit, offset } => {
101 stmt.limit = Some(convert_limit(limit)?);
102 stmt.offset = Some(convert_limit(offset)?);
103 }
104 }
105 }
106
107 Ok(stmt)
108 } else {
109 Err(QueryError::unsupported(
110 "Only simple SELECT queries supported",
111 ))
112 }
113}
114
115fn convert_select_item(item: &sql_ast::SelectItem) -> Result<SelectItem> {
116 match item {
117 sql_ast::SelectItem::UnnamedExpr(expr) => Ok(SelectItem::Expr {
118 expr: convert_expr(expr)?,
119 alias: None,
120 }),
121 sql_ast::SelectItem::ExprWithAlias { expr, alias } => Ok(SelectItem::Expr {
122 expr: convert_expr(expr)?,
123 alias: Some(alias.value.clone()),
124 }),
125 sql_ast::SelectItem::Wildcard(_) => Ok(SelectItem::Wildcard),
126 sql_ast::SelectItem::QualifiedWildcard(obj_name, _) => {
127 Ok(SelectItem::QualifiedWildcard(obj_name.to_string()))
128 }
129 sql_ast::SelectItem::ExprWithAliases { .. } => Err(crate::error::QueryError::Unsupported(
130 "ExprWithAliases SELECT items are not supported".into(),
131 )),
132 }
133}
134
135fn convert_table_reference(table: &sql_ast::TableWithJoins) -> Result<TableReference> {
136 let mut result = convert_table_factor(&table.relation)?;
137
138 for join in &table.joins {
139 let right = convert_table_factor(&join.relation)?;
140 let join_type = convert_join_type(&join.join_operator)?;
141 let on = match &join.join_operator {
142 sql_ast::JoinOperator::Inner(constraint)
143 | sql_ast::JoinOperator::LeftOuter(constraint)
144 | sql_ast::JoinOperator::RightOuter(constraint)
145 | sql_ast::JoinOperator::FullOuter(constraint) => convert_join_constraint(constraint)?,
146 sql_ast::JoinOperator::CrossJoin(_) => None,
147 _ => return Err(QueryError::unsupported("Unsupported join type")),
148 };
149
150 result = TableReference::Join {
151 left: Box::new(result),
152 right: Box::new(right),
153 join_type,
154 on,
155 };
156 }
157
158 Ok(result)
159}
160
161fn convert_table_factor(factor: &sql_ast::TableFactor) -> Result<TableReference> {
162 match factor {
163 sql_ast::TableFactor::Table {
164 name, alias, args, ..
165 } => {
166 if args.is_some() {
167 return Err(QueryError::unsupported("Table functions not supported"));
168 }
169 Ok(TableReference::Table {
170 name: name.to_string(),
171 alias: alias.as_ref().map(|a| a.name.value.clone()),
172 })
173 }
174 sql_ast::TableFactor::Derived {
175 subquery, alias, ..
176 } => {
177 let query = convert_query(subquery)?;
178 let alias_name = alias
179 .as_ref()
180 .map(|a| a.name.value.clone())
181 .ok_or_else(|| QueryError::semantic("Subquery must have an alias"))?;
182 Ok(TableReference::Subquery {
183 query: Box::new(query),
184 alias: alias_name,
185 })
186 }
187 _ => Err(QueryError::unsupported("Unsupported table reference")),
188 }
189}
190
191fn convert_join_type(op: &sql_ast::JoinOperator) -> Result<JoinType> {
192 match op {
193 sql_ast::JoinOperator::Inner(_) => Ok(JoinType::Inner),
194 sql_ast::JoinOperator::LeftOuter(_) => Ok(JoinType::Left),
195 sql_ast::JoinOperator::RightOuter(_) => Ok(JoinType::Right),
196 sql_ast::JoinOperator::FullOuter(_) => Ok(JoinType::Full),
197 sql_ast::JoinOperator::CrossJoin(_) => Ok(JoinType::Cross),
198 _ => Err(QueryError::unsupported("Unsupported join type")),
199 }
200}
201
202fn convert_join_constraint(constraint: &sql_ast::JoinConstraint) -> Result<Option<Expr>> {
203 match constraint {
204 sql_ast::JoinConstraint::On(expr) => Ok(Some(convert_expr(expr)?)),
205 sql_ast::JoinConstraint::Using(_) => {
206 Err(QueryError::unsupported("USING clause not supported"))
207 }
208 sql_ast::JoinConstraint::Natural => {
209 Err(QueryError::unsupported("NATURAL join not supported"))
210 }
211 sql_ast::JoinConstraint::None => Ok(None),
212 }
213}
214
215fn convert_expr(expr: &sql_ast::Expr) -> Result<Expr> {
216 match expr {
217 sql_ast::Expr::Identifier(ident) => Ok(Expr::Column {
218 table: None,
219 name: ident.value.clone(),
220 }),
221 sql_ast::Expr::CompoundIdentifier(parts) => {
222 if parts.len() == 2 {
223 Ok(Expr::Column {
224 table: Some(parts[0].value.clone()),
225 name: parts[1].value.clone(),
226 })
227 } else {
228 Err(QueryError::semantic("Invalid column reference"))
229 }
230 }
231 sql_ast::Expr::Value(value_with_span) => {
232 Ok(Expr::Literal(convert_value(&value_with_span.value)?))
233 }
234 sql_ast::Expr::BinaryOp { left, op, right } => Ok(Expr::BinaryOp {
235 left: Box::new(convert_expr(left)?),
236 op: convert_binary_op(op)?,
237 right: Box::new(convert_expr(right)?),
238 }),
239 sql_ast::Expr::UnaryOp { op, expr } => Ok(Expr::UnaryOp {
240 op: convert_unary_op(op)?,
241 expr: Box::new(convert_expr(expr)?),
242 }),
243 sql_ast::Expr::Function(func) => {
244 let name = func.name.to_string();
245 let mut args = Vec::new();
246
247 match &func.args {
249 sql_ast::FunctionArguments::None => {
250 }
252 sql_ast::FunctionArguments::Subquery(_) => {
253 return Err(QueryError::unsupported(
254 "Subquery in function arguments not supported",
255 ));
256 }
257 sql_ast::FunctionArguments::List(arg_list) => {
258 for arg in &arg_list.args {
259 match arg {
260 sql_ast::FunctionArg::Unnamed(sql_ast::FunctionArgExpr::Expr(e)) => {
261 args.push(convert_expr(e)?);
262 }
263 sql_ast::FunctionArg::Unnamed(sql_ast::FunctionArgExpr::Wildcard) => {
264 args.push(Expr::Wildcard);
266 }
267 sql_ast::FunctionArg::Named {
268 name: _,
269 arg: sql_ast::FunctionArgExpr::Expr(e),
270 ..
271 } => {
272 args.push(convert_expr(e)?);
273 }
274 sql_ast::FunctionArg::Named {
275 name: _,
276 arg: sql_ast::FunctionArgExpr::Wildcard,
277 ..
278 } => {
279 args.push(Expr::Wildcard);
280 }
281 _ => {
282 return Err(QueryError::unsupported(
283 "Unsupported function argument",
284 ));
285 }
286 }
287 }
288 }
289 }
290 Ok(Expr::Function { name, args })
291 }
292 sql_ast::Expr::Case {
293 operand,
294 conditions,
295 else_result,
296 ..
297 } => {
298 let operand = operand
299 .as_ref()
300 .map(|e| convert_expr(e))
301 .transpose()?
302 .map(Box::new);
303 let mut when_then = Vec::new();
304 for case_when in conditions.iter() {
305 when_then.push((
306 convert_expr(&case_when.condition)?,
307 convert_expr(&case_when.result)?,
308 ));
309 }
310 let else_result = else_result
311 .as_ref()
312 .map(|e| convert_expr(e))
313 .transpose()?
314 .map(Box::new);
315 Ok(Expr::Case {
316 operand,
317 when_then,
318 else_result,
319 })
320 }
321 sql_ast::Expr::Cast {
322 expr, data_type, ..
323 } => Ok(Expr::Cast {
324 expr: Box::new(convert_expr(expr)?),
325 data_type: convert_data_type(data_type)?,
326 }),
327 sql_ast::Expr::IsNull(expr) => Ok(Expr::IsNull(Box::new(convert_expr(expr)?))),
328 sql_ast::Expr::IsNotNull(expr) => Ok(Expr::IsNotNull(Box::new(convert_expr(expr)?))),
329 sql_ast::Expr::InList {
330 expr,
331 list,
332 negated,
333 } => {
334 let expr = Box::new(convert_expr(expr)?);
335 let list = list.iter().map(convert_expr).collect::<Result<Vec<_>>>()?;
336 Ok(Expr::InList {
337 expr,
338 list,
339 negated: *negated,
340 })
341 }
342 sql_ast::Expr::Between {
343 expr,
344 low,
345 high,
346 negated,
347 } => Ok(Expr::Between {
348 expr: Box::new(convert_expr(expr)?),
349 low: Box::new(convert_expr(low)?),
350 high: Box::new(convert_expr(high)?),
351 negated: *negated,
352 }),
353 sql_ast::Expr::Like {
354 negated,
355 expr,
356 pattern,
357 ..
358 } => Ok(Expr::BinaryOp {
359 left: Box::new(convert_expr(expr)?),
360 op: if *negated {
361 BinaryOperator::NotLike
362 } else {
363 BinaryOperator::Like
364 },
365 right: Box::new(convert_expr(pattern)?),
366 }),
367 sql_ast::Expr::ILike {
368 negated,
369 expr,
370 pattern,
371 ..
372 } => {
373 Ok(Expr::BinaryOp {
375 left: Box::new(convert_expr(expr)?),
376 op: if *negated {
377 BinaryOperator::NotLike
378 } else {
379 BinaryOperator::Like
380 },
381 right: Box::new(convert_expr(pattern)?),
382 })
383 }
384 sql_ast::Expr::Subquery(query) => Ok(Expr::Subquery(Box::new(convert_query(query)?))),
385 sql_ast::Expr::Nested(expr) => convert_expr(expr),
386 sql_ast::Expr::Wildcard(_) => Ok(Expr::Wildcard),
387 _ => Err(QueryError::unsupported(format!(
388 "Unsupported expression: {:?}",
389 expr
390 ))),
391 }
392}
393
394fn convert_value(value: &sql_ast::Value) -> Result<Literal> {
395 match value {
396 sql_ast::Value::Null => Ok(Literal::Null),
397 sql_ast::Value::Boolean(b) => Ok(Literal::Boolean(*b)),
398 sql_ast::Value::Number(n, _) => {
399 if let Ok(i) = n.parse::<i64>() {
400 Ok(Literal::Integer(i))
401 } else if let Ok(f) = n.parse::<f64>() {
402 Ok(Literal::Float(f))
403 } else {
404 Err(QueryError::parse_error("Invalid number", 0, 0))
405 }
406 }
407 sql_ast::Value::SingleQuotedString(s) | sql_ast::Value::DoubleQuotedString(s) => {
408 Ok(Literal::String(s.clone()))
409 }
410 _ => Err(QueryError::unsupported("Unsupported literal value")),
411 }
412}
413
414fn convert_binary_op(op: &sql_ast::BinaryOperator) -> Result<BinaryOperator> {
415 match op {
416 sql_ast::BinaryOperator::Plus => Ok(BinaryOperator::Plus),
417 sql_ast::BinaryOperator::Minus => Ok(BinaryOperator::Minus),
418 sql_ast::BinaryOperator::Multiply => Ok(BinaryOperator::Multiply),
419 sql_ast::BinaryOperator::Divide => Ok(BinaryOperator::Divide),
420 sql_ast::BinaryOperator::Modulo => Ok(BinaryOperator::Modulo),
421 sql_ast::BinaryOperator::Eq => Ok(BinaryOperator::Eq),
422 sql_ast::BinaryOperator::NotEq => Ok(BinaryOperator::NotEq),
423 sql_ast::BinaryOperator::Lt => Ok(BinaryOperator::Lt),
424 sql_ast::BinaryOperator::LtEq => Ok(BinaryOperator::LtEq),
425 sql_ast::BinaryOperator::Gt => Ok(BinaryOperator::Gt),
426 sql_ast::BinaryOperator::GtEq => Ok(BinaryOperator::GtEq),
427 sql_ast::BinaryOperator::And => Ok(BinaryOperator::And),
428 sql_ast::BinaryOperator::Or => Ok(BinaryOperator::Or),
429 sql_ast::BinaryOperator::StringConcat => Ok(BinaryOperator::Concat),
430 _ => Err(QueryError::unsupported("Unsupported binary operator")),
432 }
433}
434
435fn convert_unary_op(op: &sql_ast::UnaryOperator) -> Result<UnaryOperator> {
436 match op {
437 sql_ast::UnaryOperator::Minus => Ok(UnaryOperator::Minus),
438 sql_ast::UnaryOperator::Not => Ok(UnaryOperator::Not),
439 _ => Err(QueryError::unsupported("Unsupported unary operator")),
440 }
441}
442
443fn convert_order_by_expr(order: &sql_ast::OrderByExpr) -> Result<OrderByExpr> {
444 Ok(OrderByExpr {
445 expr: convert_expr(&order.expr)?,
446 asc: order.options.asc.unwrap_or(true),
447 nulls_first: order.options.nulls_first.unwrap_or(false),
448 })
449}
450
451fn convert_limit(limit: &sql_ast::Expr) -> Result<usize> {
452 match limit {
453 sql_ast::Expr::Value(value_with_span) => match &value_with_span.value {
454 sql_ast::Value::Number(n, _) => n
455 .parse::<usize>()
456 .map_err(|_| QueryError::semantic("Invalid LIMIT value")),
457 _ => Err(QueryError::semantic("LIMIT must be a number")),
458 },
459 _ => Err(QueryError::semantic("LIMIT must be a number")),
460 }
461}
462
463fn convert_offset(offset: &sql_ast::Offset) -> Result<usize> {
464 match &offset.value {
465 sql_ast::Expr::Value(value_with_span) => match &value_with_span.value {
466 sql_ast::Value::Number(n, _) => n
467 .parse::<usize>()
468 .map_err(|_| QueryError::semantic("Invalid OFFSET value")),
469 _ => Err(QueryError::semantic("OFFSET must be a number")),
470 },
471 _ => Err(QueryError::semantic("OFFSET must be a number")),
472 }
473}
474
475fn convert_data_type(data_type: &sql_ast::DataType) -> Result<DataType> {
476 match data_type {
477 sql_ast::DataType::Boolean => Ok(DataType::Boolean),
478 sql_ast::DataType::TinyInt(_) => Ok(DataType::Int8),
479 sql_ast::DataType::SmallInt(_) => Ok(DataType::Int16),
480 sql_ast::DataType::Int(_) | sql_ast::DataType::Integer(_) => Ok(DataType::Int32),
481 sql_ast::DataType::BigInt(_) => Ok(DataType::Int64),
482 sql_ast::DataType::TinyIntUnsigned(_) => Ok(DataType::UInt8),
483 sql_ast::DataType::SmallIntUnsigned(_) => Ok(DataType::UInt16),
484 sql_ast::DataType::IntUnsigned(_)
485 | sql_ast::DataType::IntegerUnsigned(_)
486 | sql_ast::DataType::UnsignedInteger => Ok(DataType::UInt32),
487 sql_ast::DataType::BigIntUnsigned(_) => Ok(DataType::UInt64),
488 sql_ast::DataType::Float(_) | sql_ast::DataType::Real => Ok(DataType::Float32),
489 sql_ast::DataType::Double(_) | sql_ast::DataType::DoublePrecision => Ok(DataType::Float64),
490 sql_ast::DataType::Varchar(_)
491 | sql_ast::DataType::Char(_)
492 | sql_ast::DataType::Text
493 | sql_ast::DataType::String(_) => Ok(DataType::String),
494 sql_ast::DataType::Binary(_) | sql_ast::DataType::Varbinary(_) => Ok(DataType::Binary),
495 sql_ast::DataType::Timestamp(_, _) => Ok(DataType::Timestamp),
496 sql_ast::DataType::Date => Ok(DataType::Date),
497 sql_ast::DataType::Custom(name, _) if name.to_string().to_uppercase() == "GEOMETRY" => {
498 Ok(DataType::Geometry)
499 }
500 _ => Err(QueryError::unsupported(format!(
501 "Unsupported data type: {:?}",
502 data_type
503 ))),
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510
511 #[test]
512 fn test_parse_simple_select() {
513 let sql = "SELECT id, name FROM users";
514 let result = parse_sql(sql);
515 assert!(result.is_ok());
516 }
517
518 #[test]
519 fn test_parse_select_with_where() {
520 let sql = "SELECT * FROM users WHERE age > 18";
521 let result = parse_sql(sql);
522 assert!(result.is_ok());
523 }
524
525 #[test]
526 fn test_parse_select_with_join() {
527 let sql = "SELECT u.name, o.total FROM users u INNER JOIN orders o ON u.id = o.user_id";
528 let result = parse_sql(sql);
529 assert!(result.is_ok());
530 }
531
532 #[test]
533 fn test_parse_spatial_function() {
534 let sql = "SELECT ST_Area(geom) FROM buildings";
535 let result = parse_sql(sql);
536 assert!(result.is_ok());
537 }
538}