surrealdb_core/sql/statements/
info.rs

1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::iam::Action;
6use crate::iam::ResourceKind;
7use crate::sql::{Base, Ident, Object, Value, Version};
8use crate::sys::INFORMATION;
9
10use reblessive::tree::Stk;
11use revision::revisioned;
12use serde::{Deserialize, Serialize};
13use std::fmt;
14use std::sync::Arc;
15
16#[revisioned(revision = 5)]
17#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
18#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
19#[non_exhaustive]
20pub enum InfoStatement {
21	// revision discriminant override accounting for previous behavior when adding variants and
22	// removing not at the end of the enum definition.
23	#[revision(override(revision = 2, discriminant = 1), override(revision = 3, discriminant = 1))]
24	Root(#[revision(start = 2)] bool),
25
26	#[revision(override(revision = 2, discriminant = 3), override(revision = 3, discriminant = 3))]
27	Ns(#[revision(start = 2)] bool),
28
29	#[revision(override(revision = 2, discriminant = 5), override(revision = 3, discriminant = 5))]
30	Db(#[revision(start = 2)] bool, #[revision(start = 5)] Option<Version>),
31
32	#[revision(override(revision = 2, discriminant = 7), override(revision = 3, discriminant = 7))]
33	Tb(Ident, #[revision(start = 2)] bool, #[revision(start = 5)] Option<Version>),
34
35	#[revision(override(revision = 2, discriminant = 9), override(revision = 3, discriminant = 9))]
36	User(Ident, Option<Base>, #[revision(start = 2)] bool),
37
38	#[revision(start = 3)]
39	#[revision(override(revision = 3, discriminant = 10))]
40	Index(Ident, Ident, bool),
41}
42
43impl InfoStatement {
44	/// Process this type returning a computed simple Value
45	pub(crate) async fn compute(
46		&self,
47		stk: &mut Stk,
48		ctx: &Context,
49		opt: &Options,
50		_doc: Option<&CursorDoc>,
51	) -> Result<Value, Error> {
52		match self {
53			InfoStatement::Root(structured) => {
54				// Allowed to run?
55				opt.is_allowed(Action::View, ResourceKind::Any, &Base::Root)?;
56				// Get the transaction
57				let txn = ctx.tx();
58				// Create the result set
59				Ok(match structured {
60					true => Value::from(map! {
61						"accesses".to_string() => process(txn.all_root_accesses().await?.iter().map(|v| v.redacted()).collect()),
62						"namespaces".to_string() => process(txn.all_ns().await?),
63						"nodes".to_string() => process(txn.all_nodes().await?),
64						"system".to_string() => system().await,
65						"users".to_string() => process(txn.all_root_users().await?),
66					}),
67					false => Value::from(map! {
68						"accesses".to_string() => {
69							let mut out = Object::default();
70							for v in txn.all_root_accesses().await?.iter().map(|v| v.redacted()) {
71								out.insert(v.name.to_raw(), v.to_string().into());
72							}
73							out.into()
74						},
75						"namespaces".to_string() => {
76							let mut out = Object::default();
77							for v in txn.all_ns().await?.iter() {
78								out.insert(v.name.to_raw(), v.to_string().into());
79							}
80							out.into()
81						},
82						"nodes".to_string() => {
83							let mut out = Object::default();
84							for v in txn.all_nodes().await?.iter() {
85								out.insert(v.id.to_string(), v.to_string().into());
86							}
87							out.into()
88						},
89						"system".to_string() => system().await,
90						"users".to_string() => {
91							let mut out = Object::default();
92							for v in txn.all_root_users().await?.iter() {
93								out.insert(v.name.to_raw(), v.to_string().into());
94							}
95							out.into()
96						}
97					}),
98				})
99			}
100			InfoStatement::Ns(structured) => {
101				// Allowed to run?
102				opt.is_allowed(Action::View, ResourceKind::Any, &Base::Ns)?;
103				// Get the NS
104				let ns = opt.ns()?;
105				// Get the transaction
106				let txn = ctx.tx();
107				// Create the result set
108				Ok(match structured {
109					true => Value::from(map! {
110						"accesses".to_string() => process(txn.all_ns_accesses(ns).await?.iter().map(|v| v.redacted()).collect()),
111						"databases".to_string() => process(txn.all_db(ns).await?),
112						"users".to_string() => process(txn.all_ns_users(ns).await?),
113					}),
114					false => Value::from(map! {
115						"accesses".to_string() => {
116							let mut out = Object::default();
117							for v in txn.all_ns_accesses(ns).await?.iter().map(|v| v.redacted()) {
118								out.insert(v.name.to_raw(), v.to_string().into());
119							}
120							out.into()
121						},
122						"databases".to_string() => {
123							let mut out = Object::default();
124							for v in txn.all_db(ns).await?.iter() {
125								out.insert(v.name.to_raw(), v.to_string().into());
126							}
127							out.into()
128						},
129						"users".to_string() => {
130							let mut out = Object::default();
131							for v in txn.all_ns_users(ns).await?.iter() {
132								out.insert(v.name.to_raw(), v.to_string().into());
133							}
134							out.into()
135						},
136					}),
137				})
138			}
139			InfoStatement::Db(structured, version) => {
140				// Allowed to run?
141				opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?;
142				// Get the NS and DB
143				let (ns, db) = opt.ns_db()?;
144				// Convert the version to u64 if present
145				let version = match version {
146					Some(v) => Some(v.compute(stk, ctx, opt, None).await?),
147					_ => None,
148				};
149				// Get the transaction
150				let txn = ctx.tx();
151				// Create the result set
152				Ok(match structured {
153					true => Value::from(map! {
154						"accesses".to_string() => process(txn.all_db_accesses(ns, db).await?.iter().map(|v| v.redacted()).collect()),
155						"apis".to_string() => process(txn.all_db_apis(ns, db).await?),
156						"analyzers".to_string() => process(txn.all_db_analyzers(ns, db).await?),
157						"functions".to_string() => process(txn.all_db_functions(ns, db).await?),
158						"models".to_string() => process(txn.all_db_models(ns, db).await?),
159						"params".to_string() => process(txn.all_db_params(ns, db).await?),
160						"tables".to_string() => process(txn.all_tb(ns, db, version).await?),
161						"users".to_string() => process(txn.all_db_users(ns, db).await?),
162						"configs".to_string() => process(txn.all_db_configs(ns, db).await?),
163					}),
164					false => Value::from(map! {
165						"accesses".to_string() => {
166							let mut out = Object::default();
167							for v in txn.all_db_accesses(ns, db).await?.iter().map(|v| v.redacted()) {
168								out.insert(v.name.to_raw(), v.to_string().into());
169							}
170							out.into()
171						},
172						"apis".to_string() => {
173							let mut out = Object::default();
174							for v in txn.all_db_apis(ns, db).await?.iter() {
175								out.insert(v.path.to_string(), v.to_string().into());
176							}
177							out.into()
178						},
179						"analyzers".to_string() => {
180							let mut out = Object::default();
181							for v in txn.all_db_analyzers( ns, db).await?.iter() {
182								out.insert(v.name.to_raw(), v.to_string().into());
183							}
184							out.into()
185						},
186						"functions".to_string() => {
187							let mut out = Object::default();
188							for v in txn.all_db_functions(ns, db).await?.iter() {
189								out.insert(v.name.to_raw(), v.to_string().into());
190							}
191							out.into()
192						},
193						"models".to_string() => {
194							let mut out = Object::default();
195							for v in txn.all_db_models(ns, db).await?.iter() {
196								out.insert(v.name.to_raw(), v.to_string().into());
197							}
198							out.into()
199						},
200						"params".to_string() => {
201							let mut out = Object::default();
202							for v in txn.all_db_params(ns, db).await?.iter() {
203								out.insert(v.name.to_raw(), v.to_string().into());
204							}
205							out.into()
206						},
207						"tables".to_string() => {
208							let mut out = Object::default();
209							for v in txn.all_tb(ns, db, version).await?.iter() {
210								out.insert(v.name.to_raw(), v.to_string().into());
211							}
212							out.into()
213						},
214						"users".to_string() => {
215							let mut out = Object::default();
216							for v in txn.all_db_users(ns, db).await?.iter() {
217								out.insert(v.name.to_raw(), v.to_string().into());
218							}
219							out.into()
220						},
221						"configs".to_string() => {
222							let mut out = Object::default();
223							for v in txn.all_db_configs(ns, db).await?.iter() {
224								out.insert(v.inner.name(), v.to_string().into());
225							}
226							out.into()
227						},
228					}),
229				})
230			}
231			InfoStatement::Tb(tb, structured, version) => {
232				// Allowed to run?
233				opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?;
234				// Get the NS and DB
235				let (ns, db) = opt.ns_db()?;
236				// Convert the version to u64 if present
237				let version = match version {
238					Some(v) => Some(v.compute(stk, ctx, opt, None).await?),
239					_ => None,
240				};
241				// Get the transaction
242				let txn = ctx.tx();
243				// Create the result set
244				Ok(match structured {
245					true => Value::from(map! {
246						"events".to_string() => process(txn.all_tb_events(ns, db, tb).await?),
247						"fields".to_string() => process(txn.all_tb_fields(ns, db, tb, version).await?),
248						"indexes".to_string() => process(txn.all_tb_indexes(ns, db, tb).await?),
249						"lives".to_string() => process(txn.all_tb_lives(ns, db, tb).await?),
250						"tables".to_string() => process(txn.all_tb_views(ns, db, tb).await?),
251					}),
252					false => Value::from(map! {
253						"events".to_string() => {
254							let mut out = Object::default();
255							for v in txn.all_tb_events(ns, db, tb).await?.iter() {
256								out.insert(v.name.to_raw(), v.to_string().into());
257							}
258							out.into()
259						},
260						"fields".to_string() => {
261							let mut out = Object::default();
262							for v in txn.all_tb_fields(ns, db, tb, version).await?.iter() {
263								out.insert(v.name.to_string(), v.to_string().into());
264							}
265							out.into()
266						},
267						"indexes".to_string() => {
268							let mut out = Object::default();
269							for v in txn.all_tb_indexes(ns, db, tb).await?.iter() {
270								out.insert(v.name.to_raw(), v.to_string().into());
271							}
272							out.into()
273						},
274						"lives".to_string() => {
275							let mut out = Object::default();
276							for v in txn.all_tb_lives(ns, db, tb).await?.iter() {
277								out.insert(v.id.to_raw(), v.to_string().into());
278							}
279							out.into()
280						},
281						"tables".to_string() => {
282							let mut out = Object::default();
283							for v in txn.all_tb_views(ns, db, tb).await?.iter() {
284								out.insert(v.name.to_raw(), v.to_string().into());
285							}
286							out.into()
287						},
288					}),
289				})
290			}
291			InfoStatement::User(user, base, structured) => {
292				// Get the base type
293				let base = base.clone().unwrap_or(opt.selected_base()?);
294				// Allowed to run?
295				opt.is_allowed(Action::View, ResourceKind::Actor, &base)?;
296				// Get the transaction
297				let txn = ctx.tx();
298				// Process the user
299				let res = match base {
300					Base::Root => txn.get_root_user(user).await?,
301					Base::Ns => txn.get_ns_user(opt.ns()?, user).await?,
302					Base::Db => {
303						let (ns, db) = opt.ns_db()?;
304						txn.get_db_user(ns, db, user).await?
305					}
306					_ => return Err(Error::InvalidLevel(base.to_string())),
307				};
308				// Ok all good
309				Ok(match structured {
310					true => res.as_ref().clone().structure(),
311					false => Value::from(res.to_string()),
312				})
313			}
314			#[allow(unused_variables)]
315			InfoStatement::Index(index, table, _structured) => {
316				// Allowed to run?
317				opt.is_allowed(Action::View, ResourceKind::Actor, &Base::Db)?;
318				// Get the transaction
319				let txn = ctx.tx();
320				// Output
321				#[cfg(not(target_family = "wasm"))]
322				if let Some(ib) = ctx.get_index_builder() {
323					// Obtain the index
324					let (ns, db) = opt.ns_db()?;
325					let res = txn.get_tb_index(ns, db, table, index).await?;
326					let status = ib.get_status(ns, db, &res).await;
327					let mut out = Object::default();
328					out.insert("building".to_string(), status.into());
329					return Ok(out.into());
330				}
331				Ok(Object::default().into())
332			}
333		}
334	}
335}
336
337impl fmt::Display for InfoStatement {
338	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
339		match self {
340			Self::Root(false) => f.write_str("INFO FOR ROOT"),
341			Self::Root(true) => f.write_str("INFO FOR ROOT STRUCTURE"),
342			Self::Ns(false) => f.write_str("INFO FOR NAMESPACE"),
343			Self::Ns(true) => f.write_str("INFO FOR NAMESPACE STRUCTURE"),
344			Self::Db(false, ref v) => match v {
345				Some(ref v) => write!(f, "INFO FOR DATABASE VERSION {v}"),
346				None => f.write_str("INFO FOR DATABASE"),
347			},
348			Self::Db(true, ref v) => match v {
349				Some(ref v) => write!(f, "INFO FOR DATABASE VERSION {v} STRUCTURE"),
350				None => f.write_str("INFO FOR DATABASE STRUCTURE"),
351			},
352			Self::Tb(ref t, false, ref v) => match v {
353				Some(ref v) => write!(f, "INFO FOR TABLE {t} VERSION {v}"),
354				None => write!(f, "INFO FOR TABLE {t}"),
355			},
356
357			Self::Tb(ref t, true, ref v) => match v {
358				Some(ref v) => write!(f, "INFO FOR TABLE {t} VERSION {v} STRUCTURE"),
359				None => write!(f, "INFO FOR TABLE {t} STRUCTURE"),
360			},
361			Self::User(ref u, ref b, false) => match b {
362				Some(ref b) => write!(f, "INFO FOR USER {u} ON {b}"),
363				None => write!(f, "INFO FOR USER {u}"),
364			},
365			Self::User(ref u, ref b, true) => match b {
366				Some(ref b) => write!(f, "INFO FOR USER {u} ON {b} STRUCTURE"),
367				None => write!(f, "INFO FOR USER {u} STRUCTURE"),
368			},
369			Self::Index(ref i, ref t, false) => write!(f, "INFO FOR INDEX {i} ON {t}"),
370			Self::Index(ref i, ref t, true) => write!(f, "INFO FOR INDEX {i} ON {t} STRUCTURE"),
371		}
372	}
373}
374
375pub(crate) trait InfoStructure {
376	fn structure(self) -> Value;
377}
378
379impl InfoStatement {
380	pub(crate) fn structurize(self) -> Self {
381		match self {
382			InfoStatement::Root(_) => InfoStatement::Root(true),
383			InfoStatement::Ns(_) => InfoStatement::Ns(true),
384			InfoStatement::Db(_, v) => InfoStatement::Db(true, v),
385			InfoStatement::Tb(t, _, v) => InfoStatement::Tb(t, true, v),
386			InfoStatement::User(u, b, _) => InfoStatement::User(u, b, true),
387			InfoStatement::Index(i, t, _) => InfoStatement::Index(i, t, true),
388		}
389	}
390
391	pub(crate) fn versionize(self, v: Version) -> Self {
392		match self {
393			InfoStatement::Db(s, _) => InfoStatement::Db(s, Some(v)),
394			InfoStatement::Tb(t, s, _) => InfoStatement::Tb(t, s, Some(v)),
395			_ => self,
396		}
397	}
398}
399
400fn process<T>(a: Arc<[T]>) -> Value
401where
402	T: InfoStructure + Clone,
403{
404	Value::Array(a.iter().cloned().map(InfoStructure::structure).collect())
405}
406
407async fn system() -> Value {
408	let info = INFORMATION.lock().await;
409	Value::from(map! {
410		"available_parallelism".to_string() => info.available_parallelism.into(),
411		"cpu_usage".to_string() => info.cpu_usage.into(),
412		"load_average".to_string() => info.load_average.to_vec().into(),
413		"memory_usage".to_string() => info.memory_usage.into(),
414		"physical_cores".to_string() => info.physical_cores.into(),
415		"memory_allocated".to_string() => info.memory_allocated.into(),
416		"threads".to_string() => info.threads.into(),
417	})
418}