dbx_core/sql/planner/logical/
mod.rs1use crate::error::{DbxError, DbxResult};
6use crate::sql::planner::types::*;
7use sqlparser::ast::Statement;
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10
11pub mod custom;
13pub mod ddl;
14pub mod delete;
15pub mod helpers;
16pub mod insert;
17pub mod select;
18pub mod update;
19
20pub use helpers::{convert_binary_op, extract_usize, match_scalar_function};
22
23pub 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 pub fn plan(&self, statement: &Statement) -> DbxResult<LogicalPlan> {
37 match statement {
38 Statement::Query(query) => self.plan_query(query),
39 Statement::Insert { .. } => {
40 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 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}