mod column_operation_extractor;
mod crud_table_extractor;
mod table_operation_extractor;
pub use column_operation_extractor::*;
pub use crud_table_extractor::*;
pub use table_operation_extractor::*;
use crate::casing::{IdentifierCasing, IdentifierStyle};
use crate::catalog::Catalog;
use crate::error::Error;
use sqlparser::ast::Statement;
use sqlparser::dialect::Dialect;
use sqlparser::parser::Parser;
#[derive(Default, Clone, Debug)]
pub struct ExtractorOptions<'a> {
pub catalog: Option<&'a Catalog>,
pub casing: Option<IdentifierCasing>,
}
impl<'a> ExtractorOptions<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn with_catalog(mut self, catalog: &'a Catalog) -> Self {
self.catalog = Some(catalog);
self
}
pub fn with_casing(mut self, casing: IdentifierCasing) -> Self {
self.casing = Some(casing);
self
}
fn casing_for(&self, dialect: &dyn Dialect) -> IdentifierCasing {
self.casing
.unwrap_or_else(|| IdentifierCasing::for_dialect(dialect))
}
pub(crate) fn identifier_style(&self, dialect: &dyn Dialect) -> IdentifierStyle {
IdentifierStyle {
casing: self.casing_for(dialect),
quote: crate::casing::canonical_quote(dialect),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum StatementKind {
Select,
Insert,
Update,
Delete,
Merge,
CreateTable,
CreateView,
AlterTable,
AlterView,
Drop,
Truncate,
Unsupported,
}
pub(crate) fn unsupported_message(statement: &Statement) -> String {
format!("Unsupported statement: {statement}")
}
pub(crate) fn extract_each<T, F>(
dialect: &dyn Dialect,
sql: &str,
options: ExtractorOptions,
extract_from: F,
) -> Result<Vec<Result<T, Error>>, Error>
where
F: Fn(&Statement, Option<&Catalog>, IdentifierStyle) -> Result<T, Error>,
{
let statements = Parser::parse_sql(dialect, sql)?;
let style = options.identifier_style(dialect);
Ok(statements
.iter()
.map(|s| extract_from(s, options.catalog, style))
.collect())
}
pub(crate) fn classify_statement(statement: &Statement) -> StatementKind {
use sqlparser::ast::ObjectType;
match statement {
Statement::Query(query) => classify_query_body(query.body.as_ref()),
Statement::Insert(_) => StatementKind::Insert,
Statement::Update(_) => StatementKind::Update,
Statement::Delete(_) => StatementKind::Delete,
Statement::Merge(_) => StatementKind::Merge,
Statement::CreateTable(_) | Statement::CreateVirtualTable { .. } => {
StatementKind::CreateTable
}
Statement::CreateView(_) => StatementKind::CreateView,
Statement::AlterTable(_) => StatementKind::AlterTable,
Statement::AlterView { .. } => StatementKind::AlterView,
Statement::Drop {
object_type: ObjectType::Table | ObjectType::View | ObjectType::MaterializedView,
..
} => StatementKind::Drop,
Statement::Truncate(_) => StatementKind::Truncate,
_ => StatementKind::Unsupported,
}
}
fn classify_query_body(body: &sqlparser::ast::SetExpr) -> StatementKind {
use sqlparser::ast::SetExpr;
match body {
SetExpr::Insert(stmt)
| SetExpr::Update(stmt)
| SetExpr::Delete(stmt)
| SetExpr::Merge(stmt) => classify_statement(stmt),
SetExpr::Query(inner) => classify_query_body(inner.body.as_ref()),
body if has_leading_select_into(body) => StatementKind::CreateTable,
SetExpr::Select(_)
| SetExpr::SetOperation { .. }
| SetExpr::Values(_)
| SetExpr::Table(_) => StatementKind::Select,
}
}
fn has_leading_select_into(body: &sqlparser::ast::SetExpr) -> bool {
use sqlparser::ast::SetExpr;
match body {
SetExpr::Select(select) => select.into.is_some(),
SetExpr::Query(query) => has_leading_select_into(&query.body),
SetExpr::SetOperation { left, .. } => has_leading_select_into(left),
SetExpr::Values(_)
| SetExpr::Insert(_)
| SetExpr::Update(_)
| SetExpr::Delete(_)
| SetExpr::Merge(_)
| SetExpr::Table(_) => false,
}
}