Branch data Line data Source code
1 : : #include "parse/ASTDot.hpp"
2 : :
3 : : #include <iomanip>
4 : : #include <mutable/catalog/Schema.hpp>
5 : : #include <sstream>
6 : :
7 : : #define q(X) '"' << X << '"' // quote
8 : : #define id(X) q(std::hex << &X << std::dec) // convert virtual address to identifier
9 : :
10 : :
11 : : using namespace m;
12 : : using namespace m::ast;
13 : :
14 : :
15 : 0 : ASTDot::ASTDot(std::ostream &out, int i)
16 : 0 : : out(out)
17 : 0 : , indent_(i)
18 : 0 : {
19 [ # # # # ]: 0 : out << GRAPH_TYPE << " ast\n{";
20 : 0 : ++indent_;
21 [ # # # # ]: 0 : indent() << "forcelabels=true;";
22 [ # # # # ]: 0 : indent() << "graph [fontname = \"DejaVu Sans\"];";
23 [ # # # # ]: 0 : indent() << "node [fontname = \"DejaVu Sans\"];";
24 [ # # # # ]: 0 : indent() << "edge [fontname = \"DejaVu Sans\"];";
25 : 0 : }
26 : :
27 : 0 : ASTDot::~ASTDot()
28 : 0 : {
29 : 0 : --indent_;
30 [ # # ]: 0 : M_insist(indent_ == 0);
31 [ # # # # ]: 0 : out << "\n}" << std::endl;
32 : 0 : }
33 : :
34 : 0 : void ASTDot::cluster(Const<Clause> &c, const char *name, const char *label, const char *color)
35 : : {
36 : 0 : indent() << "subgraph cluster_" << name << '_' << &c;
37 : 0 : indent() << '{';
38 : 0 : ++indent_;
39 : 0 : indent() << "style=\"rounded,filled\";";
40 : 0 : indent() << "color=\"" << color << "\";";
41 : 0 : indent() << "penwidth=\"4\";";
42 : 0 : indent() << id(c) << " [label=\"" << label << "\"];";
43 : :
44 : 0 : (*this)(c);
45 : :
46 : 0 : --indent_;
47 : 0 : indent() << '}';
48 : 0 : }
49 : :
50 : :
51 : : /*--- Expressions ----------------------------------------------------------------------------------------------------*/
52 : :
53 : 0 : void ASTDot::operator()(Const<ErrorExpr> &e)
54 : : {
55 : 0 : indent() << id(e) << " [label=<<FONT COLOR=\"red\"><B>ErrorExpr</B></FONT>>];";
56 : 0 : }
57 : :
58 : 0 : void ASTDot::operator()(Const<Designator> &e)
59 : : {
60 : 0 : indent() << id(e) << " [label=<<B>";
61 : :
62 [ # # ]: 0 : if (e.has_explicit_table_name()) out << e.table_name.text << '.';
63 : 0 : out << e.attr_name.text << "</B>";
64 [ # # ]: 0 : if (e.has_type()) {
65 : 0 : std::ostringstream oss;
66 [ # # # # ]: 0 : oss << *e.type();
67 [ # # # # : 0 : out << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str());
# # # # ]
68 [ # # # # : 0 : if (auto pt = cast<const PrimitiveType>(e.type()))
# # ]
69 [ # # # # : 0 : out << "<SUB>" << (pt->is_scalar() ? "s" : "v") << "</SUB>";
# # # # #
# ]
70 [ # # ]: 0 : out << "</I></FONT>";
71 : 0 : }
72 : 0 : out << ">];";
73 : :
74 : 1 : /* Dot edge to table. */
75 : 0 : const auto &t = e.target();
76 [ # # ]: 0 : if (auto val = std::get_if<const Attribute*>(&t)) {
77 : 0 : const Attribute &A = **val;
78 : 0 : indent() << id(e) << EDGE << A.table.name() << ':' << A.name
79 : 0 : << " [style=\"dashed\",dir=\"forward\",color=\"#404040\"];";
80 [ # # ]: 0 : } else if (auto val = std::get_if<const Expr*>(&t)) {
81 : 0 : const Expr *expr = *val;
82 : 0 : indent() << id(e) << EDGE << id(*expr) << " [style=\"dashed\",dir=\"forward\",color=\"crimson\"];";
83 : 0 : }
84 : 0 : }
85 : :
86 : 0 : void ASTDot::operator()(Const<Constant> &e)
87 : : {
88 : 0 : indent() << id(e) << " [label=<<B>";
89 : :
90 [ # # ]: 0 : if (e.is_string()) {
91 [ # # # # : 0 : out << html_escape(*e.tok.text);
# # ]
92 : 0 : } else {
93 : 0 : out << e.tok.text;
94 : : }
95 : 0 : out << "</B>";
96 : :
97 [ # # ]: 0 : if (e.has_type()) {
98 : 0 : std::ostringstream oss;
99 [ # # # # ]: 0 : oss << *e.type();
100 [ # # # # : 0 : out << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str()) << "</I></FONT>";
# # # # #
# ]
101 : 0 : }
102 : :
103 : 0 : out << ">];";
104 : 0 : }
105 : :
106 : 0 : void ASTDot::operator()(Const<FnApplicationExpr> &e)
107 : : {
108 : 0 : (*this)(*e.fn);
109 : 0 : indent() << id(e) << " [label=<()";
110 : :
111 [ # # ]: 0 : if (e.has_type()) {
112 : 0 : std::ostringstream oss;
113 [ # # # # ]: 0 : oss << *e.type();
114 [ # # # # : 0 : out << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str()) << "</I></FONT>";
# # # # #
# ]
115 : 0 : }
116 : :
117 : 0 : out << ">];";
118 : 0 : indent() << id(e) << EDGE << id(*e.fn) << ';';
119 : :
120 [ # # ]: 0 : for (auto &arg : e.args) {
121 : 0 : (*this)(*arg);
122 : 0 : indent() << id(e) << EDGE << id(*arg) << ';';
123 : : }
124 : 0 : }
125 : :
126 : 0 : void ASTDot::operator()(Const<UnaryExpr> &e)
127 : : {
128 : 0 : (*this)(*e.expr);
129 [ # # # # : 0 : indent() << id(e) << " [label=<" << html_escape(*e.op().text);
# # # # ]
130 : :
131 [ # # ]: 0 : if (e.has_type()) {
132 : 0 : std::ostringstream oss;
133 [ # # # # ]: 0 : oss << *e.type();
134 [ # # # # : 0 : out << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str()) << "</I></FONT>";
# # # # #
# ]
135 : 0 : }
136 : :
137 : 0 : out << ">];";
138 : 0 : indent() << id(e) << EDGE << id(*e.expr) << ';';
139 : 0 : }
140 : :
141 : 0 : void ASTDot::operator()(Const<BinaryExpr> &e)
142 : : {
143 : 0 : (*this)(*e.lhs);
144 : 0 : (*this)(*e.rhs);
145 [ # # # # : 0 : indent() << id(e) << " [label=<" << html_escape(*e.op().text);
# # # # ]
146 : :
147 [ # # ]: 0 : if (e.has_type()) {
148 : 0 : std::ostringstream oss;
149 [ # # # # ]: 0 : oss << *e.type();
150 [ # # # # : 0 : out << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str()) << "</I></FONT>";
# # # # #
# ]
151 : 0 : }
152 : :
153 : 0 : out << ">];";
154 : 0 : indent() << id(e) << EDGE << id(*e.lhs) << ';';
155 : 0 : indent() << id(e) << EDGE << id(*e.rhs) << ';';
156 : 0 : }
157 : :
158 : 0 : void ASTDot::operator()(Const<QueryExpr> &e)
159 : : {
160 : 0 : (*this)(*e.query);
161 : 0 : indent() << id(e) << " [label=<QueryExpr";
162 : :
163 [ # # ]: 0 : if (e.has_type()) {
164 : 0 : std::ostringstream oss;
165 [ # # # # ]: 0 : oss << *e.type();
166 [ # # # # : 0 : out << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str()) << "</I></FONT>";
# # # # #
# ]
167 : 0 : }
168 : :
169 : 0 : out << ">];";
170 : 0 : indent() << id(e) << EDGE << id(*e.query) << ';';
171 : 0 : }
172 : :
173 : :
174 : : /*--- Clauses --------------------------------------------------------------------------------------------------------*/
175 : :
176 : 0 : void ASTDot::operator()(Const<ErrorClause> &c)
177 : : {
178 : 0 : indent() << id(c) << " [label=\"ErrorClause\"];";
179 : 0 : }
180 : :
181 : 0 : void ASTDot::operator()(Const<SelectClause> &c)
182 : : {
183 [ # # ]: 0 : if (c.select_all) {
184 : 0 : out << '\n';
185 : 0 : indent() << q(std::hex << c << '*') << "[label=\"*\"];";
186 : 0 : indent() << id(c) << EDGE << q(std::hex << c << '*') << ';';
187 : 0 : }
188 [ # # ]: 0 : for (auto &s : c.select) {
189 : 0 : out << '\n';
190 : 0 : (*this)(*s.first);
191 [ # # ]: 0 : if (s.second) {
192 : 0 : indent() << id(s.second) << " [label=\"AS " << s.second.text << "\"];";
193 : 0 : indent() << id(c) << EDGE << id(s.second) << ';';
194 : 0 : indent() << id(s.second) << EDGE << id(*s.first) << ';';
195 : 0 : } else {
196 : 0 : indent() << id(c) << EDGE << id(*s.first) << ';';
197 : : }
198 : : }
199 : 0 : }
200 : :
201 : 0 : void ASTDot::operator()(Const<FromClause> &c)
202 : : {
203 [ # # ]: 0 : for (auto &t : c.from) {
204 : 0 : out << '\n';
205 [ # # ]: 0 : if (auto name = std::get_if<Token>(&t.source)) {
206 [ # # ]: 0 : if (t.alias) {
207 : 0 : indent() << id(t.alias) << " [label=\"AS " << t.alias.text << "\"];";
208 : 0 : indent() << id (*name) << " [label=\"" << name->text << "\"];";
209 : 0 : indent() << id(c) << EDGE << id(t.alias) << EDGE << id(*name) << ';';
210 : 0 : } else {
211 : 0 : indent() << id(*name) << " [label=\"" << name->text << "\"];";
212 : 0 : indent() << id(c) << EDGE << id(*name) << ';';
213 : : }
214 [ # # ]: 0 : } else if (auto stmt = std::get_if<Stmt*>(&t.source)) {
215 : 0 : M_insist(t.alias, "nested statements must have an alias");
216 : 0 : indent() << id(t.alias) << " [label=\"AS " << t.alias.text << "\"];";
217 : 0 : (*this)(**stmt);
218 : 0 : indent() << id(c) << EDGE << id(t.alias) << EDGE << id(**stmt) << ';';
219 : 0 : } else {
220 : 0 : M_unreachable("invalid variant");
221 : : }
222 [ # # ]: 0 : if (t.has_table()) {
223 : 0 : M_insist(std::holds_alternative<Token>(t.source));
224 : 0 : auto &name = std::get<Token>(t.source);
225 : 0 : auto &R = t.table();
226 : 0 : indent() << id(name) << EDGE << R.name() << ":n [dir=\"forward\",color=\"#404040\"];";
227 : 0 : }
228 : : }
229 : 0 : }
230 : :
231 : 0 : void ASTDot::operator()(Const<WhereClause> &c)
232 : : {
233 : 0 : out << '\n';
234 : 0 : (*this)(*c.where);
235 : 0 : indent() << id(c) << EDGE << id(*c.where) << ';';
236 : 0 : }
237 : :
238 : 0 : void ASTDot::operator()(Const<GroupByClause> &c)
239 : : {
240 [ # # ]: 0 : for (auto &[grp, alias] : c.group_by) {
241 : 0 : out << '\n';
242 : 0 : (*this)(*grp);
243 : 0 : indent() << id(c) << EDGE << id(*grp) << ';';
244 : : }
245 : 0 : }
246 : :
247 : 0 : void ASTDot::operator()(Const<HavingClause> &c)
248 : : {
249 : 0 : out << '\n';
250 : 0 : (*this)(*c.having);
251 : 0 : indent() << id(c) << EDGE << id(*c.having) << ';';
252 : 0 : }
253 : :
254 : 0 : void ASTDot::operator()(Const<OrderByClause> &c)
255 : : {
256 [ # # ]: 0 : for (auto &o : c.order_by) {
257 : 0 : out << '\n';
258 [ # # ]: 0 : if (o.second)
259 : 0 : indent() << id(o.second) << " [label=\"ASC\"];";
260 : : else
261 : 0 : indent() << id(o.second) << " [label=\"DESC\"];";
262 : 0 : indent() << id(c) << EDGE << id(o.second) << ';';
263 : 0 : (*this)(*o.first);
264 : 0 : indent() << id(o.second) << EDGE << id(*o.first) << ';';
265 : : }
266 : 0 : }
267 : :
268 : 0 : void ASTDot::operator()(Const<LimitClause> &c)
269 : : {
270 : 0 : out << '\n';
271 : 0 : indent() << id(c.limit) << " [label=<<B>" << c.limit.text << "</B>>];";
272 : 0 : indent() << id(c) << EDGE << id(c.limit) << ';';
273 : :
274 [ # # ]: 0 : if (c.offset) {
275 : 0 : out << '\n';
276 : 0 : indent() << id(c.offset) << " [label=<OFFSET <B>" << c.offset.text << "</B>>];";
277 : 0 : indent() << id(c) << EDGE << id(c.offset) << ';';
278 : 0 : }
279 : 0 : }
280 : :
281 : :
282 : : /*--- Constraints ----------------------------------------------------------------------------------------------------*/
283 : :
284 : 0 : void ASTDot::operator()(Const<PrimaryKeyConstraint> &c)
285 : : {
286 : 0 : indent() << id(c) << " [label=<<B>PRIMARY KEY</B>>];";
287 : 0 : }
288 : :
289 : 0 : void ASTDot::operator()(Const<UniqueConstraint> &c)
290 : : {
291 : 0 : indent() << id(c) << " [label=<<B>UNIQUE</B>>];";
292 : 0 : }
293 : :
294 : 0 : void ASTDot::operator()(Const<NotNullConstraint> &c)
295 : : {
296 : 0 : indent() << id(c) << " [label=<<B>NOT NULL</B>>];";
297 : 0 : }
298 : :
299 : 0 : void ASTDot::operator()(Const<CheckConditionConstraint> &c)
300 : : {
301 : 0 : (*this)(*c.cond);
302 : 0 : indent() << id(c) << " [label=<<B>CHECK()</B>>];";
303 : 0 : indent() << id(c) << EDGE << id(*c.cond) << ';';
304 : 0 : }
305 : :
306 : 0 : void ASTDot::operator()(Const<ReferenceConstraint> &c)
307 : : {
308 : 0 : indent() << id(c) << " [label=<<B>REFERENCES " << c.table_name.text << '(' << c.attr_name.text << ")</B>>];";
309 : 0 : }
310 : :
311 : :
312 : : /*----- Instruction --------------------------------------------------------------------------------------------------*/
313 : :
314 : 0 : void ASTDot::operator()(const Instruction &inst)
315 : : {
316 : 0 : out << '\n';
317 : 0 : std::ostringstream oss;
318 [ # # # # : 0 : indent() << id(inst) << " [label=\"" << inst.tok.text << "\"];";
# # # # #
# # # # #
# # # # ]
319 : 0 : }
320 : :
321 : :
322 : : /*--- Stmt -----------------------------------------------------------------------------------------------------------*/
323 : :
324 : 0 : void ASTDot::operator()(Const<ErrorStmt>&)
325 : : {
326 : : // TODO implement
327 : 0 : }
328 : :
329 : 0 : void ASTDot::operator()(Const<EmptyStmt>&)
330 : : {
331 : : // TODO implement
332 : 0 : }
333 : :
334 : 0 : void ASTDot::operator()(Const<CreateDatabaseStmt>&)
335 : : {
336 : : // TODO implement
337 : 0 : }
338 : :
339 : 0 : void ASTDot::operator()(Const<DropDatabaseStmt>&)
340 : : {
341 : : // TODO implement
342 : 0 : }
343 : :
344 : 0 : void ASTDot::operator()(Const<UseDatabaseStmt>&)
345 : : {
346 : : // TODO implement
347 : 0 : }
348 : :
349 : 0 : void ASTDot::operator()(Const<CreateTableStmt>&)
350 : : {
351 : : // TODO implement
352 : 0 : }
353 : :
354 : 0 : void ASTDot::operator()(Const<DropTableStmt>&)
355 : : {
356 : : // TODO implement
357 : 0 : }
358 : :
359 : 0 : void ASTDot::operator()(Const<CreateIndexStmt>&)
360 : : {
361 : : // TODO implement
362 : 0 : }
363 : :
364 : 0 : void ASTDot::operator()(Const<DropIndexStmt>&)
365 : : {
366 : : // TODO implement
367 : 0 : }
368 : :
369 : 0 : void ASTDot::operator()(Const<SelectStmt> &s)
370 : : {
371 : 0 : out << '\n';
372 : 0 : std::ostringstream oss;
373 [ # # # # : 0 : indent() << id(s) << " [label=\"SelectStmt\"];";
# # # # #
# # # #
# ]
374 : :
375 [ # # ]: 0 : if (s.from) {
376 : : /* Dot the accessed tables first. */
377 [ # # # # ]: 0 : indent() << "subgraph sources";
378 [ # # # # ]: 0 : indent() << '{';
379 : 0 : ++indent_;
380 [ # # # # ]: 0 : if (auto f = cast<FromClause>(s.from.get())) {
381 [ # # ]: 0 : for (auto &t : f->from) {
382 [ # # # # ]: 0 : if (t.has_table()) {
383 [ # # ]: 0 : auto &R = t.table();
384 : :
385 [ # # # # : 0 : indent() << R.name() << "[shape=none,style=filled,fillcolor=white,label=<";
# # # # ]
386 : 0 : ++indent_;
387 [ # # # # ]: 0 : indent() << "<TABLE>";
388 : 0 : ++indent_;
389 [ # # # # : 0 : indent() << "<TR><TD BORDER=\"0\"><B>" << R.name() << "</B></TD></TR>";
# # # # #
# ]
390 : :
391 [ # # # # : 0 : for (auto &A : R) {
# # # # #
# # # ]
392 [ # # # # ]: 0 : oss.str("");
393 [ # # ]: 0 : oss << *A.type;
394 [ # # # # : 0 : indent() << "<TR><TD PORT=\"" << A.name << "\">" << A.name
# # # # #
# ]
395 [ # # # # : 0 : << "<FONT POINT-SIZE=\"11\"><I> : " << html_escape(oss.str()) << "</I></FONT>"
# # # # #
# ]
396 [ # # ]: 0 : << "</TD></TR>";
397 : : }
398 : :
399 : 0 : --indent_;
400 [ # # # # ]: 0 : indent() << "</TABLE>";
401 : 0 : --indent_;
402 [ # # # # ]: 0 : indent() << ">];";
403 : 0 : }
404 : : }
405 : 0 : }
406 : 0 : --indent_;
407 [ # # # # ]: 0 : indent() << "}\n";
408 : 0 : }
409 : :
410 [ # # ]: 0 : cluster(*s.select, "select", "SELECT", "#e6194B20");
411 [ # # # # : 0 : indent() << id(s) << EDGE << id(*s.select) << ';';
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
412 : :
413 : : #define DOT(NAME, LABEL, COLOR) \
414 : : if (s.NAME) { \
415 : : out << '\n'; \
416 : : cluster(*s.NAME, #NAME, LABEL, COLOR); \
417 : : indent() << id(s) << EDGE << id(*s.NAME) << ';'; \
418 : : }
419 [ # # # # : 0 : DOT(from, "FROM", "#bfef4550");
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
420 [ # # # # : 0 : DOT(where, "WHERE", "#42d4f430");
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
421 [ # # # # : 0 : DOT(group_by, "GROUP BY", "#3cb44b30");
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
422 [ # # # # : 0 : DOT(having, "HAVING", "#aaffc350");
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
423 [ # # # # : 0 : DOT(order_by, "ORDER BY", "#ffe11950");
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
424 [ # # # # : 0 : DOT(limit, "LIMIT", "#80800040");
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
425 : : #undef DOT
426 : 0 : }
427 : :
428 : 0 : void ASTDot::operator()(Const<InsertStmt>&)
429 : : {
430 : : // TODO implement
431 : 0 : }
432 : :
433 : 0 : void ASTDot::operator()(Const<UpdateStmt>&)
434 : : {
435 : : // TODO implement
436 : 0 : }
437 : :
438 : 0 : void ASTDot::operator()(Const<DeleteStmt>&)
439 : : {
440 : : // TODO implement
441 : 0 : }
442 : :
443 : 0 : void ASTDot::operator()(Const<DSVImportStmt>&)
444 : : {
445 : : // TODO implement
446 : 0 : }
|