#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub mod optimizer;
pub use optimizer::expr_util::{
canonical_hash, collect_colrefs, equi_key, find_common_subexprs, join_conjuncts, parse_expr,
parse_predicate, render, split_conjuncts, ColRef,
};
pub use optimizer::{
CommonSubexprElimination, ConstantFolding, JoinAlgoHint, JoinAlgorithmPass, LimitPushThrough,
OptPass, Optimizer, PredicatePushdown, PredicateSimplification, ProjectionPruning,
};
pub mod decorrelate;
pub use decorrelate::PlannerOptions;
pub mod parameterize;
pub use parameterize::{parameterize, ParameterizedSql};
pub mod plan_cache;
pub use plan_cache::PlanCache;
pub mod agg;
pub use agg::{extract_aggregates, is_aggregate_expr, AggFunc, AggregateExpr};
pub mod dml;
pub use dml::{plan_dml, DmlPlan};
pub mod validate;
pub use validate::{SchemaValidator, ValidationError};
pub mod cost;
pub use cost::{ColumnStats, CostEstimate, CostModel, NodeCost, TableStats};
pub mod builder;
mod columns;
mod setops;
mod window;
pub use builder::QueryBuilder;
pub mod plan;
pub use plan::{JoinType, LogicalPlan, SetOpType, SortExpr, WindowFunctionDef};
pub mod planner;
pub use planner::{plan_query, plan_statement, plan_statement_with_opts};
pub mod explain;
pub use explain::{explain, explain_json, explain_verbose};
pub mod analysis;
pub use analysis::{count_params, extract_columns, extract_tables, is_read_only, normalize};
pub mod cache;
pub use cache::ParseCache;
use oxisql_core::OxiSqlError;
pub use sqlparser::ast::{Expr, JoinConstraint, ObjectName, SelectItem, Statement, TableFactor};
use sqlparser::dialect::Dialect;
pub use sqlparser::dialect::{GenericDialect, MySqlDialect, PostgreSqlDialect, SQLiteDialect};
use sqlparser::parser::Parser;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SqlDialect {
Generic,
Postgres,
MySQL,
SQLite,
}
impl SqlDialect {
pub(crate) fn to_sqlparser(self) -> Box<dyn Dialect> {
match self {
SqlDialect::Generic => Box::new(GenericDialect {}),
SqlDialect::Postgres => Box::new(PostgreSqlDialect {}),
SqlDialect::MySQL => Box::new(MySqlDialect {}),
SqlDialect::SQLite => Box::new(SQLiteDialect {}),
}
}
}
pub fn optimize(plan: LogicalPlan) -> LogicalPlan {
optimizer::optimize(plan)
}
pub fn parse_postgres(sql: &str) -> Result<Vec<Statement>, OxiSqlError> {
parse_with_dialect(sql, SqlDialect::Postgres)
}
pub fn parse_mysql(sql: &str) -> Result<Vec<Statement>, OxiSqlError> {
parse_with_dialect(sql, SqlDialect::MySQL)
}
pub fn parse_sqlite(sql: &str) -> Result<Vec<Statement>, OxiSqlError> {
parse_with_dialect(sql, SqlDialect::SQLite)
}
pub fn parse(sql: &str) -> Result<Vec<Statement>, OxiSqlError> {
let dialect = GenericDialect {};
Parser::parse_sql(&dialect, sql).map_err(|e| OxiSqlError::Parse(e.to_string()))
}
pub fn parse_with_dialect(sql: &str, dialect: SqlDialect) -> Result<Vec<Statement>, OxiSqlError> {
let d = dialect.to_sqlparser();
Parser::parse_sql(d.as_ref(), sql).map_err(|e| OxiSqlError::Parse(e.to_string()))
}
pub fn parse_one(sql: &str) -> Result<Statement, OxiSqlError> {
let mut stmts = parse(sql)?;
if stmts.len() != 1 {
return Err(OxiSqlError::Parse(format!(
"expected exactly 1 statement, got {}",
stmts.len()
)));
}
Ok(stmts.remove(0))
}
pub fn parse_one_with_dialect(sql: &str, dialect: SqlDialect) -> Result<Statement, OxiSqlError> {
let mut stmts = parse_with_dialect(sql, dialect)?;
if stmts.len() != 1 {
return Err(OxiSqlError::Parse(format!(
"expected exactly 1 statement, got {}",
stmts.len()
)));
}
Ok(stmts.remove(0))
}
pub fn format(stmt: &Statement) -> String {
stmt.to_string()
}
pub fn plan_query_with(sql: &str, opts: &PlannerOptions) -> Result<LogicalPlan, OxiSqlError> {
let stmt = parse_one(sql)?;
plan_statement_with_opts(&stmt, opts)
}
#[cfg(test)]
mod tests;