Skip to main content

surrealdb_expr/expr/
lookup.rs

1use surrealdb_types::{SqlFormat, ToSql};
2
3use crate::expr::order::Ordering;
4use crate::expr::start::Start;
5use crate::expr::{Cond, Dir, Fields, Groups, Idiom, Limit, RecordIdKeyRangeLit, Splits};
6use crate::val::{RecordIdKeyRange, TableName};
7
8/// A lookup is a unified way of looking up graph edges and record references.
9/// Since they both work very similarly, they also both support the same operations
10#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
11pub struct Lookup {
12	pub kind: LookupKind,
13	pub expr: Option<Fields>,
14	pub only: bool,
15	pub what: Vec<LookupSubject>,
16	pub cond: Option<Cond>,
17	pub split: Option<Splits>,
18	pub group: Option<Groups>,
19	pub order: Option<Ordering>,
20	pub limit: Option<Limit>,
21	pub start: Option<Start>,
22	pub alias: Option<Idiom>,
23}
24
25impl Lookup {
26	/// Whether evaluating this graph lookup can be done on a read-only
27	/// transaction. Every clause the lookup carries can hold a user
28	/// expression, so each is inspected; the subject (`what`) names tables and
29	/// ranges only.
30	pub fn read_only(&self) -> bool {
31		self.what.iter().all(LookupSubject::read_only)
32			&& self.expr.as_ref().map(|x| x.read_only()).unwrap_or(true)
33			&& self.cond.as_ref().map(|x| x.0.read_only()).unwrap_or(true)
34			&& self.split.as_ref().map(|x| x.read_only()).unwrap_or(true)
35			&& self.group.as_ref().map(|x| x.read_only()).unwrap_or(true)
36			&& self.order.as_ref().map(|x| x.read_only()).unwrap_or(true)
37			&& self.limit.as_ref().map(|x| x.read_only()).unwrap_or(true)
38			&& self.start.as_ref().map(|x| x.read_only()).unwrap_or(true)
39			&& self.alias.as_ref().map(|x| x.read_only()).unwrap_or(true)
40	}
41}
42
43impl LookupSubject {
44	/// A range subject's bounds are record-id keys, which carry arbitrary
45	/// expressions in their array and object forms.
46	pub fn read_only(&self) -> bool {
47		match self {
48			LookupSubject::Table {
49				..
50			} => true,
51			LookupSubject::Range {
52				range,
53				..
54			} => range.read_only(),
55		}
56	}
57}
58
59impl ToSql for Lookup {
60	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
61		let stmt: crate::sql::lookup::Lookup = self.clone().into();
62		stmt.fmt_sql(f, fmt);
63	}
64}
65
66/// This enum instructs whether the lookup is a graph edge or a record reference
67#[derive(Clone, Debug, Eq, PartialEq, Hash)]
68pub enum LookupKind {
69	Graph(Dir),
70	Reference,
71}
72
73impl Default for LookupKind {
74	fn default() -> Self {
75		Self::Graph(Dir::default())
76	}
77}
78
79impl ToSql for LookupKind {
80	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
81		match self {
82			Self::Graph(dir) => dir.fmt_sql(f, fmt),
83			Self::Reference => f.push_str("<~"),
84		}
85	}
86}
87
88/// This enum instructs whether we scan all edges on a table or just a specific range
89#[derive(Clone, Debug, Eq, PartialEq, Hash)]
90pub enum LookupSubject {
91	Table {
92		table: TableName,
93		referencing_field: Option<String>,
94	},
95	Range {
96		table: TableName,
97		range: RecordIdKeyRangeLit,
98		referencing_field: Option<String>,
99	},
100}
101
102/// This enum instructs whether we scan all edges on a table or just a specific range
103#[derive(Clone, Debug, Eq, PartialEq, Hash)]
104pub enum ComputedLookupSubject {
105	Table {
106		table: TableName,
107		referencing_field: Option<String>,
108	},
109	Range {
110		table: TableName,
111		range: RecordIdKeyRange,
112		referencing_field: Option<String>,
113	},
114}
115
116impl ComputedLookupSubject {
117	pub fn into_literal(self) -> LookupSubject {
118		match self {
119			ComputedLookupSubject::Table {
120				table,
121				referencing_field,
122			} => LookupSubject::Table {
123				table,
124				referencing_field,
125			},
126			ComputedLookupSubject::Range {
127				table,
128				range,
129				referencing_field,
130			} => LookupSubject::Range {
131				table,
132				range: range.into_literal(),
133				referencing_field,
134			},
135		}
136	}
137}
138
139impl ToSql for LookupSubject {
140	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
141		let stmt: crate::sql::lookup::LookupSubject = self.clone().into();
142		stmt.fmt_sql(f, fmt);
143	}
144}