Skip to main content

surrealdb_core/sql/
part.rs

1use surrealdb_types::{SqlFormat, ToSql, write_sql};
2
3use crate::fmt::{CoverStmts, EscapeKwFreeIdent, Fmt};
4use crate::sql::{Expr, Idiom, Lookup};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub(crate) enum Part {
8	All,
9	Flatten,
10	Last,
11	First,
12	Field(String),
13	Where(Expr),
14	Graph(Lookup),
15	Value(Expr),
16	Start(Expr),
17	Method(String, Vec<Expr>),
18	Destructure(Vec<DestructurePart>),
19	Optional,
20	Recurse(Recurse, Option<Idiom>, Option<RecurseInstruction>),
21	Doc,
22	RepeatRecurse,
23}
24
25impl From<Part> for crate::expr::Part {
26	fn from(v: Part) -> Self {
27		match v {
28			Part::All => Self::All,
29			Part::Flatten => Self::Flatten,
30			Part::Last => Self::Last,
31			Part::First => Self::First,
32			Part::Field(ident) => Self::Field(ident),
33			Part::Where(value) => Self::Where(value.into()),
34			Part::Graph(graph) => Self::Lookup(graph.into()),
35			Part::Value(value) => Self::Value(value.into()),
36			Part::Start(value) => Self::Start(value.into()),
37			Part::Method(method, values) => {
38				Self::Method(method, values.into_iter().map(Into::into).collect())
39			}
40			Part::Destructure(parts) => {
41				Self::Destructure(parts.into_iter().map(Into::into).collect())
42			}
43			Part::Optional => Self::Optional,
44			Part::Recurse(recurse, idiom, instructions) => {
45				let idiom = idiom.map(|idiom| idiom.into());
46				let instructions = instructions.map(Into::into);
47				crate::expr::Part::Recurse(recurse.into(), idiom, instructions)
48			}
49			Part::Doc => crate::expr::Part::Doc,
50			Part::RepeatRecurse => crate::expr::Part::RepeatRecurse,
51		}
52	}
53}
54
55impl From<crate::expr::Part> for Part {
56	fn from(v: crate::expr::Part) -> Self {
57		match v {
58			crate::expr::Part::All => Self::All,
59			crate::expr::Part::Flatten => Self::Flatten,
60			crate::expr::Part::Last => Self::Last,
61			crate::expr::Part::First => Self::First,
62			crate::expr::Part::Field(ident) => Self::Field(ident),
63			crate::expr::Part::Where(value) => Self::Where(value.into()),
64			crate::expr::Part::Lookup(graph) => Self::Graph(graph.into()),
65			crate::expr::Part::Value(value) => Self::Value(value.into()),
66			crate::expr::Part::Start(value) => Self::Start(value.into()),
67			crate::expr::Part::Method(method, values) => {
68				Self::Method(method, values.into_iter().map(Into::into).collect())
69			}
70			crate::expr::Part::Destructure(parts) => {
71				Self::Destructure(parts.into_iter().map(Into::<DestructurePart>::into).collect())
72			}
73			crate::expr::Part::Optional => Self::Optional,
74			crate::expr::Part::Recurse(recurse, idiom, instructions) => Self::Recurse(
75				recurse.into(),
76				idiom.map(|idiom| idiom.into()),
77				instructions.map(Into::into),
78			),
79			crate::expr::Part::Doc => Self::Doc,
80			crate::expr::Part::RepeatRecurse => Self::RepeatRecurse,
81		}
82	}
83}
84
85impl ToSql for Part {
86	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
87		match self {
88			Part::All => f.push_str(".*"),
89			Part::Last => f.push_str("[$]"),
90			Part::First => f.push_str("[0]"),
91			Part::Start(v) => v.fmt_sql(f, fmt),
92			Part::Field(v) => write_sql!(f, fmt, ".{}", EscapeKwFreeIdent(v)),
93			Part::Flatten => f.push('…'),
94			Part::Where(v) => write_sql!(f, fmt, "[WHERE {v}]"),
95			Part::Graph(v) => v.fmt_sql(f, fmt),
96			Part::Value(v) => write_sql!(f, fmt, "[{v}]"),
97			Part::Method(v, a) => {
98				write_sql!(
99					f,
100					fmt,
101					".{}({})",
102					EscapeKwFreeIdent(v),
103					Fmt::comma_separated(a.iter().map(CoverStmts))
104				)
105			}
106			Part::Destructure(v) => {
107				f.push_str(".{");
108				if !fmt.is_pretty() {
109					f.push(' ');
110				}
111				if !v.is_empty() {
112					let fmt = fmt.increment();
113					write_sql!(f, fmt, "{}", Fmt::pretty_comma_separated(v));
114				}
115				if fmt.is_pretty() {
116					f.push('}');
117				} else {
118					f.push_str(" }");
119				}
120			}
121			Part::Optional => f.push_str(".?"),
122			Part::Recurse(v, nest, instruction) => {
123				write_sql!(f, fmt, ".{{{v}");
124				if let Some(instruction) = instruction {
125					write_sql!(f, fmt, "+{instruction}");
126				}
127				f.push('}');
128
129				if let Some(nest) = nest {
130					f.push('(');
131					for p in nest.0.iter() {
132						p.fmt_sql(f, fmt);
133					}
134					f.push(')');
135				}
136			}
137			Part::Doc => f.push('@'),
138			Part::RepeatRecurse => f.push_str(".@"),
139		}
140	}
141}
142
143// ------------------------------
144
145#[derive(Clone, Debug, PartialEq, Eq)]
146#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
147pub enum DestructurePart {
148	All(String),
149	Field(String),
150	Aliased(String, Idiom),
151	Destructure(String, Vec<DestructurePart>),
152}
153
154impl ToSql for DestructurePart {
155	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
156		match self {
157			DestructurePart::All(fd) => write_sql!(f, fmt, "{}.*", EscapeKwFreeIdent(fd)),
158			DestructurePart::Field(fd) => write_sql!(f, fmt, "{}", EscapeKwFreeIdent(fd)),
159			DestructurePart::Aliased(fd, v) => write_sql!(f, fmt, "{}: {v}", EscapeKwFreeIdent(fd)),
160			DestructurePart::Destructure(fd, d) => {
161				write_sql!(f, fmt, "{}{}", EscapeKwFreeIdent(fd), Part::Destructure(d.clone()))
162			}
163		}
164	}
165}
166
167impl From<DestructurePart> for crate::expr::part::DestructurePart {
168	fn from(v: DestructurePart) -> Self {
169		match v {
170			DestructurePart::All(v) => Self::All(v),
171			DestructurePart::Field(v) => Self::Field(v),
172			DestructurePart::Aliased(v, idiom) => Self::Aliased(v, idiom.into()),
173			DestructurePart::Destructure(v, d) => {
174				Self::Destructure(v, d.into_iter().map(Into::into).collect())
175			}
176		}
177	}
178}
179
180impl From<crate::expr::part::DestructurePart> for DestructurePart {
181	fn from(v: crate::expr::part::DestructurePart) -> Self {
182		match v {
183			crate::expr::part::DestructurePart::All(v) => Self::All(v),
184			crate::expr::part::DestructurePart::Field(v) => Self::Field(v),
185			crate::expr::part::DestructurePart::Aliased(v, idiom) => Self::Aliased(v, idiom.into()),
186			crate::expr::part::DestructurePart::Destructure(v, d) => {
187				Self::Destructure(v, d.into_iter().map(Into::<DestructurePart>::into).collect())
188			}
189		}
190	}
191}
192
193// ------------------------------
194
195#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
196#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
197pub enum Recurse {
198	Fixed(u32),
199	Range(Option<u32>, Option<u32>),
200}
201
202impl ToSql for Recurse {
203	fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
204		match self {
205			Recurse::Fixed(v) => f.push_str(&v.to_string()),
206			Recurse::Range(beg, end) => match (beg, end) {
207				(None, None) => f.push_str(".."),
208				(Some(beg), None) => {
209					f.push_str(&beg.to_string());
210					f.push_str("..");
211				}
212				(None, Some(end)) => {
213					f.push_str("..");
214					f.push_str(&end.to_string());
215				}
216				(Some(beg), Some(end)) => {
217					f.push_str(&beg.to_string());
218					f.push_str("..");
219					f.push_str(&end.to_string());
220				}
221			},
222		}
223	}
224}
225
226impl From<Recurse> for crate::expr::part::Recurse {
227	fn from(v: Recurse) -> Self {
228		match v {
229			Recurse::Fixed(v) => Self::Fixed(v),
230			Recurse::Range(min, max) => Self::Range(min, max),
231		}
232	}
233}
234
235impl From<crate::expr::part::Recurse> for Recurse {
236	fn from(v: crate::expr::part::Recurse) -> Self {
237		match v {
238			crate::expr::part::Recurse::Fixed(v) => Self::Fixed(v),
239			crate::expr::part::Recurse::Range(min, max) => Self::Range(min, max),
240		}
241	}
242}
243// ------------------------------
244
245#[derive(Clone, Debug, PartialEq, Eq)]
246pub enum RecurseInstruction {
247	Path {
248		// Do we include the starting point in the paths?
249		inclusive: bool,
250	},
251	Collect {
252		// Do we include the starting point in the collection?
253		inclusive: bool,
254	},
255	Shortest {
256		// What ending node are we looking for?
257		expects: Expr,
258		// Do we include the starting point in the collection?
259		inclusive: bool,
260	},
261}
262
263impl ToSql for RecurseInstruction {
264	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
265		match self {
266			Self::Path {
267				inclusive,
268			} => {
269				f.push_str("path");
270
271				if *inclusive {
272					f.push_str("+inclusive");
273				}
274			}
275			Self::Collect {
276				inclusive,
277			} => {
278				f.push_str("collect");
279
280				if *inclusive {
281					f.push_str("+inclusive");
282				}
283			}
284			Self::Shortest {
285				expects,
286				inclusive,
287			} => {
288				write_sql!(f, fmt, "shortest={expects}");
289
290				if *inclusive {
291					f.push_str("+inclusive");
292				}
293			}
294		}
295	}
296}
297
298impl From<RecurseInstruction> for crate::expr::part::RecurseInstruction {
299	fn from(v: RecurseInstruction) -> Self {
300		match v {
301			RecurseInstruction::Path {
302				inclusive,
303			} => Self::Path {
304				inclusive,
305			},
306			RecurseInstruction::Collect {
307				inclusive,
308			} => Self::Collect {
309				inclusive,
310			},
311			RecurseInstruction::Shortest {
312				expects,
313				inclusive,
314			} => Self::Shortest {
315				expects: expects.into(),
316				inclusive,
317			},
318		}
319	}
320}
321
322impl From<crate::expr::part::RecurseInstruction> for RecurseInstruction {
323	fn from(v: crate::expr::part::RecurseInstruction) -> Self {
324		match v {
325			crate::expr::part::RecurseInstruction::Path {
326				inclusive,
327			} => Self::Path {
328				inclusive,
329			},
330			crate::expr::part::RecurseInstruction::Collect {
331				inclusive,
332			} => Self::Collect {
333				inclusive,
334			},
335			crate::expr::part::RecurseInstruction::Shortest {
336				expects,
337				inclusive,
338			} => Self::Shortest {
339				expects: expects.into(),
340				inclusive,
341			},
342		}
343	}
344}