gluesql-core 0.20.0

GlueSQL - Open source SQL database engine fully written in Rust with pure functional execution layer, easily swappable storage and web assembly support!
Documentation
mod derived;
mod dictionary;
mod series;
mod table;
mod table_access;

pub use {
    derived::DerivedSourcePlan,
    dictionary::DictionarySourcePlan,
    series::SeriesSourcePlan,
    table::TableSourcePlan,
    table_access::{IndexPredicatePlan, TableAccessPlan},
};

use {
    crate::ast,
    serde::{Deserialize, Serialize},
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SourcePlan {
    Table(TableSourcePlan),
    Derived(DerivedSourcePlan),
    Series(SeriesSourcePlan),
    Dictionary(DictionarySourcePlan),
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TableAliasPlan {
    pub name: String,
    pub columns: Vec<String>,
}

impl SourcePlan {
    pub fn alias_name(&self) -> &str {
        match self {
            Self::Table(TableSourcePlan { name, alias, .. }) => alias
                .as_ref()
                .map_or(name.as_str(), |alias| alias.name.as_str()),
            Self::Derived(DerivedSourcePlan {
                alias: TableAliasPlan { name, .. },
                ..
            })
            | Self::Series(SeriesSourcePlan {
                alias: TableAliasPlan { name, .. },
                ..
            })
            | Self::Dictionary(DictionarySourcePlan {
                alias: TableAliasPlan { name, .. },
                ..
            }) => name.as_str(),
        }
    }
}

impl From<ast::TableFactor> for SourcePlan {
    fn from(source: ast::TableFactor) -> Self {
        match source {
            ast::TableFactor::Table { name, alias } => Self::Table(TableSourcePlan {
                name,
                alias: alias.map(Into::into),
                access: TableAccessPlan::FullScan,
            }),
            ast::TableFactor::Derived { subquery, alias } => Self::Derived(DerivedSourcePlan {
                query: Box::new(subquery.into()),
                alias: alias.into(),
            }),
            ast::TableFactor::Series { alias, size } => Self::Series(SeriesSourcePlan {
                alias: alias.into(),
                size: size.into(),
            }),
            ast::TableFactor::Dictionary { dict, alias } => {
                Self::Dictionary(DictionarySourcePlan {
                    dictionary: dict,
                    alias: alias.into(),
                })
            }
        }
    }
}

impl From<ast::TableAlias> for TableAliasPlan {
    fn from(alias: ast::TableAlias) -> Self {
        let ast::TableAlias { name, columns } = alias;

        Self { name, columns }
    }
}

#[cfg(test)]
mod tests {
    use {
        super::{
            DerivedSourcePlan, DictionarySourcePlan, SeriesSourcePlan, SourcePlan, TableAccessPlan,
            TableAliasPlan, TableSourcePlan,
        },
        crate::{
            ast::{self, Dictionary, Expr, Literal, Query, SetExpr, Values},
            plan::{ExprPlan, QueryPlan},
        },
        pretty_assertions::assert_eq,
    };

    fn alias(name: &str) -> ast::TableAlias {
        ast::TableAlias {
            name: name.to_owned(),
            columns: Vec::new(),
        }
    }

    #[test]
    fn converts_each_ast_source_to_typed_plan() {
        let actual = SourcePlan::from(ast::TableFactor::Table {
            name: "Item".to_owned(),
            alias: Some(alias("i")),
        });
        let expected = SourcePlan::Table(TableSourcePlan {
            name: "Item".to_owned(),
            alias: Some(TableAliasPlan {
                name: "i".to_owned(),
                columns: Vec::new(),
            }),
            access: TableAccessPlan::FullScan,
        });
        assert_eq!(actual, expected);

        let query = Query {
            body: SetExpr::Values(Values(vec![vec![Expr::Literal(Literal::Number(1.into()))]])),
            order_by: Vec::new(),
            limit: None,
            offset: None,
        };
        let actual = SourcePlan::from(ast::TableFactor::Derived {
            subquery: query.clone(),
            alias: alias("derived"),
        });
        let expected = SourcePlan::Derived(DerivedSourcePlan {
            query: Box::new(QueryPlan::from(query)),
            alias: TableAliasPlan {
                name: "derived".to_owned(),
                columns: Vec::new(),
            },
        });
        assert_eq!(actual, expected);

        let actual = SourcePlan::from(ast::TableFactor::Series {
            alias: alias("series"),
            size: Expr::Literal(Literal::Number(3.into())),
        });
        let expected = SourcePlan::Series(SeriesSourcePlan {
            alias: TableAliasPlan {
                name: "series".to_owned(),
                columns: Vec::new(),
            },
            size: ExprPlan::Literal(Literal::Number(3.into())),
        });
        assert_eq!(actual, expected);

        let actual = SourcePlan::from(ast::TableFactor::Dictionary {
            dict: Dictionary::GlueTables,
            alias: alias("tables"),
        });
        let expected = SourcePlan::Dictionary(DictionarySourcePlan {
            dictionary: Dictionary::GlueTables,
            alias: TableAliasPlan {
                name: "tables".to_owned(),
                columns: Vec::new(),
            },
        });
        assert_eq!(actual, expected);
    }
}