use std::sync::Arc;
use crate::parser::ast::Stmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatementClass {
Analyze,
ReadOnly,
Dml,
Ddl,
Transaction,
Diagnostic,
}
pub fn requires_write_storage(stmt: &Stmt) -> bool {
match stmt {
Stmt::Match(m) => m.delete_clause.is_some(),
Stmt::Pipe(pipe) => {
requires_write_storage(&pipe.left) || requires_write_storage(&pipe.right)
}
Stmt::SetOperation(set_op) => {
requires_write_storage(&set_op.left) || requires_write_storage(&set_op.right)
}
_ => requires_auto_commit(stmt),
}
}
pub fn classify_statement(stmt: &Stmt) -> StatementClass {
if is_diagnostic(stmt) {
StatementClass::Diagnostic
} else if is_analyze(stmt) {
StatementClass::Analyze
} else if is_transaction(stmt) {
StatementClass::Transaction
} else if is_ddl(stmt) {
StatementClass::Ddl
} else if requires_write_storage(stmt) {
StatementClass::Dml
} else {
StatementClass::ReadOnly
}
}
pub fn is_analyze(stmt: &Stmt) -> bool {
matches!(stmt, Stmt::Analyze(_))
}
pub fn requires_auto_commit(stmt: &Stmt) -> bool {
match stmt {
Stmt::Pipe(pipe) => requires_auto_commit(&pipe.left) || requires_auto_commit(&pipe.right),
Stmt::SetOperation(set_op) => {
requires_auto_commit(&set_op.left) || requires_auto_commit(&set_op.right)
}
_ => is_direct_write_statement(stmt),
}
}
pub(crate) fn is_direct_write_statement(stmt: &Stmt) -> bool {
is_direct_dml_statement(stmt) || is_direct_dcl(stmt)
}
pub fn is_direct_dml_statement(stmt: &Stmt) -> bool {
matches!(
stmt,
Stmt::Insert(_)
| Stmt::Copy(_)
| Stmt::Delete(_)
| Stmt::Update(_)
| Stmt::Merge(_)
| Stmt::Set(_)
| Stmt::Remove(_)
)
}
pub(crate) fn is_direct_dcl(stmt: &Stmt) -> bool {
matches!(
stmt,
Stmt::CreateUser(_)
| Stmt::AlterUser(_)
| Stmt::DropUser(_)
| Stmt::ChangePassword(_)
| Stmt::Grant(_)
| Stmt::Revoke(_)
| Stmt::UpdateConfigs(_)
)
}
pub fn is_transaction(stmt: &Stmt) -> bool {
matches!(
stmt,
Stmt::BeginTransaction(..)
| Stmt::CommitTransaction(..)
| Stmt::RollbackTransaction(..)
| Stmt::Savepoint(..)
| Stmt::ReleaseSavepoint(..)
)
}
pub fn is_ddl(stmt: &Stmt) -> bool {
matches!(
stmt,
Stmt::Create(_)
| Stmt::Drop(_)
| Stmt::Alter(_)
| Stmt::ClearSpace(_)
| Stmt::CreateFulltextIndex(_)
| Stmt::DropFulltextIndex(_)
| Stmt::AlterFulltextIndex(_)
| Stmt::CreateVectorIndex(_)
| Stmt::DropVectorIndex(_)
| Stmt::CreateMacro(_)
| Stmt::DropMacro(_)
| Stmt::CreateType(_)
| Stmt::DropType(_)
| Stmt::PurgeExpired(_)
)
}
pub fn is_diagnostic(stmt: &Stmt) -> bool {
matches!(stmt, Stmt::Explain(_) | Stmt::Profile(_))
}
pub fn is_read_only_cacheable(stmt: &Stmt) -> bool {
match stmt {
Stmt::Pipe(pipe) => {
is_read_only_cacheable(&pipe.left) && is_read_only_cacheable(&pipe.right)
}
Stmt::SetOperation(set_op) => {
is_read_only_cacheable(&set_op.left) && is_read_only_cacheable(&set_op.right)
}
_ => !matches!(
stmt,
Stmt::Insert(_)
| Stmt::Copy(_)
| Stmt::Update(_)
| Stmt::Delete(_)
| Stmt::Set(_)
| Stmt::Remove(_)
| Stmt::Merge(_)
| Stmt::Create(_)
| Stmt::Drop(_)
| Stmt::Alter(_)
| Stmt::ClearSpace(_)
| Stmt::CreateFulltextIndex(_)
| Stmt::DropFulltextIndex(_)
| Stmt::AlterFulltextIndex(_)
| Stmt::CreateVectorIndex(_)
| Stmt::DropVectorIndex(_)
| Stmt::CreateMacro(_)
| Stmt::DropMacro(_)
| Stmt::CreateType(_)
| Stmt::DropType(_)
| Stmt::CreateUser(_)
| Stmt::AlterUser(_)
| Stmt::DropUser(_)
| Stmt::ChangePassword(_)
| Stmt::Grant(_)
| Stmt::Revoke(_)
| Stmt::UpdateConfigs(_)
| Stmt::BeginTransaction(_)
| Stmt::CommitTransaction(_)
| Stmt::RollbackTransaction(_)
| Stmt::Explain(_)
| Stmt::Profile(_)
| Stmt::Analyze(_)
| Stmt::Checkpoint(_)
| Stmt::ExportDatabase(_)
| Stmt::ImportDatabase(_)
| Stmt::AttachDatabase(_)
| Stmt::DetachDatabase(_)
| Stmt::PurgeExpired(_)
),
}
}
pub fn build_validated_fallback(
ast: &Arc<crate::parser::ast::stmt::Ast>,
) -> crate::binder::validation::ValidatedStatement {
crate::binder::validation::ValidatedStatement::new(
ast.clone(),
crate::binder::validation::ValidationInfo::new(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::ast::stmt::{CheckpointStmt, PurgeExpiredStmt, PurgeTarget};
use linkrs_core::types::Span;
#[test]
fn purge_expired_is_not_read_only_cacheable() {
let stmt = Stmt::PurgeExpired(PurgeExpiredStmt {
span: Span::default(),
space_name: "s".to_string(),
target: Some(PurgeTarget::Tag("t".to_string())),
});
assert!(!is_read_only_cacheable(&stmt));
}
#[test]
fn checkpoint_is_not_read_only_cacheable() {
let stmt = Stmt::Checkpoint(CheckpointStmt {
span: Span::default(),
});
assert!(!is_read_only_cacheable(&stmt));
}
}