Skip to main content

dbx_core/sql/planner/logical/
mod.rs

1//! SQL 논리 플래너 — AST → LogicalPlan 변환
2//!
3//! 이 모듈은 SQL AST를 논리 실행 계획으로 변환합니다.
4
5use crate::error::{DbxError, DbxResult};
6use crate::sql::planner::types::*;
7use sqlparser::ast::Statement;
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10
11// 서브 모듈
12pub mod custom;
13pub mod ddl;
14pub mod delete;
15pub mod helpers;
16pub mod insert;
17pub mod select;
18pub mod update;
19
20// Re-export helper functions
21pub use helpers::{convert_binary_op, extract_usize, match_scalar_function};
22
23/// 논리 플랜 빌더 — AST → LogicalPlan 변환
24pub struct LogicalPlanner {
25    pub(crate) alias_map: Arc<RwLock<HashMap<String, Expr>>>,
26}
27
28impl LogicalPlanner {
29    pub fn new() -> Self {
30        Self {
31            alias_map: Arc::new(RwLock::new(HashMap::new())),
32        }
33    }
34
35    /// SQL Statement → LogicalPlan 변환
36    pub fn plan(&self, statement: &Statement) -> DbxResult<LogicalPlan> {
37        match statement {
38            Statement::Query(query) => self.plan_query(query),
39            Statement::Insert { .. } => {
40                // Extract Insert struct from Statement
41                if let Statement::Insert(insert) = statement {
42                    self.plan_insert(insert)
43                } else {
44                    unreachable!()
45                }
46            }
47            Statement::Update { .. } => self.plan_update(statement),
48            Statement::Delete { .. } => self.plan_delete(statement),
49            Statement::Drop { .. }
50            | Statement::CreateTable(_)
51            | Statement::AlterTable { .. }
52            | Statement::CreateIndex(_) => self.plan_ddl(statement),
53            _ => {
54                // Try to parse custom statements (CREATE FUNCTION, CREATE TRIGGER, CREATE JOB)
55                let sql = format!("{:?}", statement);
56                if sql.contains("CREATE FUNCTION")
57                    || sql.contains("CREATE TRIGGER")
58                    || sql.contains("CREATE JOB")
59                {
60                    self.parse_custom_statement(statement)
61                } else {
62                    Err(DbxError::SqlNotSupported {
63                        feature: format!("Statement type: {:?}", statement),
64                        hint: "Only SELECT, INSERT, UPDATE, DELETE, DROP TABLE, CREATE TABLE, ALTER TABLE, CREATE INDEX, DROP INDEX, CREATE FUNCTION, CREATE TRIGGER, and CREATE JOB are currently supported".to_string(),
65                    })
66                }
67            }
68        }
69    }
70}
71
72impl Default for LogicalPlanner {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use sqlparser::dialect::GenericDialect;
82    use sqlparser::parser::Parser;
83
84    #[test]
85    fn test_simple_select() {
86        let sql = "SELECT id, name FROM users WHERE age > 18";
87        let dialect = GenericDialect {};
88        let statements = Parser::parse_sql(&dialect, sql).unwrap();
89        let planner = LogicalPlanner::new();
90        let plan = planner.plan(&statements[0]).unwrap();
91        assert!(matches!(plan, LogicalPlan::Project { .. }));
92    }
93
94    #[test]
95    fn test_insert() {
96        let sql = "INSERT INTO users (id, name) VALUES (1, 'Alice')";
97        let dialect = GenericDialect {};
98        let statements = Parser::parse_sql(&dialect, sql).unwrap();
99        let planner = LogicalPlanner::new();
100        let plan = planner.plan(&statements[0]).unwrap();
101        assert!(matches!(plan, LogicalPlan::Insert { .. }));
102    }
103
104    #[test]
105    fn test_update() {
106        let sql = "UPDATE users SET name = 'Bob' WHERE id = 1";
107        let dialect = GenericDialect {};
108        let statements = Parser::parse_sql(&dialect, sql).unwrap();
109        let planner = LogicalPlanner::new();
110        let plan = planner.plan(&statements[0]).unwrap();
111        assert!(matches!(plan, LogicalPlan::Update { .. }));
112    }
113
114    #[test]
115    fn test_delete() {
116        let sql = "DELETE FROM users WHERE id = 1";
117        let dialect = GenericDialect {};
118        let statements = Parser::parse_sql(&dialect, sql).unwrap();
119        let planner = LogicalPlanner::new();
120        let plan = planner.plan(&statements[0]).unwrap();
121        assert!(matches!(plan, LogicalPlan::Delete { .. }));
122    }
123
124    #[test]
125    fn test_create_table() {
126        let sql = "CREATE TABLE users (id INT, name TEXT)";
127        let dialect = GenericDialect {};
128        let statements = Parser::parse_sql(&dialect, sql).unwrap();
129        let planner = LogicalPlanner::new();
130        let plan = planner.plan(&statements[0]).unwrap();
131        assert!(matches!(plan, LogicalPlan::CreateTable { .. }));
132    }
133
134    #[test]
135    fn test_drop_table() {
136        let sql = "DROP TABLE users";
137        let dialect = GenericDialect {};
138        let statements = Parser::parse_sql(&dialect, sql).unwrap();
139        let planner = LogicalPlanner::new();
140        let plan = planner.plan(&statements[0]).unwrap();
141        assert!(matches!(plan, LogicalPlan::DropTable { .. }));
142    }
143}