Skip to main content

aegis_query/
parser.rs

1//! Aegis Parser - SQL Parser
2//!
3//! Wraps sqlparser-rs to parse SQL statements and convert them to the
4//! internal Aegis AST representation. Handles dialect differences and
5//! provides friendly error messages.
6//!
7//! Key Features:
8//! - Full ANSI SQL support via sqlparser-rs
9//! - Conversion to typed Aegis AST
10//! - Detailed parse error reporting
11//! - Support for multiple SQL dialects
12//!
13//! @version 0.1.0
14//! @author AutomataNexus Development Team
15
16use crate::ast::*;
17use aegis_common::{AegisError, DataType, Result};
18use sqlparser::ast as sp;
19use sqlparser::dialect::GenericDialect;
20use sqlparser::parser::Parser as SqlParser;
21
22// =============================================================================
23// Parser
24// =============================================================================
25
26/// SQL parser wrapping sqlparser-rs.
27pub struct Parser {
28    dialect: GenericDialect,
29}
30
31impl Parser {
32    pub fn new() -> Self {
33        Self {
34            dialect: GenericDialect {},
35        }
36    }
37
38    /// Parse a SQL string into statements.
39    pub fn parse(&self, sql: &str) -> Result<Vec<Statement>> {
40        let ast = SqlParser::parse_sql(&self.dialect, sql)
41            .map_err(|e| AegisError::Parse(e.to_string()))?;
42
43        ast.into_iter().map(|stmt| self.convert_statement(stmt)).collect()
44    }
45
46    /// Parse a single statement.
47    pub fn parse_single(&self, sql: &str) -> Result<Statement> {
48        let statements = self.parse(sql)?;
49        if statements.len() != 1 {
50            return Err(AegisError::Parse(format!(
51                "Expected 1 statement, got {}",
52                statements.len()
53            )));
54        }
55        // Safe to use expect here: we verified statements.len() == 1 above
56        Ok(statements.into_iter().next().expect("statements verified to have exactly 1 element"))
57    }
58
59    fn convert_statement(&self, stmt: sp::Statement) -> Result<Statement> {
60        match stmt {
61            sp::Statement::Query(query) => {
62                let select = self.convert_query(*query)?;
63                Ok(Statement::Select(select))
64            }
65            sp::Statement::Insert(insert) => {
66                let insert_stmt = self.convert_insert(insert)?;
67                Ok(Statement::Insert(insert_stmt))
68            }
69            sp::Statement::Update { table, assignments, from: _, selection, returning: _, .. } => {
70                let update_stmt = self.convert_update(table, assignments, selection)?;
71                Ok(Statement::Update(update_stmt))
72            }
73            sp::Statement::Delete(delete) => {
74                let delete_stmt = self.convert_delete(delete)?;
75                Ok(Statement::Delete(delete_stmt))
76            }
77            sp::Statement::CreateTable(create) => {
78                let create_stmt = self.convert_create_table(create)?;
79                Ok(Statement::CreateTable(create_stmt))
80            }
81            sp::Statement::Drop { object_type, if_exists, names, .. } => {
82                self.convert_drop(object_type, if_exists, names)
83            }
84            sp::Statement::CreateIndex(create) => {
85                let create_stmt = self.convert_create_index(create)?;
86                Ok(Statement::CreateIndex(create_stmt))
87            }
88            sp::Statement::AlterTable { name, operations, .. } => {
89                let alter_stmt = self.convert_alter_table(name, operations)?;
90                Ok(Statement::AlterTable(alter_stmt))
91            }
92            sp::Statement::StartTransaction { .. } => Ok(Statement::Begin),
93            sp::Statement::Commit { .. } => Ok(Statement::Commit),
94            sp::Statement::Rollback { .. } => Ok(Statement::Rollback),
95            _ => Err(AegisError::Parse(format!(
96                "Unsupported statement type: {:?}",
97                stmt
98            ))),
99        }
100    }
101
102    fn convert_query(&self, query: sp::Query) -> Result<SelectStatement> {
103        let body = match *query.body {
104            sp::SetExpr::Select(select) => select,
105            _ => return Err(AegisError::Parse("Unsupported query type".to_string())),
106        };
107
108        let columns = body
109            .projection
110            .into_iter()
111            .map(|item| self.convert_select_item(item))
112            .collect::<Result<Vec<_>>>()?;
113
114        let from = if !body.from.is_empty() {
115            Some(self.convert_from(&body.from)?)
116        } else {
117            None
118        };
119
120        let where_clause = body
121            .selection
122            .map(|expr| self.convert_expr(expr))
123            .transpose()?;
124
125        let group_by = match body.group_by {
126            sp::GroupByExpr::Expressions(exprs, _) => exprs
127                .into_iter()
128                .map(|e| self.convert_expr(e))
129                .collect::<Result<Vec<_>>>()?,
130            sp::GroupByExpr::All(_) => Vec::new(),
131        };
132
133        let having = body.having.map(|e| self.convert_expr(e)).transpose()?;
134
135        let order_by = query
136            .order_by
137            .map(|ob| {
138                ob.exprs
139                    .into_iter()
140                    .map(|item| self.convert_order_by_item(item))
141                    .collect::<Result<Vec<_>>>()
142            })
143            .transpose()?
144            .unwrap_or_default();
145
146        let limit = query
147            .limit
148            .map(|e| self.extract_limit(e))
149            .transpose()?;
150
151        let offset = query
152            .offset
153            .map(|o| self.extract_limit(o.value))
154            .transpose()?;
155
156        Ok(SelectStatement {
157            distinct: body.distinct.is_some(),
158            columns,
159            from,
160            where_clause,
161            group_by,
162            having,
163            order_by,
164            limit,
165            offset,
166        })
167    }
168
169    fn convert_select_item(&self, item: sp::SelectItem) -> Result<SelectColumn> {
170        match item {
171            sp::SelectItem::UnnamedExpr(expr) => Ok(SelectColumn::Expression {
172                expr: self.convert_expr(expr)?,
173                alias: None,
174            }),
175            sp::SelectItem::ExprWithAlias { expr, alias } => Ok(SelectColumn::Expression {
176                expr: self.convert_expr(expr)?,
177                alias: Some(alias.value),
178            }),
179            sp::SelectItem::Wildcard(_) => Ok(SelectColumn::AllColumns),
180            sp::SelectItem::QualifiedWildcard(name, _) => {
181                Ok(SelectColumn::TableAllColumns(name.to_string()))
182            }
183        }
184    }
185
186    fn convert_from(&self, from: &[sp::TableWithJoins]) -> Result<FromClause> {
187        let first = from.first().ok_or_else(|| AegisError::Parse("Empty FROM".to_string()))?;
188
189        let source = self.convert_table_factor(&first.relation)?;
190
191        let mut joins = Vec::new();
192        for join in &first.joins {
193            joins.push(self.convert_join(join)?);
194        }
195
196        Ok(FromClause { source, joins })
197    }
198
199    fn convert_table_factor(&self, factor: &sp::TableFactor) -> Result<TableReference> {
200        match factor {
201            sp::TableFactor::Table { name, alias, .. } => Ok(TableReference::Table {
202                name: name.to_string(),
203                alias: alias.as_ref().map(|a| a.name.value.clone()),
204            }),
205            sp::TableFactor::Derived { subquery, alias, .. } => {
206                let alias_name = alias
207                    .as_ref()
208                    .map(|a| a.name.value.clone())
209                    .ok_or_else(|| AegisError::Parse("Subquery requires alias".to_string()))?;
210                Ok(TableReference::Subquery {
211                    query: Box::new(self.convert_query(*subquery.clone())?),
212                    alias: alias_name,
213                })
214            }
215            _ => Err(AegisError::Parse("Unsupported table factor".to_string())),
216        }
217    }
218
219    fn convert_join(&self, join: &sp::Join) -> Result<JoinClause> {
220        let join_type = match &join.join_operator {
221            sp::JoinOperator::Inner(_) => JoinType::Inner,
222            sp::JoinOperator::LeftOuter(_) => JoinType::Left,
223            sp::JoinOperator::RightOuter(_) => JoinType::Right,
224            sp::JoinOperator::FullOuter(_) => JoinType::Full,
225            sp::JoinOperator::CrossJoin => JoinType::Cross,
226            _ => return Err(AegisError::Parse("Unsupported join type".to_string())),
227        };
228
229        let condition = match &join.join_operator {
230            sp::JoinOperator::Inner(c)
231            | sp::JoinOperator::LeftOuter(c)
232            | sp::JoinOperator::RightOuter(c)
233            | sp::JoinOperator::FullOuter(c) => match c {
234                sp::JoinConstraint::On(expr) => Some(self.convert_expr(expr.clone())?),
235                sp::JoinConstraint::None => None,
236                _ => return Err(AegisError::Parse("Unsupported join constraint".to_string())),
237            },
238            sp::JoinOperator::CrossJoin => None,
239            _ => None,
240        };
241
242        Ok(JoinClause {
243            join_type,
244            table: self.convert_table_factor(&join.relation)?,
245            condition,
246        })
247    }
248
249    fn convert_order_by_item(&self, item: sp::OrderByExpr) -> Result<OrderByItem> {
250        Ok(OrderByItem {
251            expression: self.convert_expr(item.expr)?,
252            ascending: item.asc.unwrap_or(true),
253            nulls_first: item.nulls_first,
254        })
255    }
256
257    fn convert_expr(&self, expr: sp::Expr) -> Result<Expression> {
258        match expr {
259            sp::Expr::Identifier(ident) => Ok(Expression::Column(ColumnRef {
260                table: None,
261                column: ident.value,
262            })),
263            sp::Expr::CompoundIdentifier(idents) => {
264                if idents.len() == 2 {
265                    Ok(Expression::Column(ColumnRef {
266                        table: Some(idents[0].value.clone()),
267                        column: idents[1].value.clone(),
268                    }))
269                } else {
270                    Ok(Expression::Column(ColumnRef {
271                        table: None,
272                        column: idents.last().map(|i| i.value.clone()).unwrap_or_default(),
273                    }))
274                }
275            }
276            sp::Expr::Value(value) => self.convert_value(value),
277            sp::Expr::BinaryOp { left, op, right } => Ok(Expression::BinaryOp {
278                left: Box::new(self.convert_expr(*left)?),
279                op: self.convert_binary_op(op)?,
280                right: Box::new(self.convert_expr(*right)?),
281            }),
282            sp::Expr::UnaryOp { op, expr } => Ok(Expression::UnaryOp {
283                op: self.convert_unary_op(op)?,
284                expr: Box::new(self.convert_expr(*expr)?),
285            }),
286            sp::Expr::Function(func) => {
287                let args = match func.args {
288                    sp::FunctionArguments::List(list) => list
289                        .args
290                        .into_iter()
291                        .filter_map(|arg| match arg {
292                            sp::FunctionArg::Unnamed(sp::FunctionArgExpr::Expr(e)) => {
293                                Some(self.convert_expr(e))
294                            }
295                            _ => None,
296                        })
297                        .collect::<Result<Vec<_>>>()?,
298                    _ => Vec::new(),
299                };
300
301                Ok(Expression::Function {
302                    name: func.name.to_string(),
303                    args,
304                    distinct: false,
305                })
306            }
307            sp::Expr::Nested(expr) => self.convert_expr(*expr),
308            sp::Expr::IsNull(expr) => Ok(Expression::IsNull {
309                expr: Box::new(self.convert_expr(*expr)?),
310                negated: false,
311            }),
312            sp::Expr::IsNotNull(expr) => Ok(Expression::IsNull {
313                expr: Box::new(self.convert_expr(*expr)?),
314                negated: true,
315            }),
316            sp::Expr::InList { expr, list, negated } => Ok(Expression::InList {
317                expr: Box::new(self.convert_expr(*expr)?),
318                list: list
319                    .into_iter()
320                    .map(|e| self.convert_expr(e))
321                    .collect::<Result<Vec<_>>>()?,
322                negated,
323            }),
324            sp::Expr::Between { expr, low, high, negated } => Ok(Expression::Between {
325                expr: Box::new(self.convert_expr(*expr)?),
326                low: Box::new(self.convert_expr(*low)?),
327                high: Box::new(self.convert_expr(*high)?),
328                negated,
329            }),
330            sp::Expr::Like { expr, pattern, negated, .. } => Ok(Expression::Like {
331                expr: Box::new(self.convert_expr(*expr)?),
332                pattern: Box::new(self.convert_expr(*pattern)?),
333                negated,
334            }),
335            _ => Err(AegisError::Parse(format!(
336                "Unsupported expression: {:?}",
337                expr
338            ))),
339        }
340    }
341
342    fn convert_value(&self, value: sp::Value) -> Result<Expression> {
343        let literal = match value {
344            sp::Value::Null => Literal::Null,
345            sp::Value::Boolean(b) => Literal::Boolean(b),
346            sp::Value::Number(n, _) => {
347                if n.contains('.') {
348                    Literal::Float(n.parse().map_err(|_| AegisError::Parse("Invalid float".to_string()))?)
349                } else {
350                    Literal::Integer(n.parse().map_err(|_| AegisError::Parse("Invalid integer".to_string()))?)
351                }
352            }
353            sp::Value::SingleQuotedString(s) | sp::Value::DoubleQuotedString(s) => Literal::String(s),
354            _ => return Err(AegisError::Parse("Unsupported literal value".to_string())),
355        };
356        Ok(Expression::Literal(literal))
357    }
358
359    fn convert_binary_op(&self, op: sp::BinaryOperator) -> Result<BinaryOperator> {
360        match op {
361            sp::BinaryOperator::Plus => Ok(BinaryOperator::Add),
362            sp::BinaryOperator::Minus => Ok(BinaryOperator::Subtract),
363            sp::BinaryOperator::Multiply => Ok(BinaryOperator::Multiply),
364            sp::BinaryOperator::Divide => Ok(BinaryOperator::Divide),
365            sp::BinaryOperator::Modulo => Ok(BinaryOperator::Modulo),
366            sp::BinaryOperator::Eq => Ok(BinaryOperator::Equal),
367            sp::BinaryOperator::NotEq => Ok(BinaryOperator::NotEqual),
368            sp::BinaryOperator::Lt => Ok(BinaryOperator::LessThan),
369            sp::BinaryOperator::LtEq => Ok(BinaryOperator::LessThanOrEqual),
370            sp::BinaryOperator::Gt => Ok(BinaryOperator::GreaterThan),
371            sp::BinaryOperator::GtEq => Ok(BinaryOperator::GreaterThanOrEqual),
372            sp::BinaryOperator::And => Ok(BinaryOperator::And),
373            sp::BinaryOperator::Or => Ok(BinaryOperator::Or),
374            sp::BinaryOperator::StringConcat => Ok(BinaryOperator::Concat),
375            _ => Err(AegisError::Parse(format!("Unsupported operator: {:?}", op))),
376        }
377    }
378
379    fn convert_unary_op(&self, op: sp::UnaryOperator) -> Result<UnaryOperator> {
380        match op {
381            sp::UnaryOperator::Not => Ok(UnaryOperator::Not),
382            sp::UnaryOperator::Minus => Ok(UnaryOperator::Negative),
383            sp::UnaryOperator::Plus => Ok(UnaryOperator::Positive),
384            _ => Err(AegisError::Parse(format!("Unsupported unary operator: {:?}", op))),
385        }
386    }
387
388    fn convert_insert(&self, insert: sp::Insert) -> Result<InsertStatement> {
389        let table = insert.table_name.to_string();
390
391        let columns = if insert.columns.is_empty() {
392            None
393        } else {
394            Some(insert.columns.into_iter().map(|c| c.value).collect())
395        };
396
397        let source = match insert.source {
398            Some(query) => match *query.body {
399                sp::SetExpr::Values(values) => {
400                    let rows = values
401                        .rows
402                        .into_iter()
403                        .map(|row| {
404                            row.into_iter()
405                                .map(|e| self.convert_expr(e))
406                                .collect::<Result<Vec<_>>>()
407                        })
408                        .collect::<Result<Vec<_>>>()?;
409                    InsertSource::Values(rows)
410                }
411                _ => InsertSource::Query(Box::new(self.convert_query(*query)?)),
412            },
413            None => return Err(AegisError::Parse("INSERT missing values".to_string())),
414        };
415
416        Ok(InsertStatement {
417            table,
418            columns,
419            source,
420        })
421    }
422
423    fn convert_update(
424        &self,
425        table: sp::TableWithJoins,
426        assignments: Vec<sp::Assignment>,
427        selection: Option<sp::Expr>,
428    ) -> Result<UpdateStatement> {
429        let table_name = match &table.relation {
430            sp::TableFactor::Table { name, .. } => name.to_string(),
431            _ => return Err(AegisError::Parse("UPDATE requires table name".to_string())),
432        };
433
434        let assigns = assignments
435            .into_iter()
436            .map(|a| {
437                let column = match a.target {
438                    sp::AssignmentTarget::ColumnName(names) => {
439                        names.0.into_iter().map(|i| i.value).collect::<Vec<_>>().join(".")
440                    }
441                    sp::AssignmentTarget::Tuple(cols) => {
442                        cols.into_iter()
443                            .map(|c| c.to_string())
444                            .collect::<Vec<_>>()
445                            .join(", ")
446                    }
447                };
448                Ok(Assignment {
449                    column,
450                    value: self.convert_expr(a.value)?,
451                })
452            })
453            .collect::<Result<Vec<_>>>()?;
454
455        let where_clause = selection.map(|e| self.convert_expr(e)).transpose()?;
456
457        Ok(UpdateStatement {
458            table: table_name,
459            assignments: assigns,
460            where_clause,
461        })
462    }
463
464    fn convert_delete(&self, delete: sp::Delete) -> Result<DeleteStatement> {
465        let table = match delete.from {
466            sp::FromTable::WithFromKeyword(tables) => {
467                tables.first()
468                    .map(|t| match &t.relation {
469                        sp::TableFactor::Table { name, .. } => name.to_string(),
470                        _ => String::new(),
471                    })
472                    .ok_or_else(|| AegisError::Parse("DELETE missing table".to_string()))?
473            }
474            sp::FromTable::WithoutKeyword(tables) => {
475                tables.first()
476                    .map(|t| match &t.relation {
477                        sp::TableFactor::Table { name, .. } => name.to_string(),
478                        _ => String::new(),
479                    })
480                    .ok_or_else(|| AegisError::Parse("DELETE missing table".to_string()))?
481            }
482        };
483
484        let where_clause = delete.selection.map(|e| self.convert_expr(e)).transpose()?;
485
486        Ok(DeleteStatement {
487            table,
488            where_clause,
489        })
490    }
491
492    fn convert_create_table(&self, create: sp::CreateTable) -> Result<CreateTableStatement> {
493        let columns = create
494            .columns
495            .into_iter()
496            .map(|col| {
497                Ok(ColumnDefinition {
498                    name: col.name.value,
499                    data_type: self.convert_data_type(&col.data_type)?,
500                    nullable: !col.options.iter().any(|o| {
501                        matches!(o.option, sp::ColumnOption::NotNull)
502                    }),
503                    default: col
504                        .options
505                        .iter()
506                        .find_map(|o| match &o.option {
507                            sp::ColumnOption::Default(e) => Some(self.convert_expr(e.clone())),
508                            _ => None,
509                        })
510                        .transpose()?,
511                    constraints: Vec::new(),
512                })
513            })
514            .collect::<Result<Vec<_>>>()?;
515
516        Ok(CreateTableStatement {
517            name: create.name.to_string(),
518            columns,
519            constraints: Vec::new(),
520            if_not_exists: create.if_not_exists,
521        })
522    }
523
524    fn convert_drop(
525        &self,
526        object_type: sp::ObjectType,
527        if_exists: bool,
528        names: Vec<sp::ObjectName>,
529    ) -> Result<Statement> {
530        let name = names
531            .first()
532            .map(|n| n.to_string())
533            .ok_or_else(|| AegisError::Parse("DROP missing name".to_string()))?;
534
535        match object_type {
536            sp::ObjectType::Table => Ok(Statement::DropTable(DropTableStatement {
537                name,
538                if_exists,
539            })),
540            sp::ObjectType::Index => Ok(Statement::DropIndex(DropIndexStatement {
541                name,
542                if_exists,
543            })),
544            _ => Err(AegisError::Parse(format!(
545                "Unsupported DROP object type: {:?}",
546                object_type
547            ))),
548        }
549    }
550
551    fn convert_create_index(&self, create: sp::CreateIndex) -> Result<CreateIndexStatement> {
552        let name = create
553            .name
554            .map(|n| n.to_string())
555            .ok_or_else(|| AegisError::Parse("CREATE INDEX missing name".to_string()))?;
556
557        let table = create.table_name.to_string();
558
559        let columns = create
560            .columns
561            .into_iter()
562            .map(|c| c.expr.to_string())
563            .collect();
564
565        Ok(CreateIndexStatement {
566            name,
567            table,
568            columns,
569            unique: create.unique,
570            if_not_exists: create.if_not_exists,
571        })
572    }
573
574    fn convert_alter_table(
575        &self,
576        name: sp::ObjectName,
577        operations: Vec<sp::AlterTableOperation>,
578    ) -> Result<AlterTableStatement> {
579        let ops = operations
580            .into_iter()
581            .map(|op| self.convert_alter_operation(op))
582            .collect::<Result<Vec<_>>>()?;
583
584        Ok(AlterTableStatement {
585            name: name.to_string(),
586            operations: ops,
587        })
588    }
589
590    fn convert_alter_operation(&self, op: sp::AlterTableOperation) -> Result<AlterTableOperation> {
591        match op {
592            sp::AlterTableOperation::AddColumn { column_def, .. } => {
593                let col = ColumnDefinition {
594                    name: column_def.name.value.clone(),
595                    data_type: self.convert_data_type(&column_def.data_type)?,
596                    nullable: !column_def.options.iter().any(|o| {
597                        matches!(o.option, sp::ColumnOption::NotNull)
598                    }),
599                    default: column_def
600                        .options
601                        .iter()
602                        .find_map(|o| match &o.option {
603                            sp::ColumnOption::Default(e) => Some(self.convert_expr(e.clone())),
604                            _ => None,
605                        })
606                        .transpose()?,
607                    constraints: Vec::new(),
608                };
609                Ok(AlterTableOperation::AddColumn(col))
610            }
611            sp::AlterTableOperation::DropColumn { column_name, if_exists, .. } => {
612                Ok(AlterTableOperation::DropColumn {
613                    name: column_name.value,
614                    if_exists,
615                })
616            }
617            sp::AlterTableOperation::RenameColumn { old_column_name, new_column_name } => {
618                Ok(AlterTableOperation::RenameColumn {
619                    old_name: old_column_name.value,
620                    new_name: new_column_name.value,
621                })
622            }
623            sp::AlterTableOperation::RenameTable { table_name } => {
624                Ok(AlterTableOperation::RenameTable {
625                    new_name: table_name.to_string(),
626                })
627            }
628            sp::AlterTableOperation::AlterColumn { column_name, op } => {
629                match op {
630                    sp::AlterColumnOperation::SetDataType { data_type, .. } => {
631                        Ok(AlterTableOperation::AlterColumn {
632                            name: column_name.value,
633                            data_type: Some(self.convert_data_type(&data_type)?),
634                            set_not_null: None,
635                            set_default: None,
636                        })
637                    }
638                    sp::AlterColumnOperation::SetNotNull => {
639                        Ok(AlterTableOperation::AlterColumn {
640                            name: column_name.value,
641                            data_type: None,
642                            set_not_null: Some(true),
643                            set_default: None,
644                        })
645                    }
646                    sp::AlterColumnOperation::DropNotNull => {
647                        Ok(AlterTableOperation::AlterColumn {
648                            name: column_name.value,
649                            data_type: None,
650                            set_not_null: Some(false),
651                            set_default: None,
652                        })
653                    }
654                    sp::AlterColumnOperation::SetDefault { value } => {
655                        Ok(AlterTableOperation::AlterColumn {
656                            name: column_name.value,
657                            data_type: None,
658                            set_not_null: None,
659                            set_default: Some(Some(self.convert_expr(value)?)),
660                        })
661                    }
662                    sp::AlterColumnOperation::DropDefault => {
663                        Ok(AlterTableOperation::AlterColumn {
664                            name: column_name.value,
665                            data_type: None,
666                            set_not_null: None,
667                            set_default: Some(None),
668                        })
669                    }
670                    _ => Err(AegisError::Parse(format!(
671                        "Unsupported ALTER COLUMN operation: {:?}",
672                        op
673                    ))),
674                }
675            }
676            sp::AlterTableOperation::DropConstraint { name, .. } => {
677                Ok(AlterTableOperation::DropConstraint {
678                    name: name.value,
679                })
680            }
681            _ => Err(AegisError::Parse(format!(
682                "Unsupported ALTER TABLE operation: {:?}",
683                op
684            ))),
685        }
686    }
687
688    fn convert_data_type(&self, dt: &sp::DataType) -> Result<DataType> {
689        match dt {
690            sp::DataType::Boolean => Ok(DataType::Boolean),
691            sp::DataType::TinyInt(_) => Ok(DataType::TinyInt),
692            sp::DataType::SmallInt(_) => Ok(DataType::SmallInt),
693            sp::DataType::Int(_) | sp::DataType::Integer(_) => Ok(DataType::Integer),
694            sp::DataType::BigInt(_) => Ok(DataType::BigInt),
695            sp::DataType::Real => Ok(DataType::Float),
696            sp::DataType::Float(_) | sp::DataType::Double | sp::DataType::DoublePrecision => {
697                Ok(DataType::Double)
698            }
699            sp::DataType::Decimal(info) | sp::DataType::Numeric(info) => {
700                let (precision, scale) = match info {
701                    sp::ExactNumberInfo::PrecisionAndScale(p, s) => (*p as u8, *s as u8),
702                    sp::ExactNumberInfo::Precision(p) => (*p as u8, 0),
703                    sp::ExactNumberInfo::None => (18, 0),
704                };
705                Ok(DataType::Decimal { precision, scale })
706            }
707            sp::DataType::Char(len) => {
708                let size = len.as_ref().and_then(|l| {
709                    match l {
710                        sp::CharacterLength::IntegerLength { length, .. } => Some(*length as u16),
711                        sp::CharacterLength::Max => None,
712                    }
713                }).unwrap_or(1);
714                Ok(DataType::Char(size))
715            }
716            sp::DataType::Varchar(len) => {
717                let size = len.as_ref().and_then(|l| {
718                    match l {
719                        sp::CharacterLength::IntegerLength { length, .. } => Some(*length as u16),
720                        sp::CharacterLength::Max => None,
721                    }
722                }).unwrap_or(255);
723                Ok(DataType::Varchar(size))
724            }
725            sp::DataType::Text => Ok(DataType::Text),
726            sp::DataType::Blob(_) => Ok(DataType::Blob),
727            sp::DataType::Date => Ok(DataType::Date),
728            sp::DataType::Time(..) => Ok(DataType::Time),
729            sp::DataType::Timestamp(..) => Ok(DataType::Timestamp),
730            sp::DataType::JSON => Ok(DataType::Json),
731            sp::DataType::Uuid => Ok(DataType::Uuid),
732            _ => Err(AegisError::Parse(format!("Unsupported data type: {:?}", dt))),
733        }
734    }
735
736    fn extract_limit(&self, expr: sp::Expr) -> Result<u64> {
737        match expr {
738            sp::Expr::Value(sp::Value::Number(n, _)) => n
739                .parse()
740                .map_err(|_| AegisError::Parse("Invalid LIMIT value".to_string())),
741            _ => Err(AegisError::Parse("LIMIT must be a number".to_string())),
742        }
743    }
744}
745
746impl Default for Parser {
747    fn default() -> Self {
748        Self::new()
749    }
750}
751
752// =============================================================================
753// Tests
754// =============================================================================
755
756#[cfg(test)]
757mod tests {
758    use super::*;
759
760    #[test]
761    fn test_parse_simple_select() {
762        let parser = Parser::new();
763        let stmt = parser.parse_single("SELECT id, name FROM users").unwrap();
764
765        match stmt {
766            Statement::Select(select) => {
767                assert_eq!(select.columns.len(), 2);
768                assert!(select.from.is_some());
769            }
770            _ => panic!("Expected SELECT statement"),
771        }
772    }
773
774    #[test]
775    fn test_parse_select_with_where() {
776        let parser = Parser::new();
777        let stmt = parser
778            .parse_single("SELECT * FROM users WHERE age > 18")
779            .unwrap();
780
781        match stmt {
782            Statement::Select(select) => {
783                assert!(select.where_clause.is_some());
784            }
785            _ => panic!("Expected SELECT statement"),
786        }
787    }
788
789    #[test]
790    fn test_parse_select_with_join() {
791        let parser = Parser::new();
792        let stmt = parser
793            .parse_single("SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id")
794            .unwrap();
795
796        match stmt {
797            Statement::Select(select) => {
798                let from = select.from.unwrap();
799                assert_eq!(from.joins.len(), 1);
800                assert_eq!(from.joins[0].join_type, JoinType::Inner);
801            }
802            _ => panic!("Expected SELECT statement"),
803        }
804    }
805
806    #[test]
807    fn test_parse_insert() {
808        let parser = Parser::new();
809        let stmt = parser
810            .parse_single("INSERT INTO users (name, age) VALUES ('Alice', 25)")
811            .unwrap();
812
813        match stmt {
814            Statement::Insert(insert) => {
815                assert_eq!(insert.table, "users");
816                assert_eq!(insert.columns, Some(vec!["name".to_string(), "age".to_string()]));
817            }
818            _ => panic!("Expected INSERT statement"),
819        }
820    }
821
822    #[test]
823    fn test_parse_update() {
824        let parser = Parser::new();
825        let stmt = parser
826            .parse_single("UPDATE users SET age = 26 WHERE name = 'Alice'")
827            .unwrap();
828
829        match stmt {
830            Statement::Update(update) => {
831                assert_eq!(update.table, "users");
832                assert_eq!(update.assignments.len(), 1);
833                assert!(update.where_clause.is_some());
834            }
835            _ => panic!("Expected UPDATE statement"),
836        }
837    }
838
839    #[test]
840    fn test_parse_delete() {
841        let parser = Parser::new();
842        let stmt = parser
843            .parse_single("DELETE FROM users WHERE age < 18")
844            .unwrap();
845
846        match stmt {
847            Statement::Delete(delete) => {
848                assert_eq!(delete.table, "users");
849                assert!(delete.where_clause.is_some());
850            }
851            _ => panic!("Expected DELETE statement"),
852        }
853    }
854
855    #[test]
856    fn test_parse_create_table() {
857        let parser = Parser::new();
858        let stmt = parser
859            .parse_single(
860                "CREATE TABLE users (
861                    id INTEGER NOT NULL,
862                    name VARCHAR(255),
863                    age INTEGER
864                )",
865            )
866            .unwrap();
867
868        match stmt {
869            Statement::CreateTable(create) => {
870                assert_eq!(create.name, "users");
871                assert_eq!(create.columns.len(), 3);
872                assert!(!create.columns[0].nullable);
873                assert!(create.columns[1].nullable);
874            }
875            _ => panic!("Expected CREATE TABLE statement"),
876        }
877    }
878
879    #[test]
880    fn test_parse_transaction_statements() {
881        let parser = Parser::new();
882
883        assert!(matches!(parser.parse_single("BEGIN").unwrap(), Statement::Begin));
884        assert!(matches!(parser.parse_single("COMMIT").unwrap(), Statement::Commit));
885        assert!(matches!(parser.parse_single("ROLLBACK").unwrap(), Statement::Rollback));
886    }
887}