surrealdb_core/sql/statements/define/
model.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::fmt::{is_pretty, pretty_indent};
7use crate::sql::statements::info::InfoStructure;
8use crate::sql::{Base, Ident, Permission, Strand, Value};
9
10use revision::revisioned;
11use serde::{Deserialize, Serialize};
12use std::fmt::{self, Write};
13
14#[revisioned(revision = 3)]
15#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[non_exhaustive]
18pub struct DefineModelStatement {
19	pub hash: String,
20	pub name: Ident,
21	pub version: String,
22	pub comment: Option<Strand>,
23	pub permissions: Permission,
24	#[revision(start = 2)]
25	pub if_not_exists: bool,
26	#[revision(start = 3)]
27	pub overwrite: bool,
28}
29
30impl DefineModelStatement {
31	/// Process this type returning a computed simple Value
32	pub(crate) async fn compute(
33		&self,
34		ctx: &Context,
35		opt: &Options,
36		_doc: Option<&CursorDoc>,
37	) -> Result<Value, Error> {
38		// Allowed to run?
39		opt.is_allowed(Action::Edit, ResourceKind::Model, &Base::Db)?;
40		// Fetch the transaction
41		let txn = ctx.tx();
42		// Check if the definition exists
43		let (ns, db) = opt.ns_db()?;
44		if txn.get_db_model(ns, db, &self.name, &self.version).await.is_ok() {
45			if self.if_not_exists {
46				return Ok(Value::None);
47			} else if !self.overwrite {
48				return Err(Error::MlAlreadyExists {
49					name: self.name.to_string(),
50				});
51			}
52		}
53		// Process the statement
54		let key = crate::key::database::ml::new(ns, db, &self.name, &self.version);
55		txn.get_or_add_ns(ns, opt.strict).await?;
56		txn.get_or_add_db(ns, db, opt.strict).await?;
57		txn.set(
58			key,
59			revision::to_vec(&DefineModelStatement {
60				// Don't persist the `IF NOT EXISTS` clause to schema
61				if_not_exists: false,
62				overwrite: false,
63				..self.clone()
64			})?,
65			None,
66		)
67		.await?;
68		// Clear the cache
69		txn.clear();
70		// Ok all good
71		Ok(Value::None)
72	}
73}
74
75impl fmt::Display for DefineModelStatement {
76	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77		write!(f, "DEFINE MODEL")?;
78		if self.if_not_exists {
79			write!(f, " IF NOT EXISTS")?
80		}
81		if self.overwrite {
82			write!(f, " OVERWRITE")?
83		}
84		write!(f, " ml::{}<{}>", self.name, self.version)?;
85		if let Some(comment) = self.comment.as_ref() {
86			write!(f, " COMMENT {}", comment)?;
87		}
88		let _indent = if is_pretty() {
89			Some(pretty_indent())
90		} else {
91			f.write_char(' ')?;
92			None
93		};
94		write!(f, "PERMISSIONS {}", self.permissions)?;
95		Ok(())
96	}
97}
98
99impl InfoStructure for DefineModelStatement {
100	fn structure(self) -> Value {
101		Value::from(map! {
102			"name".to_string() => self.name.structure(),
103			"version".to_string() => self.version.into(),
104			"permissions".to_string() => self.permissions.structure(),
105			"comment".to_string(), if let Some(v) = self.comment => v.into(),
106		})
107	}
108}