Line data Source code
1 : //! Property graphs and graph queries.
2 :
3 : use dashql_parser::attrs;
4 : use super::{is_true, FmtReg, Formatter, FormattingJoinPolicy, K};
5 : use dashql_buffers::parser::{Node, NodeType};
6 :
7 : /// Binding strength of `A | B`
8 : const GRAPH_LABEL_DISJUNCTION: u8 = 1;
9 : /// Binding strength of `A & B`
10 : const GRAPH_LABEL_CONJUNCTION: u8 = 2;
11 : /// Binding strength of `!A`
12 : const GRAPH_LABEL_NEGATION: u8 = 3;
13 : /// Binding strength of a label or the wildcard
14 : const GRAPH_LABEL_ATOM: u8 = 4;
15 :
16 : impl<'a> Formatter<'a> {
17 90 : pub(super) fn format_property_graph(&self, node: &'a Node) -> FmtReg {
18 90 : let f = &self.fmt;
19 90 : let [vertices, edges] =
20 90 : attrs!(self.children(node), [K::SQL_PROPERTY_GRAPH_VERTEX_TABLES, K::SQL_PROPERTY_GRAPH_EDGE_TABLES]);
21 90 : let mut sections = Vec::new();
22 90 : if let Some(vertices) = vertices.filter(|v| v.children_count() > 0) {
23 45 : sections.push(f.concat(&[f.text("vertex tables "), f.parenthesized(self.reg(vertices))]));
24 63 : }
25 90 : if let Some(edges) = edges.filter(|e| e.children_count() > 0) {
26 42 : sections.push(f.concat(&[f.text("edge tables "), f.parenthesized(self.reg(edges))]));
27 60 : }
28 90 : if sections.is_empty() {
29 : // An empty text, not the empty document
30 45 : return f.text("");
31 45 : }
32 45 : self.space_join(§ions, self.pretty_policy(FormattingJoinPolicy::BreakOnOverflow), false)
33 90 : }
34 :
35 135 : pub(super) fn format_graph_element_table(&self, node: &'a Node) -> FmtReg {
36 135 : let f = &self.fmt;
37 135 : let [table, key, source, destination, labels] = attrs!(self.children(node), [
38 : K::SQL_GRAPH_ELEMENT_TABLE_TABLE,
39 : K::SQL_GRAPH_ELEMENT_TABLE_KEY,
40 : K::SQL_GRAPH_ELEMENT_TABLE_SOURCE,
41 : K::SQL_GRAPH_ELEMENT_TABLE_DESTINATION,
42 : K::SQL_GRAPH_ELEMENT_TABLE_LABELS,
43 : ],
44 : );
45 135 : let Some(table) = table else { return self.format_unimplemented(node) };
46 135 : let [table_name, table_alias, table_query] =
47 135 : attrs!(self.children(table), [K::SQL_TABLEREF_NAME, K::SQL_TABLEREF_ALIAS, K::SQL_TABLEREF_TABLE]);
48 135 : let mut table_reg = self.reg(table);
49 135 : if let Some(table_alias) = table_alias {
50 42 : let mut base_reg = match (table_name, table_query) {
51 24 : (Some(table_name), _) => self.reg(table_name),
52 18 : (None, Some(table_query)) => self.reg(table_query),
53 0 : (None, None) => 0,
54 : };
55 42 : let alias_reg = self.reg(table_alias);
56 42 : if base_reg == 0 || alias_reg == 0 {
57 0 : return self.format_unimplemented(node);
58 42 : }
59 42 : if table_query.is_some_and(|q| q.node_type() == NodeType::OBJECT_SQL_SELECT) {
60 12 : base_reg = f.parenthesized(base_reg);
61 30 : }
62 42 : table_reg = f.concat(&[base_reg, f.text(" as "), alias_reg]);
63 93 : }
64 135 : let mut parts = vec![table_reg];
65 135 : if let Some(key) = key {
66 135 : parts.push(f.concat(&[f.text("key "), f.parenthesized(self.reg(key))]));
67 135 : }
68 135 : if let Some(source) = source {
69 48 : parts.push(f.concat(&[f.text("source "), self.reg(source)]));
70 87 : }
71 135 : if let Some(destination) = destination {
72 48 : parts.push(f.concat(&[f.text("destination "), self.reg(destination)]));
73 87 : }
74 135 : if let Some(labels) = labels {
75 135 : parts.extend(self.array_regs(labels));
76 135 : }
77 135 : self.space_join(&parts, self.pretty_policy(FormattingJoinPolicy::BreakOnOverflow), true)
78 135 : }
79 :
80 96 : pub(super) fn format_graph_vertex_reference(&self, node: &'a Node) -> FmtReg {
81 96 : let f = &self.fmt;
82 96 : let [name, key, columns] = attrs!(self.children(node), [
83 : K::SQL_GRAPH_VERTEX_REFERENCE_NAME,
84 : K::SQL_GRAPH_VERTEX_REFERENCE_KEY,
85 : K::SQL_GRAPH_VERTEX_REFERENCE_COLUMNS,
86 : ],
87 : );
88 96 : let Some(name) = name else { return self.format_unimplemented(node) };
89 96 : let Some(key) = key else { return self.reg(name) };
90 66 : let mut result =
91 66 : f.concat(&[f.text("key "), f.parenthesized(self.reg(key)), f.text(" references "), self.reg(name)]);
92 66 : if let Some(columns) = columns.filter(|c| c.children_count() > 0) {
93 18 : result = f.concat(&[result, f.parenthesized(self.reg(columns))]);
94 60 : }
95 66 : result
96 96 : }
97 :
98 138 : pub(super) fn format_graph_label(&self, node: &'a Node) -> FmtReg {
99 138 : let f = &self.fmt;
100 138 : let [name, is_default, properties] =
101 138 : attrs!(self.children(node), [K::SQL_GRAPH_LABEL_NAME, K::SQL_GRAPH_LABEL_DEFAULT, K::SQL_GRAPH_LABEL_PROPERTIES]);
102 138 : let Some(properties) = properties else { return self.format_unimplemented(node) };
103 138 : if name.is_none() && !is_true(is_default) {
104 0 : return self.format_unimplemented(node);
105 138 : }
106 138 : let label = match name {
107 75 : Some(name) => f.concat(&[f.text("label "), self.reg(name)]),
108 63 : None => f.text("default label"),
109 : };
110 138 : f.concat(&[label, f.text(" "), self.reg(properties)])
111 138 : }
112 :
113 138 : pub(super) fn format_graph_properties(&self, node: &'a Node) -> FmtReg {
114 138 : let f = &self.fmt;
115 138 : let [all, columns, exclude] = attrs!(self.children(node), [K::SQL_GRAPH_PROPERTIES_ALL, K::SQL_GRAPH_PROPERTIES_COLUMNS, K::SQL_GRAPH_PROPERTIES_EXCLUDE],
116 : );
117 138 : if is_true(all) {
118 78 : let mut result = f.text("properties are all columns");
119 78 : if let Some(exclude) = exclude.filter(|e| e.children_count() > 0) {
120 12 : result = f.concat(&[result, f.text(" except "), f.parenthesized(self.reg(exclude))]);
121 66 : }
122 78 : return result;
123 60 : }
124 60 : if let Some(columns) = columns {
125 48 : return f.concat(&[f.text("properties "), f.parenthesized(self.reg(columns))]);
126 12 : }
127 12 : f.text("no properties")
128 138 : }
129 :
130 78 : pub(super) fn format_graph_property(&self, node: &'a Node) -> FmtReg {
131 78 : let f = &self.fmt;
132 78 : let [value, name] = attrs!(self.children(node), [K::SQL_GRAPH_PROPERTY_VALUE, K::SQL_GRAPH_PROPERTY_NAME]);
133 78 : let Some(value) = value else { return self.format_unimplemented(node) };
134 78 : match name {
135 54 : Some(name) => f.concat(&[self.reg(value), f.text(" as "), self.reg(name)]),
136 24 : None => self.reg(value),
137 : }
138 78 : }
139 :
140 105 : pub(super) fn format_graph_table(&self, node: &'a Node) -> FmtReg {
141 105 : let f = &self.fmt;
142 105 : let [graph, graph_match, rows, columns] = attrs!(self.children(node), [K::SQL_GRAPH_TABLE_GRAPH, K::SQL_GRAPH_TABLE_MATCH, K::SQL_GRAPH_TABLE_ROWS, K::SQL_GRAPH_TABLE_COLUMNS],
143 : );
144 105 : let (Some(graph), Some(graph_match)) = (graph, graph_match) else { return self.format_unimplemented(node) };
145 105 : let mut clauses = vec![self.reg(graph), self.reg(graph_match)];
146 105 : if let Some(rows) = rows {
147 18 : clauses.push(self.reg(rows));
148 87 : }
149 105 : if let Some(columns) = columns {
150 99 : clauses.push(f.concat(&[f.text("columns "), f.parenthesized(self.reg(columns))]));
151 99 : }
152 105 : let body = self.space_join(&clauses, self.pretty_policy(FormattingJoinPolicy::BreakOnOverflow), true);
153 105 : if self.is_pretty() {
154 35 : return f.concat(&[
155 35 : f.text("graph_table("),
156 35 : f.indented(f.concat(&[f.break_(), body])),
157 35 : f.break_(),
158 35 : f.text(")"),
159 35 : ]);
160 70 : }
161 70 : f.concat(&[f.text("graph_table"), f.parenthesized(body)])
162 105 : }
163 :
164 105 : pub(super) fn format_graph_match(&self, node: &'a Node) -> FmtReg {
165 105 : let f = &self.fmt;
166 105 : let [patterns, where_clause] = attrs!(self.children(node), [K::SQL_GRAPH_MATCH_PATTERNS, K::SQL_GRAPH_MATCH_WHERE]);
167 105 : let Some(patterns) = patterns else { return self.format_unimplemented(node) };
168 105 : let result = f.concat(&[f.text("match "), self.reg(patterns)]);
169 105 : match where_clause {
170 18 : Some(where_clause) => f.concat(&[result, f.text(" where "), self.reg(where_clause)]),
171 87 : None => result,
172 : }
173 105 : }
174 :
175 318 : pub(super) fn format_graph_path_element(&self, node: &'a Node) -> FmtReg {
176 318 : let f = &self.fmt;
177 318 : let [vertex, left, right, any, variable, label, where_clause, quantifier] = attrs!(self.children(node), [
178 : K::SQL_GRAPH_PATH_ELEMENT_VERTEX,
179 : K::SQL_GRAPH_PATH_ELEMENT_EDGE_LEFT,
180 : K::SQL_GRAPH_PATH_ELEMENT_EDGE_RIGHT,
181 : K::SQL_GRAPH_PATH_ELEMENT_EDGE_ANY,
182 : K::SQL_GRAPH_PATH_ELEMENT_VARIABLE,
183 : K::SQL_GRAPH_PATH_ELEMENT_LABEL,
184 : K::SQL_GRAPH_PATH_ELEMENT_WHERE,
185 : K::SQL_GRAPH_PATH_ELEMENT_QUANTIFIER,
186 : ],
187 : );
188 318 : let mut body_parts = Vec::new();
189 318 : if let Some(variable) = variable {
190 237 : body_parts.push(self.reg(variable));
191 237 : }
192 318 : if let Some(label) = label {
193 216 : body_parts.push(f.concat(&[f.text(":"), self.reg(label)]));
194 234 : }
195 318 : if let Some(where_clause) = where_clause {
196 6 : body_parts.push(f.concat(&[f.text("where "), self.reg(where_clause)]));
197 312 : }
198 : // Path element bodies are short and must not wrap, breaks happen between path elements instead.
199 318 : let mut spaced_parts = Vec::new();
200 459 : for (i, &part) in body_parts.iter().enumerate() {
201 459 : if i > 0 {
202 153 : spaced_parts.push(f.text(" "));
203 306 : }
204 459 : spaced_parts.push(part);
205 : }
206 318 : let body = f.concat(&spaced_parts);
207 318 : let result = if is_true(vertex) {
208 213 : if body == 0 {
209 6 : f.text("()")
210 : } else {
211 207 : f.concat(&[f.text("("), body, f.text(")")])
212 : }
213 105 : } else if is_true(left) {
214 18 : f.concat(&[f.text("<-["), body, f.text("]-")])
215 87 : } else if is_true(right) {
216 84 : f.concat(&[f.text("-["), body, f.text("]->")])
217 3 : } else if is_true(any) {
218 3 : f.concat(&[f.text("-["), body, f.text("]-")])
219 : } else {
220 0 : return self.format_unimplemented(node);
221 : };
222 318 : match quantifier {
223 21 : Some(quantifier) => f.concat(&[result, self.reg(quantifier)]),
224 297 : None => result,
225 : }
226 318 : }
227 :
228 21 : pub(super) fn format_graph_quantifier(&self, node: &'a Node) -> FmtReg {
229 21 : let f = &self.fmt;
230 21 : let [lower, upper, fixed] = attrs!(self.children(node), [K::SQL_GRAPH_QUANTIFIER_LOWER, K::SQL_GRAPH_QUANTIFIER_UPPER, K::SQL_GRAPH_QUANTIFIER_FIXED]);
231 21 : if is_true(fixed) {
232 3 : let Some(lower) = lower else { return self.format_unimplemented(node) };
233 3 : return f.concat(&[f.text("{"), self.reg(lower), f.text("}")]);
234 18 : }
235 18 : let lower_reg = lower.map(|l| self.reg(l)).unwrap_or(0);
236 18 : let upper_reg = upper.map(|u| self.reg(u)).unwrap_or(0);
237 18 : f.concat(&[f.text("{"), lower_reg, f.text(","), upper_reg, f.text("}")])
238 21 : }
239 :
240 288 : pub(super) fn format_graph_label_expression(&self, node: &'a Node) -> FmtReg {
241 288 : let f = &self.fmt;
242 288 : let [label, wildcard, negated, left, right, disjunction] = attrs!(self.children(node), [
243 : K::SQL_GRAPH_LABEL_EXPRESSION_LABEL,
244 : K::SQL_GRAPH_LABEL_EXPRESSION_WILDCARD,
245 : K::SQL_GRAPH_LABEL_EXPRESSION_NEGATED,
246 : K::SQL_GRAPH_LABEL_EXPRESSION_LEFT,
247 : K::SQL_GRAPH_LABEL_EXPRESSION_RIGHT,
248 : K::SQL_GRAPH_LABEL_EXPRESSION_DISJUNCTION,
249 : ],
250 : );
251 288 : if let Some(label) = label {
252 243 : return self.reg(label);
253 45 : }
254 45 : if is_true(wildcard) {
255 3 : return f.text("%");
256 42 : }
257 : // The operand of an operator needs parentheses if it binds weaker (or equally strong on the right)
258 72 : let operand = |node: &'a Node, level: u8, is_right: bool| {
259 72 : let operand_level = self.graph_label_expression_level(node);
260 72 : let reg = self.reg(node);
261 72 : if operand_level < level || (is_right && operand_level == level) {
262 9 : f.parenthesized(reg)
263 : } else {
264 63 : reg
265 : }
266 72 : };
267 42 : if is_true(negated) {
268 12 : return match left {
269 12 : Some(left) => f.concat(&[f.text("!"), operand(left, GRAPH_LABEL_NEGATION, false)]),
270 0 : None => self.format_unimplemented(node),
271 : };
272 30 : }
273 30 : let (Some(left), Some(right)) = (left, right) else { return self.format_unimplemented(node) };
274 30 : let (op, level) = if is_true(disjunction) {
275 15 : (f.text(" | "), GRAPH_LABEL_DISJUNCTION)
276 : } else {
277 15 : (f.text(" & "), GRAPH_LABEL_CONJUNCTION)
278 : };
279 30 : f.concat(&[operand(left, level, false), op, operand(right, level, true)])
280 288 : }
281 :
282 : /// How strong a label expression binds, atoms bind strongest
283 72 : fn graph_label_expression_level(&self, node: &Node) -> u8 {
284 72 : if node.node_type() != NodeType::OBJECT_SQL_GRAPH_LABEL_EXPRESSION {
285 0 : return GRAPH_LABEL_ATOM;
286 72 : }
287 72 : let [negated, disjunction, right] = attrs!(self.children(node), [
288 : K::SQL_GRAPH_LABEL_EXPRESSION_NEGATED,
289 : K::SQL_GRAPH_LABEL_EXPRESSION_DISJUNCTION,
290 : K::SQL_GRAPH_LABEL_EXPRESSION_RIGHT,
291 : ]);
292 72 : if is_true(negated) {
293 6 : GRAPH_LABEL_NEGATION
294 66 : } else if right.is_none() {
295 54 : GRAPH_LABEL_ATOM
296 12 : } else if is_true(disjunction) {
297 9 : GRAPH_LABEL_DISJUNCTION
298 : } else {
299 3 : GRAPH_LABEL_CONJUNCTION
300 : }
301 72 : }
302 :
303 18 : pub(super) fn format_graph_rows(&self, node: &'a Node) -> FmtReg {
304 18 : let f = &self.fmt;
305 18 : let [per_match, per_step, source, edge, destination] = attrs!(self.children(node), [
306 : K::SQL_GRAPH_ROWS_PER_MATCH,
307 : K::SQL_GRAPH_ROWS_PER_STEP,
308 : K::SQL_GRAPH_ROWS_SOURCE,
309 : K::SQL_GRAPH_ROWS_EDGE,
310 : K::SQL_GRAPH_ROWS_DESTINATION,
311 : ],
312 : );
313 18 : if is_true(per_match) {
314 9 : return f.text("one row per match");
315 9 : }
316 9 : let (Some(source), Some(edge), Some(destination)) = (source, edge, destination) else {
317 0 : return self.format_unimplemented(node);
318 : };
319 9 : if !is_true(per_step) {
320 0 : return self.format_unimplemented(node);
321 9 : }
322 9 : let vars = f.concat(&[self.reg(source), f.text(", "), self.reg(edge), f.text(", "), self.reg(destination)]);
323 9 : f.concat(&[f.text("one row per step"), f.parenthesized(vars)])
324 18 : }
325 : }
|