quill_sql/plan/logical_planner/
logical_planner.rs1use crate::error::{QuillSQLError, QuillSQLResult};
2
3use crate::catalog::Catalog;
4use crate::plan::logical_plan::{
5 Analyze, LogicalPlan, OrderByExpr, TransactionModes, TransactionScope,
6};
7use crate::sql::ast;
8use crate::sql::ast::Value;
9use crate::utils::table_ref::TableReference;
10use sqlparser::ast::{Ident, ObjectType};
11
12pub struct PlannerContext<'a> {
13 pub catalog: &'a Catalog,
14}
15
16pub struct LogicalPlanner<'a> {
17 pub context: PlannerContext<'a>,
18}
19impl<'a> LogicalPlanner<'a> {
20 pub fn plan(&mut self, stmt: &ast::Statement) -> QuillSQLResult<LogicalPlan> {
21 match stmt {
22 ast::Statement::CreateTable {
23 name,
24 columns,
25 if_not_exists,
26 ..
27 } => self.plan_create_table(name, columns, *if_not_exists),
28 ast::Statement::CreateIndex {
29 name,
30 table_name,
31 columns,
32 ..
33 } => self.plan_create_index(name, table_name, columns),
34 ast::Statement::Drop {
35 object_type,
36 if_exists,
37 names,
38 cascade,
39 restrict: _,
40 purge,
41 } => match object_type {
42 ObjectType::Table => self.plan_drop_table(names, *if_exists, *cascade, *purge),
43 ObjectType::Index => self.plan_drop_index(names, *if_exists, *cascade, *purge),
44 other => Err(QuillSQLError::NotSupport(format!(
45 "DROP {} is not supported",
46 other
47 ))),
48 },
49 ast::Statement::Query(query) => self.plan_query(query),
50 ast::Statement::Insert {
51 table_name,
52 columns,
53 source,
54 ..
55 } => self.plan_insert(table_name, columns, source),
56 ast::Statement::Update {
57 table,
58 assignments,
59 selection,
60 ..
61 } => self.plan_update(table, assignments, selection),
62 ast::Statement::Delete {
63 tables,
64 from,
65 using,
66 selection,
67 returning,
68 } => {
69 if !tables.is_empty() {
70 return Err(QuillSQLError::Plan(
71 "DELETE with table aliases is not supported".to_string(),
72 ));
73 }
74 if using.is_some() {
75 return Err(QuillSQLError::Plan(
76 "DELETE USING clause is not supported".to_string(),
77 ));
78 }
79 if returning.is_some() {
80 return Err(QuillSQLError::Plan(
81 "DELETE RETURNING is not supported".to_string(),
82 ));
83 }
84 if from.len() != 1 {
85 return Err(QuillSQLError::Plan(
86 "DELETE must target exactly one table".to_string(),
87 ));
88 }
89 self.plan_delete(&from[0], selection)
90 }
91 ast::Statement::Explain { statement, .. } => self.plan_explain(statement),
92 ast::Statement::StartTransaction { modes, .. } => self.plan_begin_transaction(modes),
93 ast::Statement::Commit { .. } => Ok(LogicalPlan::CommitTransaction),
94 ast::Statement::Rollback { .. } => Ok(LogicalPlan::RollbackTransaction),
95 ast::Statement::SetTransaction {
96 modes,
97 snapshot,
98 session,
99 } => self.plan_set_transaction(*session, snapshot, modes),
100 ast::Statement::Analyze {
101 table_name,
102 partitions,
103 for_columns,
104 columns,
105 cache_metadata,
106 noscan,
107 compute_statistics: _,
108 } => self.plan_analyze(
109 table_name,
110 partitions,
111 *for_columns,
112 columns,
113 *cache_metadata,
114 *noscan,
115 ),
116 _ => unimplemented!(),
117 }
118 }
119
120 fn plan_begin_transaction(
121 &self,
122 modes: &Vec<ast::TransactionMode>,
123 ) -> QuillSQLResult<LogicalPlan> {
124 Ok(LogicalPlan::BeginTransaction(TransactionModes::from_modes(
125 modes,
126 )))
127 }
128
129 fn plan_set_transaction(
130 &self,
131 session_scope: bool,
132 snapshot: &Option<Value>,
133 modes: &Vec<ast::TransactionMode>,
134 ) -> QuillSQLResult<LogicalPlan> {
135 if snapshot.is_some() {
136 return Err(QuillSQLError::Plan(
137 "SET TRANSACTION SNAPSHOT is not supported".to_string(),
138 ));
139 };
140 let logical_scope = if session_scope {
141 TransactionScope::Session
142 } else {
143 TransactionScope::Transaction
144 };
145 Ok(LogicalPlan::SetTransaction {
146 scope: logical_scope,
147 modes: TransactionModes::from_modes(modes),
148 })
149 }
150
151 pub fn bind_order_by_expr(&self, order_by: &ast::OrderByExpr) -> QuillSQLResult<OrderByExpr> {
152 let expr = self.bind_expr(&order_by.expr)?;
153 Ok(OrderByExpr {
154 expr: Box::new(expr),
155 asc: order_by.asc.unwrap_or(true),
156 nulls_first: order_by.nulls_first.unwrap_or(false),
157 })
158 }
159
160 pub fn bind_table_name(&self, table_name: &ast::ObjectName) -> QuillSQLResult<TableReference> {
161 match table_name.0.as_slice() {
162 [table] => Ok(TableReference::Bare {
163 table: table.value.clone(),
164 }),
165 [schema, table] => Ok(TableReference::Partial {
166 schema: schema.value.clone(),
167 table: table.value.clone(),
168 }),
169 [catalog, schema, table] => Ok(TableReference::Full {
170 catalog: catalog.value.clone(),
171 schema: schema.value.clone(),
172 table: table.value.clone(),
173 }),
174 _ => Err(QuillSQLError::Plan(format!(
175 "Fail to plan table name: {}",
176 table_name
177 ))),
178 }
179 }
180
181 fn plan_analyze(
182 &self,
183 table_name: &ast::ObjectName,
184 partitions: &Option<Vec<ast::Expr>>,
185 for_columns: bool,
186 columns: &[Ident],
187 cache_metadata: bool,
188 noscan: bool,
189 ) -> QuillSQLResult<LogicalPlan> {
190 if partitions.as_ref().map_or(false, |p| !p.is_empty()) {
191 return Err(QuillSQLError::Plan(
192 "ANALYZE PARTITION is not supported".to_string(),
193 ));
194 }
195 if for_columns || !columns.is_empty() {
196 return Err(QuillSQLError::Plan(
197 "ANALYZE FOR COLUMNS is not supported".to_string(),
198 ));
199 }
200 if cache_metadata || noscan {
201 return Err(QuillSQLError::Plan(
202 "ANALYZE options NOSCAN/CACHE METADATA are not supported".to_string(),
203 ));
204 }
205 let table_ref = self.bind_table_name(table_name)?;
206 Ok(LogicalPlan::Analyze(Analyze { table: table_ref }))
207 }
208}