use std::borrow::Cow;
use anyhow::Result;
use reblessive::tree::Stk;
use surrealdb_types::{SqlFormat, ToSql};
use crate::catalog::{Permission, TableDefinition};
use crate::ctx::{Context, FrozenContext};
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::expr::cond::Cond;
use crate::expr::data::Data;
use crate::expr::fetch::Fetchs;
use crate::expr::field::Fields;
use crate::expr::group::Groups;
use crate::expr::limit::Limit;
use crate::expr::order::Ordering;
use crate::expr::output::Output;
use crate::expr::parameterize::exprs_to_fields;
use crate::expr::split::Splits;
use crate::expr::start::Start;
use crate::expr::statements::LiveFields;
use crate::expr::statements::access::AccessStatement;
use crate::expr::statements::create::CreateStatement;
use crate::expr::statements::delete::DeleteStatement;
use crate::expr::statements::insert::InsertStatement;
use crate::expr::statements::live::LiveStatement;
use crate::expr::statements::relate::RelateStatement;
use crate::expr::statements::select::SelectStatement;
use crate::expr::statements::show::ShowStatement;
use crate::expr::statements::update::UpdateStatement;
use crate::expr::statements::upsert::UpsertStatement;
use crate::expr::{Explain, Expr, FlowResultExt, Idiom, With};
use crate::idx::planner::QueryPlanner;
use crate::val::Duration;
#[derive(Clone, Debug)]
pub(crate) enum Statement<'a> {
Live(&'a LiveStatement),
Show(&'a ShowStatement),
Select {
stmt: &'a SelectStatement,
omit: Vec<Idiom>,
rewritten_cond: Option<Cond>,
},
Create(&'a CreateStatement),
Upsert(&'a UpsertStatement),
Update(&'a UpdateStatement),
Relate(&'a RelateStatement),
Delete(&'a DeleteStatement),
Insert(&'a InsertStatement),
Access(&'a AccessStatement),
}
impl<'a> From<&'a LiveStatement> for Statement<'a> {
fn from(v: &'a LiveStatement) -> Self {
Statement::Live(v)
}
}
impl<'a> From<&'a ShowStatement> for Statement<'a> {
fn from(v: &'a ShowStatement) -> Self {
Statement::Show(v)
}
}
impl<'a> From<&'a CreateStatement> for Statement<'a> {
fn from(v: &'a CreateStatement) -> Self {
Statement::Create(v)
}
}
impl<'a> From<&'a UpsertStatement> for Statement<'a> {
fn from(v: &'a UpsertStatement) -> Self {
Statement::Upsert(v)
}
}
impl<'a> From<&'a UpdateStatement> for Statement<'a> {
fn from(v: &'a UpdateStatement) -> Self {
Statement::Update(v)
}
}
impl<'a> From<&'a RelateStatement> for Statement<'a> {
fn from(v: &'a RelateStatement) -> Self {
Statement::Relate(v)
}
}
impl<'a> From<&'a DeleteStatement> for Statement<'a> {
fn from(v: &'a DeleteStatement) -> Self {
Statement::Delete(v)
}
}
impl<'a> From<&'a InsertStatement> for Statement<'a> {
fn from(v: &'a InsertStatement) -> Self {
Statement::Insert(v)
}
}
impl<'a> From<&'a AccessStatement> for Statement<'a> {
fn from(v: &'a AccessStatement) -> Self {
Statement::Access(v)
}
}
impl ToSql for Statement<'_> {
fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
match self {
Statement::Live(v) => {
let sql_stmt: crate::sql::statements::LiveStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Show(v) => {
let sql_stmt: crate::sql::statements::ShowStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Select {
stmt,
..
} => {
let sql_stmt: crate::sql::statements::SelectStatement = (*stmt).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Create(v) => {
let sql_stmt: crate::sql::statements::CreateStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Upsert(v) => {
let sql_stmt: crate::sql::statements::UpsertStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Update(v) => {
let sql_stmt: crate::sql::statements::UpdateStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Relate(v) => {
let sql_stmt: crate::sql::statements::RelateStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Delete(v) => {
let sql_stmt: crate::sql::statements::DeleteStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Insert(v) => {
let sql_stmt: crate::sql::statements::InsertStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
Statement::Access(v) => {
let sql_stmt: crate::sql::statements::AccessStatement = (*v).clone().into();
sql_stmt.fmt_sql(f, fmt);
}
}
}
}
impl Statement<'_> {
pub(crate) fn is_select(&self) -> bool {
matches!(self, Statement::Select { .. })
}
pub(crate) fn is_create(&self) -> bool {
matches!(self, Statement::Create(_))
}
pub(crate) fn is_delete(&self) -> bool {
matches!(self, Statement::Delete(_))
}
pub(crate) fn is_mutation(&self) -> bool {
matches!(
self,
Statement::Create(_)
| Statement::Upsert(_)
| Statement::Update(_)
| Statement::Relate(_)
| Statement::Delete(_)
| Statement::Insert(_)
)
}
pub(crate) fn is_deferable(&self) -> bool {
match self {
Statement::Upsert(v) if v.cond.is_none() => true,
Statement::Create(_) => true,
_ => false,
}
}
pub(crate) fn is_repeatable(&self) -> bool {
match self {
Statement::Upsert(v) if v.cond.is_none() => match v.data {
Some(Data::ContentExpression(_)) => false,
Some(Data::ReplaceExpression(_)) => false,
Some(_) => true,
None => false,
},
_ => false,
}
}
pub(crate) fn requires_table_existence(&self) -> bool {
match self {
Statement::Live(_)
| Statement::Show(_)
| Statement::Select {
..
}
| Statement::Update {
..
}
| Statement::Access(_)
| Statement::Delete(_) => true,
Statement::Create(_)
| Statement::Upsert(_)
| Statement::Relate(_)
| Statement::Insert(_) => false,
}
}
pub(crate) fn is_guaranteed(&self) -> bool {
matches!(self, Statement::Upsert(v) if v.cond.is_some())
}
pub(crate) fn expr(&self) -> Option<&Fields> {
match self {
Statement::Select {
stmt,
..
} => Some(&stmt.fields),
Statement::Live(v) => match &v.fields {
LiveFields::Diff => None,
LiveFields::Select(x) => Some(x),
},
_ => None,
}
}
pub(crate) fn data(&self) -> Option<&Data> {
match self {
Statement::Create(v) => v.data.as_ref(),
Statement::Upsert(v) => v.data.as_ref(),
Statement::Update(v) => v.data.as_ref(),
Statement::Relate(v) => v.data.as_ref(),
Statement::Insert(v) => v.update.as_ref(),
_ => None,
}
}
pub(crate) fn cond(&self) -> Option<&Cond> {
match self {
Statement::Live(v) => v.cond.as_ref(),
Statement::Select {
stmt,
rewritten_cond,
..
} => rewritten_cond.as_ref().or(stmt.cond.as_ref()),
Statement::Upsert(v) => v.cond.as_ref(),
Statement::Update(v) => v.cond.as_ref(),
Statement::Delete(v) => v.cond.as_ref(),
_ => None,
}
}
pub(crate) fn split(&self) -> Option<&Splits> {
match self {
Statement::Select {
stmt,
..
} => stmt.split.as_ref(),
_ => None,
}
}
pub(crate) fn group(&self) -> Option<&Groups> {
match self {
Statement::Select {
stmt,
..
} => stmt.group.as_ref(),
_ => None,
}
}
pub(crate) fn order(&self) -> Option<&Ordering> {
match self {
Statement::Select {
stmt,
..
} => stmt.order.as_ref(),
_ => None,
}
}
pub(crate) fn with(&self) -> Option<&With> {
match self {
Statement::Select {
stmt,
..
} => stmt.with.as_ref(),
Statement::Update(s) => s.with.as_ref(),
Statement::Upsert(s) => s.with.as_ref(),
Statement::Delete(s) => s.with.as_ref(),
_ => None,
}
}
pub(crate) fn fetch(&self) -> Option<&Fetchs> {
match self {
Statement::Select {
stmt,
..
} => stmt.fetch.as_ref(),
_ => None,
}
}
pub(crate) fn start(&self) -> Option<&Start> {
match self {
Statement::Select {
stmt,
..
} => stmt.start.as_ref(),
_ => None,
}
}
pub(crate) fn limit(&self) -> Option<&Limit> {
match self {
Statement::Select {
stmt,
..
} => stmt.limit.as_ref(),
_ => None,
}
}
pub(crate) fn update(&self) -> Option<&Data> {
match self {
Statement::Insert(v) => v.update.as_ref(),
_ => None,
}
}
pub(crate) fn omit(&self) -> &[Idiom] {
match self {
Statement::Select {
omit,
..
} => omit.as_slice(),
_ => &[],
}
}
pub(crate) fn is_only(&self) -> bool {
match self {
Statement::Create(v) => v.only,
Statement::Delete(v) => v.only,
Statement::Relate(v) => v.only,
Statement::Upsert(v) => v.only,
Statement::Update(v) => v.only,
Statement::Select {
stmt,
..
} => stmt.only,
_ => false,
}
}
pub(crate) fn is_ignore(&self) -> bool {
match self {
Statement::Insert(v) => v.ignore,
_ => false,
}
}
pub(crate) fn output(&self) -> Option<&Output> {
match self {
Statement::Create(v) => v.output.as_ref(),
Statement::Upsert(v) => v.output.as_ref(),
Statement::Update(v) => v.output.as_ref(),
Statement::Relate(v) => v.output.as_ref(),
Statement::Delete(v) => v.output.as_ref(),
Statement::Insert(v) => v.output.as_ref(),
_ => None,
}
}
#[cfg(storage)]
pub(crate) fn tempfiles(&self) -> bool {
match self {
Statement::Select {
stmt,
..
} => stmt.tempfiles,
_ => false,
}
}
pub(crate) fn explain(&self) -> Option<&Explain> {
match self {
Statement::Select {
stmt,
..
} => stmt.explain.as_ref(),
Statement::Update(s) => s.explain.as_ref(),
Statement::Upsert(s) => s.explain.as_ref(),
Statement::Delete(s) => s.explain.as_ref(),
_ => None,
}
}
pub(crate) fn permissions<'b>(
&self,
table: &'b TableDefinition,
doc_is_new: bool,
) -> &'b Permission {
if self.is_delete() {
&table.permissions.delete
} else if self.is_select() {
&table.permissions.select
} else if doc_is_new {
&table.permissions.create
} else {
&table.permissions.update
}
}
pub(crate) fn timeout(&self) -> Option<&Expr> {
match self {
Statement::Create(s) => Some(&s.timeout),
Statement::Delete(s) => Some(&s.timeout),
Statement::Insert(s) => Some(&s.timeout),
Statement::Select {
stmt,
..
} => Some(&stmt.timeout),
Statement::Update(s) => Some(&s.timeout),
Statement::Upsert(s) => Some(&s.timeout),
_ => None,
}
}
pub(crate) async fn setup_timeout<'a>(
&self,
stk: &mut Stk,
ctx: &'a FrozenContext,
opt: &Options,
doc: Option<&CursorDoc>,
) -> Result<Cow<'a, FrozenContext>> {
if let Some(t) = self.timeout() {
let Some(x) = stk
.run(|stk| t.compute(stk, ctx, opt, doc))
.await
.catch_return()?
.cast_to::<Option<Duration>>()?
else {
return Ok(Cow::Borrowed(ctx));
};
let mut ctx = Context::new_child(ctx);
ctx.add_timeout(x.0)?;
Ok(Cow::Owned(ctx.freeze()))
} else {
Ok(Cow::Borrowed(ctx))
}
}
pub(crate) fn setup_query_planner<'a>(
&self,
planner: QueryPlanner,
ctx: Cow<'a, FrozenContext>,
) -> Cow<'a, FrozenContext> {
if planner.has_executors() {
let mut ctx = Context::new_child(&ctx);
ctx.set_query_planner(planner);
Cow::Owned(ctx.freeze())
} else {
ctx
}
}
pub(crate) async fn from_select<'a>(
stk: &mut Stk,
ctx: &FrozenContext,
opt: &Options,
doc: Option<&CursorDoc>,
stmt: &'a SelectStatement,
) -> Result<Statement<'a>> {
use crate::expr::visit::MutVisitor;
use crate::idx::planner::count_exists_rewriter::CountLimitRewriter;
let omit = exprs_to_fields(stk, ctx, opt, doc, stmt.omit.as_slice()).await?;
let rewritten_cond = if let Some(cond) = &stmt.cond {
let mut cond_expr = cond.0.clone();
if CountLimitRewriter.visit_mut_expr(&mut cond_expr).is_ok() && cond_expr != cond.0 {
Some(Cond(cond_expr))
} else {
None
}
} else {
None
};
Ok(Statement::Select {
stmt,
omit,
rewritten_cond,
})
}
}