use std::borrow::Cow;
use std::ops::Deref;
use anyhow::Result;
use reblessive::tree::Stk;
use tracing::instrument;
use crate::catalog::providers::TableProvider;
use crate::catalog::{Error, TableType};
use crate::ctx::FrozenContext;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::expr::Base;
use crate::expr::statements::alter::AlterKind;
use crate::expr::statements::alter::table::AlterTableStatement;
use crate::iam::{Action, ResourceKind};
use crate::key::schema::{EdgeCachePrefix, RefCachePrefix, TblRoot};
use crate::legacy::expr_to_ident;
use crate::val::{TableName, Value};
#[instrument(level = "trace", name = "AlterTableStatement::compute", skip_all)]
pub(crate) async fn alter_table_statement_compute(
this: &AlterTableStatement,
stk: &mut Stk,
ctx: &FrozenContext,
opt: &Options,
doc: Option<&CursorDoc>,
) -> Result<Value> {
ctx.is_allowed(opt, Action::Edit, ResourceKind::Table, Base::Db)?;
let name = TableName::new(expr_to_ident(stk, ctx, opt, doc, &this.name, "table name").await?);
let (ns_name, db_name) = opt.ns_db()?;
let (ns, db) = ctx.expect_ns_db_ids(opt).await?;
let txn = ctx.tx();
let previous = match txn.get_tb(ns, db, &name, None).await? {
Some(tb) => tb,
None => {
if this.if_exists {
return Ok(Value::None);
} else {
return Err(Error::TbNotFound {
name: name.clone(),
}
.into());
}
}
};
let mut dt = previous.deref().clone();
match this.schemafull {
AlterKind::Set(_) => dt.schemafull = true,
AlterKind::Drop => dt.schemafull = false,
AlterKind::None => {}
}
if let Some(permissions) = &this.permissions {
dt.permissions = permissions.clone();
}
let mut changefeed_replaced = false;
let mut changefeed_set = false;
match this.changefeed {
AlterKind::Set(x) => {
changefeed_replaced = dt.changefeed.is_some();
changefeed_set = true;
dt.changefeed = Some(x)
}
AlterKind::Drop => dt.changefeed = None,
AlterKind::None => {}
}
match this.comment {
AlterKind::Set(ref x) => dt.comment = Some(x.clone()),
AlterKind::Drop => dt.comment = None,
AlterKind::None => {}
}
let old_edges_cap = dt.inline_edges_cap;
let old_refs_cap = dt.inline_refs_cap;
for (kind, cap) in [
(&this.inline_edges_cap, &mut dt.inline_edges_cap),
(&this.inline_refs_cap, &mut dt.inline_refs_cap),
] {
match kind {
AlterKind::Set(x) => {
anyhow::ensure!(
*x <= crate::idx::inline_cache::MAX_INLINE_CACHE_CAP,
crate::exec::Error::Thrown(format!(
"an INLINE cache cap cannot exceed {}",
crate::idx::inline_cache::MAX_INLINE_CACHE_CAP
))
);
*cap = Some(*x);
}
AlterKind::Drop => *cap = None,
AlterKind::None => {}
}
}
if let Some(kind) = &this.kind {
dt.table_type = kind.clone();
}
crate::legacy::expr::statements::define::table::validate_lightweight_table(
&txn,
ns,
db,
Some(previous.deref()),
&mut dt,
)
.await?;
crate::legacy::expr::statements::define::table::validate_inline_fields_survive_type_change(
&txn,
ns,
db,
Some(previous.deref()),
&dt,
)
.await?;
if matches!(this.kind, Some(TableType::Relation(_))) {
crate::legacy::define_table_statement_add_in_out_fields(&txn, ns, db, &mut dt).await?;
}
if changefeed_replaced {
txn.changefeed_buffer_table_change(ns, db, &name, &dt.to_stored());
}
if this.compact {
let key = TblRoot {
ns,
db,
tb: Cow::Borrowed(&name),
};
txn.compact(&key).await?;
}
txn.replace_tb(ns_name, db_name, &dt).await?;
if changefeed_set {
txn.bump_changefeed_retention_fence(ns, db).await?;
}
if old_edges_cap != dt.inline_edges_cap {
let key = EdgeCachePrefix {
ns,
db,
tb: Cow::Borrowed(&name),
};
txn.del_prefix_key(&key).await?;
}
if old_refs_cap != dt.inline_refs_cap {
let key = RefCachePrefix {
ns,
db,
tb: Cow::Borrowed(&name),
};
txn.del_prefix_key(&key).await?;
}
txn.clear_cache();
Ok(Value::None)
}