surrealdb_core/expr/
param.rs

1use std::ops::Deref;
2use std::{fmt, str};
3
4use anyhow::{Result, bail};
5use reblessive::tree::Stk;
6use revision::revisioned;
7use serde::{Deserialize, Serialize};
8
9use super::FlowResultExt as _;
10use crate::ctx::Context;
11use crate::dbs::Options;
12use crate::doc::CursorDoc;
13use crate::err::Error;
14use crate::expr::Permission;
15use crate::expr::escape::EscapeKwFreeIdent;
16use crate::expr::ident::Ident;
17use crate::iam::Action;
18use crate::val::{Strand, Value};
19
20#[revisioned(revision = 1)]
21#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
22pub struct Param(String);
23
24impl Param {
25	/// Create a new identifier
26	///
27	/// This function checks if the string has a null byte, returns None if it
28	/// has.
29	pub fn new(str: String) -> Option<Self> {
30		if str.contains('\0') {
31			return None;
32		}
33		Some(Self(str))
34	}
35
36	/// Create a new identifier
37	///
38	/// # Safety
39	/// Caller should ensure that the string does not contain a null byte.
40	pub unsafe fn new_unchecked(str: String) -> Self {
41		Self(str)
42	}
43
44	/// returns the identifier section of the parameter,
45	/// i.e. `$foo` without the `$` so: `foo`
46	pub fn ident(self) -> Ident {
47		// Safety: Param guarentees no null bytes within it's internal string.
48		unsafe { Ident::new_unchecked(self.0) }
49	}
50}
51
52impl From<Ident> for Param {
53	fn from(v: Ident) -> Self {
54		Self(v.to_string())
55	}
56}
57
58impl From<Strand> for Param {
59	fn from(v: Strand) -> Self {
60		Self(v.into_string())
61	}
62}
63
64impl Deref for Param {
65	type Target = str;
66	fn deref(&self) -> &Self::Target {
67		self.0.as_str()
68	}
69}
70
71impl Param {
72	/// Process this type returning a computed simple Value
73	pub(crate) async fn compute(
74		&self,
75		stk: &mut Stk,
76		ctx: &Context,
77		opt: &Options,
78		doc: Option<&CursorDoc>,
79	) -> Result<Value> {
80		// Find the variable by name
81		match self.0.as_str() {
82			// This is a special param
83			"this" | "self" => match doc {
84				// The base document exists
85				Some(v) => Ok(v.doc.as_ref().clone()),
86				// The base document does not exist
87				None => Ok(Value::None),
88			},
89			// This is a normal param
90			v => match ctx.value(v) {
91				// The param has been set locally
92				Some(v) => Ok(v.clone()),
93				// The param has not been set locally
94				None => {
95					// Ensure a database is set
96					opt.valid_for_db()?;
97					// Fetch a defined param if set
98					let (ns, db) = ctx.expect_ns_db_ids(opt).await?;
99					let val = ctx.tx().get_db_param(ns, db, v).await;
100					// Check if the param has been set globally
101					let val = match val {
102						Ok(x) => x,
103						Err(e) => {
104							if matches!(e.downcast_ref(), Some(Error::PaNotFound { .. })) {
105								return Ok(Value::None);
106							} else {
107								return Err(e);
108							}
109						}
110					};
111
112					if opt.check_perms(Action::View)? {
113						match &val.permissions {
114							Permission::Full => (),
115							Permission::None => {
116								bail!(Error::ParamPermissions {
117									name: v.to_owned(),
118								})
119							}
120							Permission::Specific(e) => {
121								// Disable permissions
122								let opt = &opt.new_with_perms(false);
123								// Process the PERMISSION clause
124								if !stk
125									.run(|stk| e.compute(stk, ctx, opt, doc))
126									.await
127									.catch_return()?
128									.is_truthy()
129								{
130									bail!(Error::ParamPermissions {
131										name: v.to_owned(),
132									});
133								}
134							}
135						}
136					}
137					// Return the computed value
138					Ok(val.value.clone())
139				}
140			},
141		}
142	}
143}
144
145impl fmt::Display for Param {
146	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147		write!(f, "${}", EscapeKwFreeIdent(&self.0))
148	}
149}