surrealdb_core/sql/statements/define/
function.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, Block, Ident, Kind, Permission, Strand, Value};
9
10use revision::revisioned;
11use serde::{Deserialize, Serialize};
12use std::fmt::{self, Display, Write};
13
14#[revisioned(revision = 4)]
15#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[non_exhaustive]
18pub struct DefineFunctionStatement {
19	pub name: Ident,
20	pub args: Vec<(Ident, Kind)>,
21	pub block: Block,
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	#[revision(start = 4)]
29	pub returns: Option<Kind>,
30}
31
32impl DefineFunctionStatement {
33	/// Process this type returning a computed simple Value
34	pub(crate) async fn compute(
35		&self,
36		ctx: &Context,
37		opt: &Options,
38		_doc: Option<&CursorDoc>,
39	) -> Result<Value, Error> {
40		// Allowed to run?
41		opt.is_allowed(Action::Edit, ResourceKind::Function, &Base::Db)?;
42		// Fetch the transaction
43		let txn = ctx.tx();
44		// Check if the definition exists
45		let (ns, db) = opt.ns_db()?;
46		if txn.get_db_function(ns, db, &self.name).await.is_ok() {
47			if self.if_not_exists {
48				return Ok(Value::None);
49			} else if !self.overwrite {
50				return Err(Error::FcAlreadyExists {
51					name: self.name.to_string(),
52				});
53			}
54		}
55		// Process the statement
56		let key = crate::key::database::fc::new(ns, db, &self.name);
57		txn.get_or_add_ns(ns, opt.strict).await?;
58		txn.get_or_add_db(ns, db, opt.strict).await?;
59		txn.set(
60			key,
61			revision::to_vec(&DefineFunctionStatement {
62				// Don't persist the `IF NOT EXISTS` clause to schema
63				if_not_exists: false,
64				overwrite: false,
65				..self.clone()
66			})?,
67			None,
68		)
69		.await?;
70		// Clear the cache
71		txn.clear();
72		// Ok all good
73		Ok(Value::None)
74	}
75}
76
77impl fmt::Display for DefineFunctionStatement {
78	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79		write!(f, "DEFINE FUNCTION")?;
80		if self.if_not_exists {
81			write!(f, " IF NOT EXISTS")?
82		}
83		if self.overwrite {
84			write!(f, " OVERWRITE")?
85		}
86		write!(f, " fn::{}(", self.name.0)?;
87		for (i, (name, kind)) in self.args.iter().enumerate() {
88			if i > 0 {
89				f.write_str(", ")?;
90			}
91			write!(f, "${name}: {kind}")?;
92		}
93		f.write_str(") ")?;
94		if let Some(ref v) = self.returns {
95			write!(f, "-> {v} ")?;
96		}
97		Display::fmt(&self.block, f)?;
98		if let Some(ref v) = self.comment {
99			write!(f, " COMMENT {v}")?
100		}
101		let _indent = if is_pretty() {
102			Some(pretty_indent())
103		} else {
104			f.write_char(' ')?;
105			None
106		};
107		write!(f, "PERMISSIONS {}", self.permissions)?;
108		Ok(())
109	}
110}
111
112impl InfoStructure for DefineFunctionStatement {
113	fn structure(self) -> Value {
114		Value::from(map! {
115			"name".to_string() => self.name.structure(),
116			"args".to_string() => self.args
117				.into_iter()
118				.map(|(n, k)| vec![n.structure(), k.structure()].into())
119				.collect::<Vec<Value>>()
120				.into(),
121			"block".to_string() => self.block.structure(),
122			"permissions".to_string() => self.permissions.structure(),
123			"comment".to_string(), if let Some(v) = self.comment => v.into(),
124			"returns".to_string(), if let Some(v) = self.returns => v.structure(),
125		})
126	}
127}