surrealdb_core/sql/statements/alter/
table.rs1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::iam::{Action, ResourceKind};
6use crate::sql::fmt::{is_pretty, pretty_indent};
7use crate::sql::statements::DefineTableStatement;
8use crate::sql::{changefeed::ChangeFeed, Base, Ident, Permissions, Strand, Value};
9use crate::sql::{Kind, TableType};
10
11use reblessive::tree::Stk;
12use revision::revisioned;
13use serde::{Deserialize, Serialize};
14use std::fmt::{self, Display, Write};
15use std::ops::Deref;
16
17#[revisioned(revision = 1)]
18#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
19#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
20#[non_exhaustive]
21pub struct AlterTableStatement {
22 pub name: Ident,
23 pub if_exists: bool,
24 pub drop: Option<bool>,
25 pub full: Option<bool>,
26 pub permissions: Option<Permissions>,
27 pub changefeed: Option<Option<ChangeFeed>>,
28 pub comment: Option<Option<Strand>>,
29 pub kind: Option<TableType>,
30}
31
32impl AlterTableStatement {
33 pub(crate) async fn compute(
34 &self,
35 _stk: &mut Stk,
36 ctx: &Context,
37 opt: &Options,
38 _doc: Option<&CursorDoc>,
39 ) -> Result<Value, Error> {
40 opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?;
42 let (ns, db) = opt.ns_db()?;
44 let txn = ctx.tx();
46 let mut dt = match txn.get_tb(ns, db, &self.name).await {
48 Ok(tb) => tb.deref().clone(),
49 Err(Error::TbNotFound {
50 ..
51 }) if self.if_exists => return Ok(Value::None),
52 Err(v) => return Err(v),
53 };
54 let key = crate::key::database::tb::new(ns, db, &self.name);
56 if let Some(ref drop) = &self.drop {
57 dt.drop = *drop;
58 }
59 if let Some(ref full) = &self.full {
60 dt.full = *full;
61 }
62 if let Some(ref permissions) = &self.permissions {
63 dt.permissions = permissions.clone();
64 }
65 if let Some(ref changefeed) = &self.changefeed {
66 dt.changefeed = *changefeed;
67 }
68 if let Some(ref comment) = &self.comment {
69 dt.comment.clone_from(comment);
70 }
71 if let Some(ref kind) = &self.kind {
72 dt.kind = kind.clone();
73 }
74
75 if matches!(self.kind, Some(TableType::Relation(_))) {
77 DefineTableStatement::add_in_out_fields(&txn, ns, db, &mut dt).await?;
78 }
79 txn.set(key, revision::to_vec(&dt)?, None).await?;
81 if self.changefeed.is_some() && dt.changefeed.is_some() {
83 txn.lock().await.record_table_change(ns, db, &self.name, &dt);
84 }
85 txn.clear();
87 Ok(Value::None)
89 }
90}
91
92impl Display for AlterTableStatement {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 write!(f, "ALTER TABLE")?;
95 if self.if_exists {
96 write!(f, " IF EXISTS")?
97 }
98 write!(f, " {}", self.name)?;
99 if let Some(kind) = &self.kind {
100 write!(f, " TYPE")?;
101 match &kind {
102 TableType::Normal => {
103 f.write_str(" NORMAL")?;
104 }
105 TableType::Relation(rel) => {
106 f.write_str(" RELATION")?;
107 if let Some(Kind::Record(kind)) = &rel.from {
108 write!(
109 f,
110 " IN {}",
111 kind.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
112 )?;
113 }
114 if let Some(Kind::Record(kind)) = &rel.to {
115 write!(
116 f,
117 " OUT {}",
118 kind.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
119 )?;
120 }
121 }
122 TableType::Any => {
123 f.write_str(" ANY")?;
124 }
125 }
126 }
127 if let Some(drop) = self.drop {
128 write!(f, " DROP {drop}")?;
129 }
130 if let Some(full) = self.full {
131 f.write_str(if full {
132 " SCHEMAFULL"
133 } else {
134 " SCHEMALESS"
135 })?;
136 }
137 if let Some(comment) = &self.comment {
138 write!(f, " COMMENT {}", comment.clone().unwrap_or("NONE".into()))?
139 }
140 if let Some(changefeed) = &self.changefeed {
141 write!(f, " CHANGEFEED {}", changefeed.map_or("NONE".into(), |v| v.to_string()))?
142 }
143 let _indent = if is_pretty() {
144 Some(pretty_indent())
145 } else {
146 f.write_char(' ')?;
147 None
148 };
149 if let Some(permissions) = &self.permissions {
150 write!(f, "{permissions}")?;
151 }
152 Ok(())
153 }
154}