marsdb_query/ast.rs
1#[derive(Debug, Clone, PartialEq)]
2pub enum Literal {
3 Int(i64),
4 Float(f64),
5 String(String),
6 Bool(bool),
7 Null,
8 /// `$name` placeholder — resolved to a concrete `Literal` by
9 /// `params::substitute_params` before execution, never seen by the
10 /// executor.
11 Param(String),
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct PropAccess {
16 pub var: String,
17 pub prop: String,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum CompareOp {
22 Eq,
23 Ne,
24 Lt,
25 Le,
26 Gt,
27 Ge,
28}
29
30#[derive(Debug, Clone)]
31pub enum Expr {
32 And(Box<Expr>, Box<Expr>),
33 Or(Box<Expr>, Box<Expr>),
34 Not(Box<Expr>),
35 Compare(PropAccess, CompareOp, Literal),
36 /// Does the node bound to `var` have label `label` among its (possibly
37 /// multiple) labels? Synthesized by the planner for the 2nd+ label in a
38 /// multi-label pattern like `(n:Post:Message)` — never user-typed.
39 HasLabel(String, String),
40 /// Do these two row bindings refer to the same node/edge? Synthesized
41 /// by the planner when a pattern's hop variable is a "bound-node
42 /// repetition" — the same variable already bound earlier reappearing
43 /// mid-pattern (e.g. IS7's `p`, bound by an earlier MATCH, reappearing
44 /// as the endpoint of an OPTIONAL MATCH pattern: `(a)-[r:KNOWS]-(p)`
45 /// must mean "KNOWS *this* `p`", not "KNOWS anyone"). Never user-typed.
46 VarEq(String, String),
47}
48
49#[derive(Debug, Clone)]
50pub enum ReturnExpr {
51 Var(String),
52 Prop(PropAccess),
53 Lit(Literal),
54 Call(String, Vec<ReturnExpr>),
55 /// Simple/value `CASE`: `CASE <test> WHEN <value> THEN <result> ... [ELSE
56 /// <else>] END`. `test` is `Some` for every form the parser currently
57 /// produces; kept `Option` so a future searched-`CASE` (`CASE WHEN
58 /// <bool_expr> THEN ...`) can reuse this variant without another type
59 /// change.
60 Case {
61 test: Option<Box<ReturnExpr>>,
62 whens: Vec<(ReturnExpr, ReturnExpr)>,
63 else_: Option<Box<ReturnExpr>>,
64 },
65}
66
67#[derive(Debug, Clone)]
68pub struct ReturnItem {
69 pub expr: ReturnExpr,
70 pub alias: Option<String>,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum SortDir {
75 Asc,
76 Desc,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum RelDirection {
81 /// (a)-[..]->(b)
82 Right,
83 /// (a)<-[..]-(b)
84 Left,
85 /// (a)-[..]-(b) — matches either direction.
86 Either,
87}
88
89#[derive(Debug, Clone)]
90pub struct NodePattern {
91 pub var: Option<String>,
92 pub labels: Vec<String>,
93 pub props: Vec<(String, Literal)>,
94}
95
96#[derive(Debug, Clone)]
97pub struct RelPattern {
98 pub var: Option<String>,
99 pub rel_type: Option<String>,
100 #[allow(dead_code)] // property-map on relationships parsed but not filtered on in v1
101 pub props: Vec<(String, Literal)>,
102 pub direction: RelDirection,
103 /// `[:TYPE*min..max]` — `None` means a fixed single hop (existing
104 /// behavior). `max: None` means unbounded, capped at a safety depth by
105 /// the executor.
106 pub hop_range: Option<(u32, Option<u32>)>,
107}
108
109/// A linear chain: node, (rel, node)*.
110#[derive(Debug, Clone)]
111pub struct Pattern {
112 pub start: NodePattern,
113 pub hops: Vec<(RelPattern, NodePattern)>,
114}
115
116#[derive(Debug, Clone)]
117pub enum Tail {
118 Return(Vec<ReturnItem>),
119 Delete(Vec<String>),
120 DetachDelete(Vec<String>),
121 Set(Vec<(PropAccess, Literal)>),
122}
123
124/// A `WITH` clause: projects/renames the current bindings, optionally
125/// filtered/sorted/limited at that boundary, and becomes the binding scope
126/// for whatever follows (the next `QueryPart`, or the final `Tail`).
127#[derive(Debug, Clone)]
128pub struct WithClause {
129 pub items: Vec<ReturnItem>,
130 pub order_by: Option<Vec<(ReturnExpr, SortDir)>>,
131 pub limit: Option<i64>,
132}
133
134/// One `MATCH <pattern>[, <pattern>...] [WHERE ...] [WITH ...]` segment.
135/// Comma-separated patterns within one part are spliced into a single
136/// linear `Pattern` at parse time (see `parser::splice_patterns`) — this
137/// only ever holds one already-combined `Pattern`, not several.
138#[derive(Debug, Clone)]
139pub struct QueryPart {
140 pub optional: bool,
141 pub pattern: Pattern,
142 pub where_clause: Option<Expr>,
143 pub with: Option<WithClause>,
144}
145
146#[derive(Debug, Clone)]
147pub enum Statement {
148 Create(Vec<Pattern>),
149 Match {
150 /// One or more `MATCH ... [WITH ...]` segments. The parser enforces
151 /// every part except the last has a `with` (matching real Cypher's
152 /// rule that multiple reading clauses must be separated by WITH)
153 /// and that at most one part has a `with` at all (v1 doesn't
154 /// support chaining past one WITH boundary — nothing in the target
155 /// query set needs it, and it keeps a hand-rolled parser's
156 /// untested-path surface smaller).
157 parts: Vec<QueryPart>,
158 tail: Tail,
159 /// Only meaningful for `Tail::Return`; evaluated against the
160 /// projected/aliased output row, not the raw pattern bindings —
161 /// every ORDER BY key in practice is a RETURN alias, not a bare
162 /// pattern variable.
163 order_by: Option<Vec<(ReturnExpr, SortDir)>>,
164 limit: Option<i64>,
165 },
166}