quill_sql/plan/logical_planner/
plan_create_table.rs1use crate::error::{QuillSQLError, QuillSQLResult};
2use std::collections::HashSet;
3
4use crate::catalog::{Column, DataType};
5use crate::expression::Expr;
6use crate::plan::logical_plan::{CreateTable, LogicalPlan};
7use crate::utils::scalar::ScalarValue;
8
9use super::LogicalPlanner;
10
11impl<'a> LogicalPlanner<'a> {
12 pub fn plan_create_table(
13 &self,
14 name: &sqlparser::ast::ObjectName,
15 column_defs: &Vec<sqlparser::ast::ColumnDef>,
16 if_not_exists: bool,
17 engine: Option<&str>,
18 ) -> QuillSQLResult<LogicalPlan> {
19 let name = self.bind_table_name(name)?;
20 validate_table_engine(engine)?;
21 let mut columns = vec![];
22 for col_def in column_defs {
23 let data_type: DataType = (&col_def.data_type).try_into()?;
24 let not_null: bool = col_def
25 .options
26 .iter()
27 .any(|opt| matches!(opt.option, sqlparser::ast::ColumnOption::NotNull));
28 let default_expr: Option<&sqlparser::ast::Expr> = col_def
29 .options
30 .iter()
31 .find(|opt| matches!(opt.option, sqlparser::ast::ColumnOption::Default(_)))
32 .map(|opt| {
33 if let sqlparser::ast::ColumnOption::Default(expr) = &opt.option {
34 expr
35 } else {
36 unreachable!()
37 }
38 });
39 let default = if let Some(expr) = default_expr {
40 let expr = self.bind_expr(expr)?;
41 match expr {
42 Expr::Literal(lit) => lit.value.cast_to(&data_type)?,
43 _ => {
44 return Err(QuillSQLError::Internal(
45 "The expr is not literal".to_string(),
46 ))
47 }
48 }
49 } else {
50 ScalarValue::new_empty(data_type)
51 };
52
53 columns.push(
54 Column::new(col_def.name.value.clone(), data_type, !not_null)
55 .with_relation(Some(name.clone()))
56 .with_default(default),
57 )
58 }
59
60 check_column_name_conflict(&columns)?;
61 Ok(LogicalPlan::CreateTable(CreateTable {
62 name,
63 columns,
64 if_not_exists,
65 }))
66 }
67}
68
69fn validate_table_engine(engine: Option<&str>) -> QuillSQLResult<()> {
70 match engine.map(str::to_ascii_lowercase).as_deref() {
71 None | Some("holt") | Some("default") => Ok(()),
72 Some(other) => Err(QuillSQLError::NotSupport(format!(
73 "table engine {other} is not supported; only ENGINE=HOLT is supported"
74 ))),
75 }
76}
77
78fn check_column_name_conflict(columns: &[Column]) -> QuillSQLResult<()> {
79 let mut names = HashSet::new();
80 for col in columns {
81 if names.contains(col.name.as_str()) {
82 return Err(QuillSQLError::Plan(format!(
83 "Column names have conflict on '{}'",
84 col.name
85 )));
86 } else {
87 names.insert(col.name.as_str());
88 }
89 }
90 Ok(())
91}