bustubx 0.3.0

A simple relational database written in Rust
Documentation
use crate::common::{ScalarValue, TableReference};
use crate::expression::{AggregateFunction, BinaryExpr, ColumnExpr, Expr, Literal};
use crate::function::AggregateFunctionKind;
use crate::planner::LogicalPlanner;
use crate::{BustubxError, BustubxResult};

impl LogicalPlanner<'_> {
    pub fn bind_expr(&self, sql: &sqlparser::ast::Expr) -> BustubxResult<Expr> {
        match sql {
            sqlparser::ast::Expr::Identifier(ident) => Ok(Expr::Column(ColumnExpr {
                relation: None,
                name: ident.value.clone(),
            })),
            sqlparser::ast::Expr::BinaryOp { left, op, right } => {
                let left = Box::new(self.bind_expr(left)?);
                let right = Box::new(self.bind_expr(right)?);
                Ok(Expr::Binary(BinaryExpr {
                    left,
                    op: op.try_into()?,
                    right,
                }))
            }
            sqlparser::ast::Expr::Value(value) => self.bind_value(value),
            sqlparser::ast::Expr::CompoundIdentifier(idents) => match idents.as_slice() {
                [col] => Ok(Expr::Column(ColumnExpr {
                    relation: None,
                    name: col.value.clone(),
                })),
                [table, col] => Ok(Expr::Column(ColumnExpr {
                    relation: Some(TableReference::bare(table.value.clone())),
                    name: col.value.clone(),
                })),
                [schema, table, col] => Ok(Expr::Column(ColumnExpr {
                    relation: Some(TableReference::partial(
                        schema.value.clone(),
                        table.value.clone(),
                    )),
                    name: col.value.clone(),
                })),
                [catalog, schema, table, col] => Ok(Expr::Column(ColumnExpr {
                    relation: Some(TableReference::full(
                        catalog.value.clone(),
                        schema.value.clone(),
                        table.value.clone(),
                    )),
                    name: col.value.clone(),
                })),
                _ => Err(BustubxError::NotSupport(format!(
                    "sqlparser expr CompoundIdentifier has more than 4 identifiers: {:?}",
                    idents
                ))),
            },
            sqlparser::ast::Expr::Function(function) => self.bind_function(function),
            _ => Err(BustubxError::NotSupport(format!(
                "sqlparser expr {} not supported",
                sql
            ))),
        }
    }

    pub fn bind_value(&self, value: &sqlparser::ast::Value) -> BustubxResult<Expr> {
        match value {
            sqlparser::ast::Value::Number(s, _) => {
                if let Ok(num) = s.parse::<i64>() {
                    return Ok(Expr::Literal(Literal { value: num.into() }));
                }
                if let Ok(num) = s.parse::<f64>() {
                    return Ok(Expr::Literal(Literal { value: num.into() }));
                }
                Err(BustubxError::Internal(
                    "Failed to parse sql number value".to_string(),
                ))
            }
            sqlparser::ast::Value::Boolean(b) => Ok(Expr::Literal(Literal { value: (*b).into() })),
            sqlparser::ast::Value::Null => Ok(Expr::Literal(Literal {
                value: ScalarValue::Int8(None),
            })),
            sqlparser::ast::Value::SingleQuotedString(s) => Ok(Expr::Literal(Literal {
                value: s.clone().into(),
            })),
            _ => Err(BustubxError::NotSupport(format!(
                "sqlparser value {} not supported",
                value
            ))),
        }
    }

    pub fn bind_function(&self, function: &sqlparser::ast::Function) -> BustubxResult<Expr> {
        let name = function.name.to_string();

        if let Some(func_kind) = AggregateFunctionKind::find(name.as_str()) {
            let args = function
                .args
                .iter()
                .map(|arg| self.bind_function_arg(arg))
                .collect::<BustubxResult<Vec<Expr>>>()?;
            return Ok(Expr::AggregateFunction(AggregateFunction {
                func_kind,
                args,
                distinct: function.distinct,
            }));
        }

        Err(BustubxError::Plan(format!(
            "The function {} is not supported",
            function
        )))
    }

    pub fn bind_function_arg(&self, arg: &sqlparser::ast::FunctionArg) -> BustubxResult<Expr> {
        match arg {
            sqlparser::ast::FunctionArg::Named {
                name: _,
                arg: sqlparser::ast::FunctionArgExpr::Expr(arg),
            } => self.bind_expr(arg),
            sqlparser::ast::FunctionArg::Unnamed(sqlparser::ast::FunctionArgExpr::Expr(arg)) => {
                self.bind_expr(arg)
            }
            _ => Err(BustubxError::Plan(format!(
                "The function arg {} is not supported",
                arg
            ))),
        }
    }
}