surrealdb_core/sql/statements/define/
field.rs

1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::iam::{Action, ResourceKind};
6use crate::sql::statements::info::InfoStructure;
7use crate::sql::statements::DefineTableStatement;
8use crate::sql::{
9	fmt::is_pretty, fmt::pretty_indent, Base, Ident, Idiom, Kind, Permissions, Strand, Value,
10};
11use crate::sql::{Object, Part};
12use crate::sql::{Relation, TableType};
13use derive::Store;
14use revision::revisioned;
15use serde::{Deserialize, Serialize};
16use std::fmt::{self, Display, Write};
17
18#[revisioned(revision = 3)]
19#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
20#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
21#[non_exhaustive]
22pub struct DefineFieldStatement {
23	pub name: Idiom,
24	pub what: Ident,
25	pub flex: bool,
26	pub kind: Option<Kind>,
27	#[revision(start = 2)]
28	pub readonly: bool,
29	pub value: Option<Value>,
30	pub assert: Option<Value>,
31	pub default: Option<Value>,
32	pub permissions: Permissions,
33	pub comment: Option<Strand>,
34	#[revision(start = 3)]
35	pub if_not_exists: bool,
36}
37
38impl DefineFieldStatement {
39	/// Process this type returning a computed simple Value
40	pub(crate) async fn compute(
41		&self,
42		ctx: &Context<'_>,
43		opt: &Options,
44		_doc: Option<&CursorDoc<'_>>,
45	) -> Result<Value, Error> {
46		// Allowed to run?
47		opt.is_allowed(Action::Edit, ResourceKind::Field, &Base::Db)?;
48		// Claim transaction
49		let mut run = ctx.tx_lock().await;
50		// Clear the cache
51		run.clear_cache();
52		// Check if field already exists
53		let fd = self.name.to_string();
54		if run.get_tb_field(opt.ns()?, opt.db()?, &self.what, &fd).await.is_ok() {
55			if self.if_not_exists {
56				return Ok(Value::None);
57			} else {
58				return Err(Error::FdAlreadyExists {
59					value: fd,
60				});
61			}
62		}
63		// Process the statement
64		run.add_ns(opt.ns()?, opt.strict).await?;
65		run.add_db(opt.ns()?, opt.db()?, opt.strict).await?;
66
67		let tb = run.add_tb(opt.ns()?, opt.db()?, &self.what, opt.strict).await?;
68		let key = crate::key::table::fd::new(opt.ns()?, opt.db()?, &self.what, &fd);
69		run.set(
70			key,
71			DefineFieldStatement {
72				if_not_exists: false,
73				..self.clone()
74			},
75		)
76		.await?;
77
78		// find existing field definitions.
79		let fields = run.all_tb_fields(opt.ns()?, opt.db()?, &self.what).await.ok();
80
81		// Process possible recursive_definitions.
82		if let Some(mut cur_kind) = self.kind.as_ref().and_then(|x| x.inner_kind()) {
83			let mut name = self.name.clone();
84			loop {
85				let new_kind = cur_kind.inner_kind();
86				name.0.push(Part::All);
87
88				let fd = name.to_string();
89				let key = crate::key::table::fd::new(opt.ns()?, opt.db()?, &self.what, &fd);
90				run.add_ns(opt.ns()?, opt.strict).await?;
91				run.add_db(opt.ns()?, opt.db()?, opt.strict).await?;
92
93				// merge the new definition with possible existing definitions.
94				let statement = if let Some(existing) =
95					fields.as_ref().and_then(|x| x.iter().find(|x| x.name == name))
96				{
97					DefineFieldStatement {
98						kind: Some(cur_kind),
99						if_not_exists: false,
100						..existing.clone()
101					}
102				} else {
103					DefineFieldStatement {
104						name: name.clone(),
105						what: self.what.clone(),
106						flex: self.flex,
107						kind: Some(cur_kind),
108						..Default::default()
109					}
110				};
111
112				run.set(key, statement).await?;
113
114				if let Some(new_kind) = new_kind {
115					cur_kind = new_kind;
116				} else {
117					break;
118				}
119			}
120		}
121
122		let new_tb = match (fd.as_str(), tb.kind.clone(), self.kind.clone()) {
123			("in", TableType::Relation(rel), Some(dk)) => {
124				if !matches!(dk, Kind::Record(_)) {
125					return Err(Error::Thrown("in field on a relation must be a record".into()));
126				};
127				if rel.from.as_ref() != Some(&dk) {
128					Some(DefineTableStatement {
129						kind: TableType::Relation(Relation {
130							from: Some(dk),
131							..rel
132						}),
133						..tb
134					})
135				} else {
136					None
137				}
138			}
139			("out", TableType::Relation(rel), Some(dk)) => {
140				if !matches!(dk, Kind::Record(_)) {
141					return Err(Error::Thrown("out field on a relation must be a record".into()));
142				};
143				if rel.to.as_ref() != Some(&dk) {
144					Some(DefineTableStatement {
145						kind: TableType::Relation(Relation {
146							to: Some(dk),
147							..rel
148						}),
149						..tb
150					})
151				} else {
152					None
153				}
154			}
155			_ => None,
156		};
157		if let Some(tb) = new_tb {
158			let key = crate::key::database::tb::new(opt.ns()?, opt.db()?, &self.what);
159			run.set(key, &tb).await?;
160			let key = crate::key::table::ft::prefix(opt.ns()?, opt.db()?, &self.what);
161			run.clr(key).await?;
162		}
163
164		// Clear the cache
165		let key = crate::key::table::fd::prefix(opt.ns()?, opt.db()?, &self.what);
166		run.clr(key).await?;
167		// Ok all good
168		Ok(Value::None)
169	}
170}
171
172impl Display for DefineFieldStatement {
173	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174		write!(f, "DEFINE FIELD")?;
175		if self.if_not_exists {
176			write!(f, " IF NOT EXISTS")?
177		}
178		write!(f, " {} ON {}", self.name, self.what)?;
179		if self.flex {
180			write!(f, " FLEXIBLE")?
181		}
182		if let Some(ref v) = self.kind {
183			write!(f, " TYPE {v}")?
184		}
185		if let Some(ref v) = self.default {
186			write!(f, " DEFAULT {v}")?
187		}
188		if self.readonly {
189			write!(f, " READONLY")?
190		}
191		if let Some(ref v) = self.value {
192			write!(f, " VALUE {v}")?
193		}
194		if let Some(ref v) = self.assert {
195			write!(f, " ASSERT {v}")?
196		}
197		if let Some(ref v) = self.comment {
198			write!(f, " COMMENT {v}")?
199		}
200		let _indent = if is_pretty() {
201			Some(pretty_indent())
202		} else {
203			f.write_char(' ')?;
204			None
205		};
206		write!(f, "{}", self.permissions)?;
207		Ok(())
208	}
209}
210
211impl InfoStructure for DefineFieldStatement {
212	fn structure(self) -> Value {
213		let Self {
214			name,
215			what,
216			flex,
217			kind,
218			readonly,
219			value,
220			assert,
221			default,
222			permissions,
223			comment,
224			..
225		} = self;
226		let mut acc = Object::default();
227
228		acc.insert("name".to_string(), name.structure());
229
230		acc.insert("what".to_string(), what.structure());
231
232		acc.insert("flex".to_string(), flex.into());
233
234		if let Some(kind) = kind {
235			acc.insert("kind".to_string(), kind.structure());
236		}
237
238		acc.insert("readonly".to_string(), readonly.into());
239
240		if let Some(value) = value {
241			acc.insert("value".to_string(), value.structure());
242		}
243
244		if let Some(assert) = assert {
245			acc.insert("assert".to_string(), assert.structure());
246		}
247
248		if let Some(default) = default {
249			acc.insert("default".to_string(), default.structure());
250		}
251
252		acc.insert("permissions".to_string(), permissions.structure());
253
254		if let Some(comment) = comment {
255			acc.insert("comment".to_string(), comment.into());
256		}
257
258		Value::Object(acc)
259	}
260}