Skip to main content

quill_sql/plan/logical_planner/
plan_create_table.rs

1use 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}