Skip to main content

surrealdb_expr/expr/
mod.rs

1//! The type definitions for the computation format of the surreaql executor.
2
3use anyhow::Result;
4
5use crate::val::Value;
6
7pub mod access;
8pub mod access_type;
9pub mod algorithm;
10pub mod base;
11pub mod block;
12pub mod bytesize;
13pub mod changefeed;
14pub mod computed_deps;
15pub mod cond;
16pub mod constant;
17pub mod convert;
18pub mod data;
19pub mod dir;
20pub mod error;
21pub mod explain;
22pub mod expression;
23pub mod fetch;
24pub mod field;
25pub mod filter;
26pub mod function;
27pub mod function_facts;
28pub mod graphql_config;
29pub mod group;
30pub mod idiom;
31pub mod index_kind;
32pub mod kind;
33pub mod language;
34pub mod limit;
35pub mod literal;
36pub mod lookup;
37pub mod match_plan;
38// The builtin-method name table lives one layer down, in `surrealdb-sql`, so
39// that the surface AST can classify a method call the same way this layer
40// does. Re-exported here to keep the `crate::expr::method::…` paths intact.
41pub use crate::sql::method;
42pub mod mock;
43pub mod model;
44pub mod mutation;
45pub mod operation;
46pub mod operator;
47pub mod order;
48pub mod output;
49pub mod param;
50pub mod part;
51pub mod paths;
52pub mod permission;
53pub mod plan;
54pub mod record_id;
55pub mod reference;
56pub mod script;
57pub mod split;
58pub mod start;
59pub mod table_type;
60pub mod tokenizer;
61pub mod user;
62pub mod variables;
63pub mod view;
64pub mod with;
65
66pub mod decimal;
67pub mod module;
68
69pub mod closure;
70pub mod statements;
71pub mod visit;
72
73pub use self::access_type::{AccessType, JwtAccess, RecordAccess};
74pub use self::algorithm::Algorithm;
75pub use self::base::Base;
76pub use self::block::Block;
77pub use self::bytesize::Bytesize;
78pub use self::changefeed::ChangeFeed;
79pub use self::closure::ClosureExpr;
80pub use self::cond::Cond;
81pub use self::constant::Constant;
82pub use self::data::Data;
83pub use self::dir::Dir;
84pub use self::error::Error;
85pub use self::explain::Explain;
86pub use self::expression::{ExplainFormat, Expr};
87pub use self::fetch::{Fetch, Fetchs};
88pub use self::field::{Field, Fields};
89pub use self::filter::Filter;
90pub use self::function::{Function, FunctionCall};
91pub use self::group::{Group, Groups};
92pub use self::idiom::Idiom;
93pub use self::kind::{Kind, KindLiteral};
94pub use self::limit::Limit;
95pub use self::literal::{Literal, ObjectEntry};
96pub use self::lookup::Lookup;
97// Re-exported for the GQL lowering and the streaming planner (PR-A landing
98// piecemeal); not yet referenced through this alias in this crate.
99#[allow(unused_imports)]
100pub use self::match_plan::MatchPlan;
101pub use self::mock::Mock;
102pub use self::model::Model;
103pub use self::module::{ModuleExecutable, SiloExecutable, SurrealismExecutable};
104pub use self::operation::Operation;
105pub use self::operator::{AssignOperator, BinaryOperator, PostfixOperator, PrefixOperator};
106pub use self::order::Order;
107pub use self::output::Output;
108pub use self::param::Param;
109pub use self::part::Part;
110pub use self::plan::{LogicalPlan, TopLevelExpr};
111pub use self::record_id::{RecordIdKeyGen, RecordIdKeyLit, RecordIdKeyRangeLit, RecordIdLit};
112pub use self::script::Script;
113pub use self::split::{Split, Splits};
114pub use self::start::Start;
115pub use self::statements::{DefineAnalyzerStatement, SelectStatement, SleepStatement};
116pub use self::tokenizer::{SegmentLanguage, Tokenizer};
117pub use self::view::View;
118pub use self::with::With;
119
120/// Result of functions which can impact the controlflow of query execution.
121pub type FlowResult<T> = Result<T, ControlFlow>;
122
123/// An enum carrying control flow information.
124///
125/// Returned by compute functions which can impact control flow.
126#[derive(Debug)]
127pub enum ControlFlow {
128	Break,
129	Continue,
130	Return(Value),
131	Err(anyhow::Error),
132}
133
134impl std::fmt::Display for ControlFlow {
135	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136		match self {
137			ControlFlow::Break => write!(f, "BREAK"),
138			ControlFlow::Continue => write!(f, "CONTINUE"),
139			ControlFlow::Return(v) => write!(f, "RETURN {:?}", v),
140			ControlFlow::Err(e) => write!(f, "{}", e),
141		}
142	}
143}
144
145impl From<anyhow::Error> for ControlFlow {
146	fn from(error: anyhow::Error) -> Self {
147		ControlFlow::Err(error)
148	}
149}
150
151impl From<Error> for ControlFlow {
152	/// Boxes the value-algebra failure directly, so it stays the concrete type
153	/// in the `anyhow` slot and [`ControlFlow::is_ignorable`] keeps matching it.
154	fn from(error: Error) -> Self {
155		ControlFlow::Err(anyhow::Error::new(error))
156	}
157}
158
159impl ControlFlow {
160	/// Returns true if this represents a data-shape error that can safely
161	/// be treated as `Value::None` (e.g., type mismatches, coercion failures).
162	///
163	/// Returns false for system errors (storage, timeout, permissions),
164	/// control flow signals (Break/Continue), and Return values.
165	pub fn is_ignorable(&self) -> bool {
166		match self {
167			// Control flow signals are never ignorable
168			ControlFlow::Break | ControlFlow::Continue | ControlFlow::Return(_) => false,
169			ControlFlow::Err(e) => match e.downcast_ref::<Error>() {
170				Some(err) => err.is_ignorable(),
171				// Unknown error types are not ignorable
172				None => false,
173			},
174		}
175	}
176}
177
178/// Extension trait for wrapping errors with context and converting them into
179/// [`ControlFlow`].
180///
181/// Modelled on [`anyhow::Context`], this provides `.context()` and
182/// `.with_context()` methods that map any compatible error into
183/// `ControlFlow::Err` with the given message.
184///
185/// # Implemented for
186///
187/// * `Result<T, E>` where `E: Into<anyhow::Error>` – wraps the error with context and converts it
188///   to `ControlFlow::Err`.
189/// * `Option<T>` – produces `ControlFlow::Err` when `None`.
190///
191/// # Examples
192///
193/// ```ignore
194/// use crate::expr::ControlFlowExt;
195///
196/// // Convert a Result<T, E> into FlowResult<T> with context:
197/// let key = encode_key(data).context("Failed to encode scan key")?;
198///
199/// // Unwrap an Option<T> into FlowResult<T> with context:
200/// let table = opt_table.context("Referencing table is required")?;
201/// ```
202pub trait ControlFlowExt<T> {
203	/// Wrap the error value with additional context, converting it into a
204	/// `ControlFlow::Err`.
205	fn context<C: std::fmt::Display + Send + Sync + 'static>(
206		self,
207		context: C,
208	) -> std::result::Result<T, ControlFlow>;
209
210	/// Wrap the error value with lazily-evaluated context, converting it into
211	/// a `ControlFlow::Err`.
212	#[allow(dead_code)] // Part of the public API; matches anyhow::Context.
213	fn with_context<C, F>(self, f: F) -> std::result::Result<T, ControlFlow>
214	where
215		C: std::fmt::Display + Send + Sync + 'static,
216		F: FnOnce() -> C;
217}
218
219impl<T, E: Into<anyhow::Error>> ControlFlowExt<T> for std::result::Result<T, E> {
220	fn context<C: std::fmt::Display + Send + Sync + 'static>(
221		self,
222		context: C,
223	) -> std::result::Result<T, ControlFlow> {
224		self.map_err(|e| ControlFlow::Err(e.into().context(context)))
225	}
226
227	fn with_context<C, F>(self, f: F) -> std::result::Result<T, ControlFlow>
228	where
229		C: std::fmt::Display + Send + Sync + 'static,
230		F: FnOnce() -> C,
231	{
232		self.map_err(|e| ControlFlow::Err(e.into().context(f())))
233	}
234}
235
236impl<T> ControlFlowExt<T> for Option<T> {
237	fn context<C: std::fmt::Display + Send + Sync + 'static>(
238		self,
239		context: C,
240	) -> std::result::Result<T, ControlFlow> {
241		self.ok_or_else(|| ControlFlow::Err(anyhow::anyhow!("{}", context)))
242	}
243
244	fn with_context<C, F>(self, f: F) -> std::result::Result<T, ControlFlow>
245	where
246		C: std::fmt::Display + Send + Sync + 'static,
247		F: FnOnce() -> C,
248	{
249		self.ok_or_else(|| ControlFlow::Err(anyhow::anyhow!("{}", f())))
250	}
251}