use std::borrow::Cow;
use std::sync::Arc;
use anyhow::Result;
use reblessive::tree::Stk;
use surrealdb_types::{SqlFormat, ToSql};
use crate::catalog::providers::{DatabaseProvider, NamespaceProvider};
use crate::ctx::FrozenContext;
use crate::dbs::{Iterator, Options, Statement};
use crate::doc::{CursorDoc, NsDbCtx};
use crate::err::Error;
use crate::expr::{Cond, Data, Explain, Expr, Literal, Output, With};
use crate::idx::planner::{QueryPlanner, RecordStrategy, StatementContext};
use crate::val::Value;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct UpdateStatement {
pub only: bool,
pub what: Vec<Expr>,
pub with: Option<With>,
pub data: Option<Data>,
pub cond: Option<Cond>,
pub output: Option<Output>,
pub timeout: Expr,
pub explain: Option<Explain>,
}
impl Default for UpdateStatement {
fn default() -> Self {
Self {
only: Default::default(),
what: Default::default(),
with: Default::default(),
data: Default::default(),
cond: Default::default(),
output: Default::default(),
timeout: Expr::Literal(Literal::None),
explain: Default::default(),
}
}
}
impl UpdateStatement {
#[instrument(level = "trace", name = "UpdateStatement::compute", skip_all)]
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &FrozenContext,
opt: &Options,
doc: Option<&CursorDoc>,
) -> Result<Value> {
opt.valid_for_db()?;
let mut iterator = Iterator::new();
let stm = Statement::from(self);
let ctx = stm.setup_timeout(stk, ctx, opt, doc).await?;
let mut planner = QueryPlanner::new();
let stm_ctx = StatementContext::new(&ctx, opt, &stm)?;
let txn = ctx.tx();
let ns = txn.expect_ns_by_name(opt.ns()?).await?;
let db = txn.expect_db_by_name(opt.ns()?, opt.db()?).await?;
let doc_ctx = NsDbCtx {
ns: Arc::clone(&ns),
db: Arc::clone(&db),
};
let prepare_ctx: Cow<'_, FrozenContext> = CursorDoc::with_parent_ctx(&ctx, doc);
for w in self.what.iter() {
iterator
.prepare(stk, prepare_ctx.as_ref(), opt, doc, &mut planner, &stm_ctx, &doc_ctx, w)
.await
.map_err(|e| {
if matches!(e.downcast_ref(), Some(Error::InvalidStatementTarget { .. })) {
let Ok(Error::InvalidStatementTarget {
value,
}) = e.downcast()
else {
unreachable!()
};
anyhow::Error::new(Error::UpdateStatement {
value,
})
} else {
e
}
})?;
}
CursorDoc::update_parent(prepare_ctx.as_ref(), None, async |ctx| {
let ctx = stm.setup_query_planner(planner, ctx);
let res = iterator.output(stk, &ctx, opt, &stm, RecordStrategy::KeysAndValues).await?;
ctx.expect_not_timedout().await?;
match res {
Value::Array(mut a) if self.only => match a.len() {
1 => Ok(a.remove(0)),
0 => Ok(Value::None),
_ => Err(anyhow::Error::new(Error::SingleOnlyOutput)),
},
v => Ok(v),
}
})
.await
}
}
impl ToSql for UpdateStatement {
fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
let stmt: crate::sql::statements::update::UpdateStatement = self.clone().into();
stmt.fmt_sql(f, fmt);
}
}