surrealdb_core/sql/
param.rs

1use crate::{
2	ctx::Context,
3	dbs::Options,
4	doc::CursorDoc,
5	err::Error,
6	iam::Action,
7	sql::{ident::Ident, value::Value, Permission},
8};
9use reblessive::tree::Stk;
10use revision::revisioned;
11use serde::{Deserialize, Serialize};
12use std::{fmt, ops::Deref, str};
13
14pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Param";
15
16#[revisioned(revision = 1)]
17#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
18#[serde(rename = "$surrealdb::private::sql::Param")]
19#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
20#[non_exhaustive]
21pub struct Param(pub Ident);
22
23impl From<Ident> for Param {
24	fn from(v: Ident) -> Self {
25		Self(v)
26	}
27}
28
29impl From<String> for Param {
30	fn from(v: String) -> Self {
31		Self(v.into())
32	}
33}
34
35impl From<&str> for Param {
36	fn from(v: &str) -> Self {
37		Self(v.into())
38	}
39}
40
41impl Deref for Param {
42	type Target = Ident;
43	fn deref(&self) -> &Self::Target {
44		&self.0
45	}
46}
47
48impl Param {
49	/// Process this type returning a computed simple Value
50	pub(crate) async fn compute(
51		&self,
52		stk: &mut Stk,
53		ctx: &Context,
54		opt: &Options,
55		doc: Option<&CursorDoc>,
56	) -> Result<Value, Error> {
57		// Find the variable by name
58		match self.as_str() {
59			// This is a special param
60			"this" | "self" => match doc {
61				// The base document exists
62				Some(v) => v.doc.as_ref().compute(stk, ctx, opt, doc).await,
63				// The base document does not exist
64				None => Ok(Value::None),
65			},
66			// This is a normal param
67			v => match ctx.value(v) {
68				// The param has been set locally
69				Some(v) => v.compute(stk, ctx, opt, doc).await,
70				// The param has not been set locally
71				None => {
72					// Ensure a database is set
73					opt.valid_for_db()?;
74					// Fetch a defined param if set
75					let (ns, db) = opt.ns_db()?;
76					let val = ctx.tx().get_db_param(ns, db, v).await;
77					// Check if the param has been set globally
78					match val {
79						// The param has been set globally
80						Ok(val) => {
81							// Check permissions
82							if opt.check_perms(Action::View)? {
83								match &val.permissions {
84									Permission::Full => (),
85									Permission::None => {
86										return Err(Error::ParamPermissions {
87											name: v.to_owned(),
88										})
89									}
90									Permission::Specific(e) => {
91										// Disable permissions
92										let opt = &opt.new_with_perms(false);
93										// Process the PERMISSION clause
94										if !e.compute(stk, ctx, opt, doc).await?.is_truthy() {
95											return Err(Error::ParamPermissions {
96												name: v.to_owned(),
97											});
98										}
99									}
100								}
101							}
102							// Return the computed value
103							val.value.compute(stk, ctx, opt, doc).await
104						}
105						// The param has not been set globally
106						Err(Error::PaNotFound {
107							..
108						}) => Ok(Value::None),
109						// There was another request error
110						Err(e) => Err(e),
111					}
112				}
113			},
114		}
115	}
116}
117
118impl fmt::Display for Param {
119	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120		write!(f, "${}", &self.0 .0)
121	}
122}