uqa_graph/cypher/
labels.rs1pub fn validate_default_label_relations(
11 store: &crate::GraphStoreHandle,
12 graph: &str,
13 query: &super::CypherQuery,
14) -> Result<(), super::CypherError> {
15 use super::CypherError;
16 let labels = store
17 .graph_labels(graph)
18 .map_err(|error| CypherError::Storage(error.to_string()))?;
19 let (requires_vertex, requires_edge) = label_requirements(query);
20 for (required, kind) in [
21 (requires_vertex, crate::LabelKind::Vertex),
22 (requires_edge, crate::LabelKind::Edge),
23 ] {
24 if required
25 && !labels
26 .iter()
27 .any(|label| label.id == kind.default_label_id())
28 {
29 return Err(CypherError::MissingLabelRelation(format!(
30 "{graph}.{}",
31 kind.default_label_name()
32 )));
33 }
34 }
35 Ok(())
36}
37
38fn mark_path_requirements(path: &super::PathPattern, required: &mut (bool, bool)) {
39 use super::PathElement;
40
41 for element in &path.elements {
42 match element {
43 PathElement::Node(node) => {
44 required.0 = true;
45 if let Some(properties) = &node.properties {
46 for expression in properties.values() {
47 mark_expression_requirements(expression, required);
48 }
49 }
50 }
51 PathElement::Rel(relation) => {
52 required.1 = true;
53 if let Some(properties) = &relation.properties {
54 for expression in properties.values() {
55 mark_expression_requirements(expression, required);
56 }
57 }
58 }
59 }
60 }
61}
62
63fn mark_expression_requirements(expression: &super::CypherExpr, required: &mut (bool, bool)) {
64 use super::CypherExpr;
65
66 match expression {
67 CypherExpr::FunctionCall(call) => {
68 for argument in &call.args {
69 mark_expression_requirements(argument, required);
70 }
71 }
72 CypherExpr::BinaryOp(binary) => {
73 mark_expression_requirements(&binary.left, required);
74 mark_expression_requirements(&binary.right, required);
75 }
76 CypherExpr::UnaryOp(unary) => {
77 mark_expression_requirements(&unary.operand, required);
78 }
79 CypherExpr::ListIndex(index) => {
80 mark_expression_requirements(&index.expr, required);
81 mark_expression_requirements(&index.index, required);
82 }
83 CypherExpr::ListSlice(slice) => {
84 mark_expression_requirements(&slice.expr, required);
85 if let Some(start) = &slice.start {
86 mark_expression_requirements(start, required);
87 }
88 if let Some(end) = &slice.end {
89 mark_expression_requirements(end, required);
90 }
91 }
92 CypherExpr::ListComprehension(comprehension) => {
93 mark_expression_requirements(&comprehension.list_expr, required);
94 if let Some(filter) = &comprehension.filter {
95 mark_expression_requirements(filter, required);
96 }
97 if let Some(map) = &comprehension.map_expr {
98 mark_expression_requirements(map, required);
99 }
100 }
101 CypherExpr::InList(list) => {
102 mark_expression_requirements(&list.expr, required);
103 mark_expression_requirements(&list.list_expr, required);
104 }
105 CypherExpr::IsNull(null) => mark_expression_requirements(&null.expr, required),
106 CypherExpr::IsNotNull(not_null) => {
107 mark_expression_requirements(¬_null.expr, required);
108 }
109 CypherExpr::CaseExpr(case) => {
110 if let Some(operand) = &case.operand {
111 mark_expression_requirements(operand, required);
112 }
113 for (condition, result) in &case.whens {
114 mark_expression_requirements(condition, required);
115 mark_expression_requirements(result, required);
116 }
117 if let Some(else_expression) = &case.else_expr {
118 mark_expression_requirements(else_expression, required);
119 }
120 }
121 CypherExpr::ListLiteral(list) => {
122 for element in &list.elements {
123 mark_expression_requirements(element, required);
124 }
125 }
126 CypherExpr::MapLiteral(map) => {
127 for (_, value) in &map.pairs {
128 mark_expression_requirements(value, required);
129 }
130 }
131 CypherExpr::ExistsPattern(path) => mark_path_requirements(path, required),
132 CypherExpr::PropertyAccess(_)
133 | CypherExpr::Parameter(_)
134 | CypherExpr::Literal(_)
135 | CypherExpr::Variable(_) => {}
136 }
137}
138
139fn mark_return_requirements(
140 items: &[super::ReturnItem],
141 order_by: Option<&[super::OrderByItem]>,
142 skip: Option<&super::CypherExpr>,
143 limit: Option<&super::CypherExpr>,
144 required: &mut (bool, bool),
145) {
146 for item in items {
147 mark_expression_requirements(&item.expr, required);
148 }
149 for item in order_by.into_iter().flatten() {
150 mark_expression_requirements(&item.expr, required);
151 }
152 if let Some(skip) = skip {
153 mark_expression_requirements(skip, required);
154 }
155 if let Some(limit) = limit {
156 mark_expression_requirements(limit, required);
157 }
158}
159
160fn label_requirements(query: &super::CypherQuery) -> (bool, bool) {
161 use super::CypherClause;
162
163 let mut required = (false, false);
164 for clause in &query.clauses {
165 match clause {
166 CypherClause::Match(clause) => {
167 for path in &clause.patterns {
168 mark_path_requirements(path, &mut required);
169 }
170 if let Some(filter) = &clause.r#where {
171 mark_expression_requirements(filter, &mut required);
172 }
173 }
174 CypherClause::Create(clause) => {
175 for path in &clause.patterns {
176 mark_path_requirements(path, &mut required);
177 }
178 }
179 CypherClause::Merge(clause) => {
180 mark_path_requirements(&clause.pattern, &mut required);
181 for item in clause
182 .on_create_set
183 .iter()
184 .chain(&clause.on_match_set)
185 .flatten()
186 {
187 mark_expression_requirements(&item.target, &mut required);
188 mark_expression_requirements(&item.value, &mut required);
189 }
190 }
191 CypherClause::Set(clause) => {
192 for item in &clause.items {
193 mark_expression_requirements(&item.target, &mut required);
194 mark_expression_requirements(&item.value, &mut required);
195 }
196 }
197 CypherClause::Delete(clause) => {
198 for expression in &clause.expressions {
199 mark_expression_requirements(expression, &mut required);
200 }
201 }
202 CypherClause::Return(clause) => mark_return_requirements(
203 &clause.items,
204 clause.order_by.as_deref(),
205 clause.skip.as_ref(),
206 clause.limit.as_ref(),
207 &mut required,
208 ),
209 CypherClause::With(clause) => {
210 mark_return_requirements(
211 &clause.items,
212 clause.order_by.as_deref(),
213 clause.skip.as_ref(),
214 clause.limit.as_ref(),
215 &mut required,
216 );
217 if let Some(filter) = &clause.r#where {
218 mark_expression_requirements(filter, &mut required);
219 }
220 }
221 CypherClause::Unwind(clause) => {
222 mark_expression_requirements(&clause.expr, &mut required);
223 }
224 }
225 }
226 required
227}
228
229#[cfg(test)]
230mod tests;