1use revision::{DeserializeRevisioned, Revisioned, SerializeRevisioned};
2use surrealdb_types::{RecordId, SqlFormat, ToSql};
3
4use super::SleepStatement;
5use crate::expr::closure::ClosureExpr;
6use crate::expr::statements::info::InfoStructure;
7use crate::expr::statements::{
8 AlterStatement, CreateStatement, DefineStatement, DeleteStatement, ForeachStatement,
9 IfelseStatement, InfoStatement, InsertStatement, OutputStatement, RebuildStatement,
10 RelateStatement, RemoveStatement, SelectStatement, SetStatement, UpdateStatement,
11 UpsertStatement,
12};
13use crate::expr::{
14 BinaryOperator, Block, Constant, FunctionCall, Idiom, Literal, Mock, ObjectEntry, Param,
15 PostfixOperator, PrefixOperator, RecordIdKeyLit, RecordIdLit,
16};
17use crate::types::PublicValue;
18use crate::val::table_name_public::IntoTableName;
19use crate::val::{TableName, Value};
20
21#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
22pub enum ExplainFormat {
23 #[default]
24 Text,
25 Json,
26}
27
28#[derive(Clone, Debug, PartialEq, Eq, Hash)]
29pub enum Expr {
30 Literal(Literal),
31 Param(Param),
32 Idiom(Idiom),
33 Table(TableName),
35 Mock(Mock),
37 Block(Box<Block>),
38 Constant(Constant),
39 Prefix {
40 op: PrefixOperator,
41 expr: Box<Expr>,
42 },
43 Postfix {
44 expr: Box<Expr>,
45 op: PostfixOperator,
46 },
47 Binary {
48 left: Box<Expr>,
49 op: BinaryOperator,
50 right: Box<Expr>,
51 },
52 FunctionCall(Box<FunctionCall>),
54
55 Closure(Box<ClosureExpr>),
56
57 Break,
58 Continue,
59 Return(Box<OutputStatement>),
60 Throw(Box<Expr>),
61
62 IfElse(Box<IfelseStatement>),
63 Select(Box<SelectStatement>),
64 Create(Box<CreateStatement>),
65 Update(Box<UpdateStatement>),
66 Upsert(Box<UpsertStatement>),
67 Delete(Box<DeleteStatement>),
68 Relate(Box<RelateStatement>),
69 Insert(Box<InsertStatement>),
70 Define(Box<DefineStatement>),
71 Remove(Box<RemoveStatement>),
72 Rebuild(Box<RebuildStatement>),
73 Alter(Box<AlterStatement>),
74 Info(Box<InfoStatement>),
75 Foreach(Box<ForeachStatement>),
76 Let(Box<SetStatement>),
77 Sleep(Box<SleepStatement>),
78 Explain {
79 format: ExplainFormat,
80 analyze: bool,
81 statement: Box<Expr>,
82 },
83 Match(Box<crate::expr::match_plan::MatchPlan>),
90}
91
92impl Expr {
93 pub fn read_only(&self) -> bool {
95 match self {
96 Expr::Param(_)
97 | Expr::Table(_)
98 | Expr::Mock(_)
99 | Expr::Constant(_)
100 | Expr::Break
101 | Expr::Continue
102 | Expr::Sleep(_) => true,
103
104 Expr::Info(info) => info.read_only(),
105
106 Expr::Literal(l) => l.read_only(),
108
109 Expr::Idiom(x) => x.read_only(),
110 Expr::Block(block) => block.read_only(),
111 Expr::Prefix {
112 expr,
113 ..
114 } => expr.read_only(),
115 Expr::Postfix {
116 expr,
117 op,
118 } => {
119 match op {
120 PostfixOperator::Call(args) => {
124 matches!(&**expr, Expr::Closure(_))
125 && expr.read_only() && args.iter().all(Expr::read_only)
126 }
127 PostfixOperator::MethodCall(name, args) => {
133 crate::expr::method::is_builtin_method(name)
134 && expr.read_only() && args.iter().all(Expr::read_only)
135 }
136 PostfixOperator::Range | PostfixOperator::RangeSkip => expr.read_only(),
137 }
138 }
139 Expr::Binary {
140 left,
141 right,
142 ..
143 } => left.read_only() && right.read_only(),
144 Expr::FunctionCall(function) => function.read_only(),
145 Expr::Return(expr) => expr.read_only(),
146 Expr::Throw(expr) => expr.read_only(),
147 Expr::IfElse(s) => s.read_only(),
148 Expr::Select(s) => s.read_only(),
149 Expr::Let(s) => s.read_only(),
150 Expr::Foreach(s) => s.read_only(),
151 Expr::Explain {
152 statement,
153 ..
154 } => statement.read_only(),
155 Expr::Closure(c) => c.body.read_only(),
159 Expr::Match(plan) => !plan.has_mutations(),
162 Expr::Create(_)
163 | Expr::Update(_)
164 | Expr::Delete(_)
165 | Expr::Relate(_)
166 | Expr::Insert(_)
167 | Expr::Define(_)
168 | Expr::Remove(_)
169 | Expr::Rebuild(_)
170 | Expr::Upsert(_)
171 | Expr::Alter(_) => false,
172 }
173 }
174
175 pub fn from_public_value(value: PublicValue) -> Self {
176 match value {
177 surrealdb_types::Value::None => Expr::Literal(Literal::None),
178 surrealdb_types::Value::Null => Expr::Literal(Literal::Null),
179 surrealdb_types::Value::Bool(b) => Expr::Literal(Literal::Bool(b)),
180 surrealdb_types::Value::Number(n) => match n {
181 surrealdb_types::Number::Int(i) => Expr::Literal(Literal::Integer(i)),
182 surrealdb_types::Number::Float(f) => Expr::Literal(Literal::Float(f)),
183 surrealdb_types::Number::Decimal(d) => Expr::Literal(Literal::Decimal(d)),
184 },
185 surrealdb_types::Value::String(s) => Expr::Literal(Literal::String(s.into())),
186 surrealdb_types::Value::Bytes(b) => {
187 Expr::Literal(Literal::Bytes(crate::val::Bytes(b.into_inner())))
188 }
189 surrealdb_types::Value::Duration(d) => {
190 Expr::Literal(Literal::Duration(crate::val::Duration(d.into_inner())))
191 }
192 surrealdb_types::Value::Datetime(d) => {
193 Expr::Literal(Literal::Datetime(crate::val::Datetime(d.into_inner())))
194 }
195 surrealdb_types::Value::Uuid(u) => {
196 Expr::Literal(Literal::Uuid(crate::val::Uuid(u.into_inner())))
197 }
198 surrealdb_types::Value::Array(a) => {
199 Expr::Literal(Literal::Array(a.into_iter().map(Expr::from_public_value).collect()))
200 }
201 surrealdb_types::Value::Set(s) => {
202 Expr::Literal(Literal::Array(s.into_iter().map(Expr::from_public_value).collect()))
203 }
204 surrealdb_types::Value::Object(o) => Expr::Literal(Literal::Object(
205 o.into_iter()
206 .map(|(k, v)| ObjectEntry {
207 key: k.into(),
208 value: Expr::from_public_value(v),
209 })
210 .collect(),
211 )),
212 surrealdb_types::Value::Table(t) => Expr::Table(t.into_table_name()),
213 surrealdb_types::Value::RecordId(RecordId {
214 table,
215 key,
216 }) => {
217 let key_lit = match key {
218 surrealdb_types::RecordIdKey::Number(n) => RecordIdKeyLit::Number(n),
219 surrealdb_types::RecordIdKey::String(s) => RecordIdKeyLit::String(s.into()),
220 surrealdb_types::RecordIdKey::Uuid(u) => {
221 RecordIdKeyLit::Uuid(crate::val::Uuid(u.into_inner()))
222 }
223 surrealdb_types::RecordIdKey::Array(a) => {
224 RecordIdKeyLit::Array(a.into_iter().map(Expr::from_public_value).collect())
225 }
226 surrealdb_types::RecordIdKey::Object(o) => RecordIdKeyLit::Object(
227 o.into_iter()
228 .map(|(k, v)| ObjectEntry {
229 key: k.into(),
230 value: Expr::from_public_value(v),
231 })
232 .collect(),
233 ),
234 _ => return Expr::Literal(Literal::None), };
236 Expr::Literal(Literal::RecordId(RecordIdLit {
237 table: table.into_table_name(),
238 key: key_lit,
239 }))
240 }
241 surrealdb_types::Value::Geometry(g) => Expr::Literal(Literal::Geometry(g.into())),
242 surrealdb_types::Value::File(f) => {
243 Expr::Literal(Literal::File(crate::val::File::new(f.bucket, f.key)))
244 }
245 surrealdb_types::Value::Range(r) => Expr::from(*r),
246 surrealdb_types::Value::Regex(r) => {
247 Expr::Literal(Literal::Regex(crate::val::Regex(r.into_inner())))
248 }
249 }
250 }
251}
252
253impl From<surrealdb_types::Range> for Expr {
254 fn from(r: surrealdb_types::Range) -> Self {
255 use std::ops::Bound;
256 match r.into_inner() {
257 (Bound::Unbounded, Bound::Unbounded) => Expr::Literal(Literal::UnboundedRange),
259 (Bound::Unbounded, Bound::Excluded(end)) => Expr::Prefix {
261 op: PrefixOperator::Range,
262 expr: Box::new(Expr::from_public_value(end)),
263 },
264 (Bound::Unbounded, Bound::Included(end)) => Expr::Prefix {
265 op: PrefixOperator::RangeInclusive,
266 expr: Box::new(Expr::from_public_value(end)),
267 },
268 (Bound::Included(start), Bound::Excluded(end)) => Expr::Binary {
270 left: Box::new(Expr::from_public_value(start)),
271 op: BinaryOperator::Range,
272 right: Box::new(Expr::from_public_value(end)),
273 },
274 (Bound::Included(start), Bound::Included(end)) => Expr::Binary {
275 left: Box::new(Expr::from_public_value(start)),
276 op: BinaryOperator::RangeInclusive,
277 right: Box::new(Expr::from_public_value(end)),
278 },
279 (Bound::Excluded(start), Bound::Excluded(end)) => Expr::Binary {
281 left: Box::new(Expr::from_public_value(start)),
282 op: BinaryOperator::RangeSkip,
283 right: Box::new(Expr::from_public_value(end)),
284 },
285 (Bound::Excluded(start), Bound::Included(end)) => Expr::Binary {
286 left: Box::new(Expr::from_public_value(start)),
287 op: BinaryOperator::RangeSkipInclusive,
288 right: Box::new(Expr::from_public_value(end)),
289 },
290 (Bound::Excluded(_), Bound::Unbounded) | (Bound::Included(_), Bound::Unbounded) => {
293 Expr::Literal(Literal::None)
294 }
295 }
296 }
297}
298
299impl Expr {
300 pub fn as_static_value(&self) -> Option<crate::val::Value> {
311 match self {
312 Expr::Literal(literal) => literal.as_static_value(),
313 _ => None,
314 }
315 }
316
317 pub fn is_static(&self) -> bool {
319 match self {
320 Expr::Literal(literal) => literal.is_static(),
321 Expr::Constant(_) => true,
322 Expr::Prefix {
323 expr,
324 ..
325 } => expr.is_static(),
326 Expr::Postfix {
327 expr,
328 ..
329 } => expr.is_static(),
330 Expr::Binary {
331 left,
332 right,
333 ..
334 } => left.is_static() && right.is_static(),
335 Expr::FunctionCall(x) => {
336 x.arguments.iter().all(|x| x.is_static())
341 }
342 Expr::Param(_)
343 | Expr::Idiom(_)
344 | Expr::Table(_)
345 | Expr::Mock(_)
346 | Expr::Block(_)
347 | Expr::Closure(_)
348 | Expr::Break
349 | Expr::Continue
350 | Expr::Return(_)
351 | Expr::Throw(_)
352 | Expr::IfElse(_)
353 | Expr::Select(_)
354 | Expr::Create(_)
355 | Expr::Update(_)
356 | Expr::Delete(_)
357 | Expr::Relate(_)
358 | Expr::Insert(_)
359 | Expr::Define(_)
360 | Expr::Remove(_)
361 | Expr::Rebuild(_)
362 | Expr::Upsert(_)
363 | Expr::Alter(_)
364 | Expr::Info(_)
365 | Expr::Foreach(_)
366 | Expr::Let(_)
367 | Expr::Sleep(_)
368 | Expr::Explain {
369 ..
370 } => false,
371 Expr::Match(_) => false,
373 }
374 }
375
376 pub fn to_idiom(&self) -> Idiom {
377 match self {
378 Expr::Idiom(i) => i.simplify(),
379 Expr::Param(i) => Idiom::field(i.clone().into_strand()),
380 Expr::FunctionCall(x) => x.receiver.to_idiom(),
381 Expr::Literal(l) => match l {
382 Literal::String(s) => Idiom::field(s.clone()),
383 Literal::Datetime(d) => Idiom::field(d.to_string()),
384 x => Idiom::field(x.to_sql()),
385 },
386 x => Idiom::field(x.to_sql()),
387 }
388 }
389
390 pub fn to_raw_string(&self) -> String {
391 match self {
392 Expr::Idiom(idiom) => idiom.to_raw_string(),
393 Expr::Table(ident) => ident.as_str().to_string(),
394 _ => self.to_sql(),
395 }
396 }
397
398 #[allow(dead_code)]
402 fn needs_parentheses(&self) -> bool {
403 match self {
404 Expr::Literal(Literal::UnboundedRange | Literal::RecordId(_))
405 | Expr::Closure(_)
406 | Expr::Break
407 | Expr::Continue
408 | Expr::Throw(_)
409 | Expr::Return(_)
410 | Expr::IfElse(_)
411 | Expr::Select(_)
412 | Expr::Create(_)
413 | Expr::Update(_)
414 | Expr::Delete(_)
415 | Expr::Relate(_)
416 | Expr::Insert(_)
417 | Expr::Define(_)
418 | Expr::Remove(_)
419 | Expr::Rebuild(_)
420 | Expr::Upsert(_)
421 | Expr::Alter(_)
422 | Expr::Info(_)
423 | Expr::Foreach(_)
424 | Expr::Let(_)
425 | Expr::Sleep(_)
426 | Expr::Explain {
427 ..
428 } => true,
429
430 Expr::Match(_) => true,
433
434 Expr::Literal(_)
435 | Expr::Param(_)
436 | Expr::Idiom(_)
437 | Expr::Table(_)
438 | Expr::Mock(_)
439 | Expr::Block(_)
440 | Expr::Constant(_)
441 | Expr::Prefix {
442 ..
443 }
444 | Expr::Postfix {
445 ..
446 }
447 | Expr::Binary {
448 ..
449 }
450 | Expr::FunctionCall(_) => false,
451 }
452 }
453}
454
455impl ToSql for Expr {
456 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
457 if let Expr::Match(plan) = self {
461 plan.fmt_sql(f, fmt);
462 return;
463 }
464 let sql_expr: crate::sql::Expr = self.clone().into();
465 sql_expr.fmt_sql(f, fmt);
466 }
467}
468
469impl Expr {
470 pub fn to_stored_sql(&self) -> String {
481 let sql_expr: crate::sql::Expr = self.clone().into();
482 crate::sql::CoverStmts(&sql_expr).to_sql()
483 }
484}
485
486impl InfoStructure for Expr {
487 fn structure(self) -> Value {
488 self.to_sql().into()
489 }
490}
491
492impl Revisioned for Expr {
493 fn revision() -> u16 {
494 1
495 }
496}
497
498impl SerializeRevisioned for Expr {
499 fn serialize_revisioned<W: std::io::Write>(
500 &self,
501 writer: &mut W,
502 ) -> Result<(), revision::Error> {
503 if matches!(self, Expr::Match(_)) {
513 tracing::error!(
514 "Expr::Match reached Revisioned serialization; it must never enter a \
515 sql::Ast, the catalog, or Revisioned serialization"
516 );
517 debug_assert!(false, "Expr::Match must not be Revisioned-serialized");
518 }
519 SerializeRevisioned::serialize_revisioned(&self.to_sql(), writer)
520 }
521}
522
523impl DeserializeRevisioned for Expr {
524 fn deserialize_revisioned<R: std::io::Read>(reader: &mut R) -> Result<Self, revision::Error> {
525 let query: String = DeserializeRevisioned::deserialize_revisioned(reader)?;
526
527 let expr = crate::syn::parse_with_settings(
528 query.as_bytes(),
529 crate::syn::parser::ParserSettings::STORED_TEXT,
532 async |p, stk| {
538 let expr = p.parse_expr(stk).await?;
539 p.assert_finished()?;
540 Ok(expr)
541 },
542 )
543 .map_err(|err| revision::Error::Conversion(err.to_string()))?;
544 Ok(expr.into())
545 }
546}
547
548impl revision::SkipRevisioned for Expr {
549 fn skip_revisioned<R: std::io::Read>(reader: &mut R) -> Result<(), revision::Error> {
550 <String as revision::SkipRevisioned>::skip_revisioned(reader)
553 }
554}
555
556impl revision::WalkRevisioned for Expr {
557 type Walker<'r, R: revision::BorrowedReader + 'r> = revision::LeafWalker<'r, Expr, R>;
558
559 fn walk_revisioned<'r, R: revision::BorrowedReader>(
560 reader: &'r mut R,
561 ) -> Result<Self::Walker<'r, R>, revision::Error> {
562 Ok(revision::LeafWalker::new(reader))
563 }
564}
565
566impl revision::LengthPrefixedBytes for Expr {}
567
568#[cfg(test)]
569mod length_prefixed_bytes_tests {
570 use revision::{SerializeRevisioned, WalkRevisioned};
571 use surrealdb_types::ToSql;
572
573 use crate::expr::Expr;
574 use crate::expr::literal::Literal;
575
576 #[test]
577 fn expr_with_bytes_matches_serialize() {
578 let expr = Expr::Literal(Literal::Integer(42));
579 let mut bytes = Vec::new();
580 expr.serialize_revisioned(&mut bytes).unwrap();
581 let wire_text = expr.to_sql();
582 let mut r = bytes.as_slice();
583 let walker = Expr::walk_revisioned(&mut r).unwrap();
584 let observed = walker.with_bytes(|raw| raw.to_vec()).unwrap();
585 assert_eq!(observed.as_slice(), wire_text.as_bytes());
586 assert!(r.is_empty());
587 }
588}