surrealdb_core/sql/statements/
relate.rs

1use crate::ctx::{Context, MutableContext};
2use crate::dbs::{Iterable, Iterator, Options, Statement};
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::idx::planner::RecordStrategy;
6use crate::sql::{Data, Output, Timeout, Value};
7
8use reblessive::tree::Stk;
9use revision::revisioned;
10use serde::{Deserialize, Serialize};
11use std::fmt;
12
13#[revisioned(revision = 2)]
14#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
15#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
16#[non_exhaustive]
17pub struct RelateStatement {
18	#[revision(start = 2)]
19	pub only: bool,
20	pub kind: Value,
21	pub from: Value,
22	pub with: Value,
23	pub uniq: bool,
24	pub data: Option<Data>,
25	pub output: Option<Output>,
26	pub timeout: Option<Timeout>,
27	pub parallel: bool,
28}
29
30impl RelateStatement {
31	/// Check if we require a writeable transaction
32	pub(crate) fn writeable(&self) -> bool {
33		true
34	}
35	/// Process this type returning a computed simple Value
36	pub(crate) async fn compute(
37		&self,
38		stk: &mut Stk,
39		ctx: &Context,
40		opt: &Options,
41		doc: Option<&CursorDoc>,
42	) -> Result<Value, Error> {
43		// Valid options?
44		opt.valid_for_db()?;
45		// Create a new iterator
46		let mut i = Iterator::new();
47		// Ensure futures are stored
48		let opt = &opt.new_with_futures(false);
49		// Check if there is a timeout
50		let ctx = match self.timeout.as_ref() {
51			Some(timeout) => {
52				let mut ctx = MutableContext::new(ctx);
53				ctx.add_timeout(*timeout.0)?;
54				ctx.freeze()
55			}
56			None => ctx.clone(),
57		};
58		// Loop over the from targets
59		let from = {
60			let mut out = Vec::new();
61			match self.from.compute(stk, &ctx, opt, doc).await? {
62				Value::Thing(v) => out.push(v),
63				Value::Array(v) => {
64					for v in v {
65						match v {
66							Value::Thing(v) => out.push(v),
67							Value::Object(v) => match v.rid() {
68								Some(v) => out.push(v),
69								_ => {
70									return Err(Error::RelateStatementIn {
71										value: v.to_string(),
72									})
73								}
74							},
75							v => {
76								return Err(Error::RelateStatementIn {
77									value: v.to_string(),
78								})
79							}
80						}
81					}
82				}
83				Value::Object(v) => match v.rid() {
84					Some(v) => out.push(v),
85					None => {
86						return Err(Error::RelateStatementIn {
87							value: v.to_string(),
88						})
89					}
90				},
91				v => {
92					return Err(Error::RelateStatementIn {
93						value: v.to_string(),
94					})
95				}
96			};
97			// }
98			out
99		};
100		// Loop over the with targets
101		let with = {
102			let mut out = Vec::new();
103			match self.with.compute(stk, &ctx, opt, doc).await? {
104				Value::Thing(v) => out.push(v),
105				Value::Array(v) => {
106					for v in v {
107						match v {
108							Value::Thing(v) => out.push(v),
109							Value::Object(v) => match v.rid() {
110								Some(v) => out.push(v),
111								None => {
112									return Err(Error::RelateStatementId {
113										value: v.to_string(),
114									})
115								}
116							},
117							v => {
118								return Err(Error::RelateStatementId {
119									value: v.to_string(),
120								})
121							}
122						}
123					}
124				}
125				Value::Object(v) => match v.rid() {
126					Some(v) => out.push(v),
127					None => {
128						return Err(Error::RelateStatementId {
129							value: v.to_string(),
130						})
131					}
132				},
133				v => {
134					return Err(Error::RelateStatementId {
135						value: v.to_string(),
136					})
137				}
138			};
139			out
140		};
141		//
142		for f in from.iter() {
143			for w in with.iter() {
144				let f = f.clone();
145				let w = w.clone();
146				match &self.kind.compute(stk, &ctx, opt, doc).await? {
147					// The relation has a specific record id
148					Value::Thing(id) => i.ingest(Iterable::Relatable(f, id.to_owned(), w, None)),
149					// The relation does not have a specific record id
150					Value::Table(tb) => match &self.data {
151						// There is a data clause so check for a record id
152						Some(data) => {
153							let id = match data.rid(stk, &ctx, opt).await? {
154								Some(id) => id.generate(tb, false)?,
155								None => tb.generate(),
156							};
157							i.ingest(Iterable::Relatable(f, id, w, None))
158						}
159						// There is no data clause so create a record id
160						None => i.ingest(Iterable::Relatable(f, tb.generate(), w, None)),
161					},
162					// The relation can not be any other type
163					v => {
164						return Err(Error::RelateStatementOut {
165							value: v.to_string(),
166						})
167					}
168				};
169			}
170		}
171		// Assign the statement
172		let stm = Statement::from(self);
173		// Process the statement
174		let res = i.output(stk, &ctx, opt, &stm, RecordStrategy::KeysAndValues).await?;
175		// Catch statement timeout
176		if ctx.is_timedout() {
177			return Err(Error::QueryTimedout);
178		}
179		// Output the results
180		match res {
181			// This is a single record result
182			Value::Array(mut a) if self.only => match a.len() {
183				// There was exactly one result
184				1 => Ok(a.remove(0)),
185				// There were no results
186				_ => Err(Error::SingleOnlyOutput),
187			},
188			// This is standard query result
189			v => Ok(v),
190		}
191	}
192}
193
194impl fmt::Display for RelateStatement {
195	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196		write!(f, "RELATE")?;
197		if self.only {
198			f.write_str(" ONLY")?
199		}
200		write!(f, " {} -> {} -> {}", self.from, self.kind, self.with)?;
201		if self.uniq {
202			f.write_str(" UNIQUE")?
203		}
204		if let Some(ref v) = self.data {
205			write!(f, " {v}")?
206		}
207		if let Some(ref v) = self.output {
208			write!(f, " {v}")?
209		}
210		if let Some(ref v) = self.timeout {
211			write!(f, " {v}")?
212		}
213		if self.parallel {
214			f.write_str(" PARALLEL")?
215		}
216		Ok(())
217	}
218}