surrealdb_expr/expr/statements/live.rs
1use anyhow::Result;
2use uuid::Uuid;
3
4use crate::expr::visit::{Visit, Visitor};
5use crate::expr::{Cond, Expr, Fetchs, Fields, Idiom, Param};
6
7/// The set of parameter names that carry per-event document data and are therefore
8/// only meaningful at notification time, not at LIVE query registration time.
9const DOC_PARAMS: &[&str] = &["value", "before", "after", "event", "this"];
10
11/// Visitor that short-circuits with `Err(())` on the first expression node that
12/// depends on the document being processed:
13///
14/// - Any field-access [`Idiom`] (e.g. `name`, `user.age`).
15/// - Any of the live-query event params: `$value`, `$before`, `$after`, `$event`, `$this`.
16///
17/// Session params (`$access`, `$auth`, `$token`, `$session`) and user-defined LET
18/// params are captured at registration time and treated as constants.
19///
20/// If the visitor completes without returning `Err`, the expression is
21/// "document-independent": its result is the same for every record that triggers
22/// the LIVE query, so it can be evaluated once at registration time to catch
23/// errors early.
24struct IsDocumentDependentChecker;
25
26impl Visitor for IsDocumentDependentChecker {
27 /// `()` is used as an early-termination signal — `Err(())` means "found a
28 /// document-dependent node, stop traversal".
29 type Error = ();
30
31 fn visit_idiom(&mut self, _idiom: &Idiom) -> Result<(), ()> {
32 Err(()) // any field access → document-dependent, stop
33 }
34
35 fn visit_param(&mut self, param: &Param) -> Result<(), ()> {
36 if DOC_PARAMS.contains(¶m.as_str()) {
37 Err(()) // document-event param → document-dependent, stop
38 } else {
39 Ok(())
40 }
41 }
42}
43
44/// Returns `true` if `expr` contains any field-access idiom or document-event
45/// param, meaning its result can differ per-document and it cannot be safely
46/// evaluated at LIVE query registration time.
47pub fn is_document_dependent(expr: &Expr) -> bool {
48 expr.visit(&mut IsDocumentDependentChecker).is_err()
49}
50
51#[derive(Clone, Debug, Eq, PartialEq, Hash)]
52pub enum LiveFields {
53 Diff,
54 Select(Fields),
55}
56
57#[derive(Clone, Debug, Eq, PartialEq, Hash)]
58pub struct LiveStatement {
59 pub id: Uuid,
60 pub node: Uuid,
61 pub fields: LiveFields,
62 pub what: Expr,
63 pub cond: Option<Cond>,
64 pub fetch: Option<Fetchs>,
65}