icydb_core/db/query/explain/
render.rs1use crate::db::query::explain::{
7 ExplainExecutionNodeDescriptor, ExplainPropertyMap,
8 access_projection::write_access_strategy_label,
9 execution::{execution_mode_label, ordering_source_label},
10 nodes::{
11 execution_mode_detail_label, fast_path_reason, fast_path_selected, predicate_pushdown_mode,
12 },
13};
14use std::fmt::Write;
15
16impl ExplainExecutionNodeDescriptor {
17 #[must_use]
19 pub fn render_text_tree(&self) -> String {
20 let mut out = String::new();
21 let mut node_id_counter = 0_u64;
22 self.render_text_tree_into(0, &mut node_id_counter, &mut out);
23 out
24 }
25
26 #[must_use]
28 pub fn render_text_tree_verbose(&self) -> String {
29 self.render_text_tree_verbose_with_indent("")
30 }
31
32 #[must_use]
35 pub fn render_text_tree_verbose_with_indent(&self, indent: &str) -> String {
36 let mut out = String::new();
37 let mut node_id_counter = 0_u64;
38 self.render_text_tree_verbose_into(indent, 0, &mut node_id_counter, &mut out);
39 out
40 }
41
42 fn render_text_tree_into(&self, depth: usize, node_id_counter: &mut u64, out: &mut String) {
43 let node_id = *node_id_counter;
44 *node_id_counter = node_id_counter.saturating_add(1);
45 push_rendered_line_prefix(out, depth);
46 let _ = write!(
47 out,
48 "{} execution_mode={}",
49 self.node_type.as_str(),
50 execution_mode_label(self.execution_mode)
51 );
52 let _ = write!(out, " node_id={node_id}");
53 let _ = write!(out, " layer={}", self.node_type.layer_label());
54 let _ = write!(
55 out,
56 " execution_mode_detail={}",
57 execution_mode_detail_label(self.execution_mode)
58 );
59 let _ = write!(
60 out,
61 " predicate_pushdown_mode={}",
62 predicate_pushdown_mode(self)
63 );
64 if let Some(fast_path_selected) = fast_path_selected(self) {
65 let _ = write!(out, " fast_path_selected={fast_path_selected}");
66 }
67 if let Some(fast_path_reason) = fast_path_reason(self) {
68 let _ = write!(out, " fast_path_reason={fast_path_reason}");
69 }
70
71 if let Some(access_strategy) = self.access_strategy.as_ref() {
72 out.push_str(" access=");
73 write_access_strategy_label(out, access_strategy);
74 }
75 if let Some(predicate_pushdown) = self.predicate_pushdown.as_ref() {
76 let _ = write!(out, " predicate_pushdown={predicate_pushdown}");
77 }
78 if let Some(residual_predicate) = self.residual_predicate.as_ref() {
79 let _ = write!(out, " residual_predicate={residual_predicate:?}");
80 }
81 if let Some(projection) = self.projection.as_ref() {
82 let _ = write!(out, " projection={projection}");
83 }
84 if let Some(ordering_source) = self.ordering_source {
85 let _ = write!(
86 out,
87 " ordering_source={}",
88 ordering_source_label(ordering_source)
89 );
90 }
91 if let Some(limit) = self.limit {
92 let _ = write!(out, " limit={limit}");
93 }
94 if let Some(cursor) = self.cursor {
95 let _ = write!(out, " cursor={cursor}");
96 }
97 if let Some(covering_scan) = self.covering_scan {
98 let _ = write!(out, " covering_scan={covering_scan}");
99 }
100 if let Some(rows_expected) = self.rows_expected {
101 let _ = write!(out, " rows_expected={rows_expected}");
102 }
103 if !self.node_properties.is_empty() {
104 out.push_str(" node_properties=");
105 write_node_properties(out, &self.node_properties);
106 }
107
108 for child in &self.children {
109 child.render_text_tree_into(depth.saturating_add(1), node_id_counter, out);
110 }
111 }
112
113 fn render_text_tree_verbose_into(
114 &self,
115 base_indent: &str,
116 depth: usize,
117 node_id_counter: &mut u64,
118 out: &mut String,
119 ) {
120 let node_id = *node_id_counter;
121 *node_id_counter = node_id_counter.saturating_add(1);
122
123 push_rendered_line_prefix_with_base_depth(out, base_indent, depth);
126 let _ = write!(
127 out,
128 "{} execution_mode={}",
129 self.node_type.as_str(),
130 execution_mode_label(self.execution_mode)
131 );
132 push_rendered_line_prefix_with_base_depth(out, base_indent, depth.saturating_add(1));
133 let _ = write!(out, "node_id={node_id}");
134 push_rendered_line_prefix_with_base_depth(out, base_indent, depth.saturating_add(1));
135 let _ = write!(out, "layer={}", self.node_type.layer_label());
136 push_rendered_line_prefix_with_base_depth(out, base_indent, depth.saturating_add(1));
137 let _ = write!(
138 out,
139 "execution_mode_detail={}",
140 execution_mode_detail_label(self.execution_mode)
141 );
142 push_rendered_line_prefix_with_base_depth(out, base_indent, depth.saturating_add(1));
143 let _ = write!(
144 out,
145 "predicate_pushdown_mode={}",
146 predicate_pushdown_mode(self)
147 );
148 if let Some(fast_path_selected) = fast_path_selected(self) {
149 push_rendered_line_prefix_with_base_depth(out, base_indent, depth.saturating_add(1));
150 let _ = write!(out, "fast_path_selected={fast_path_selected}");
151 }
152 if let Some(fast_path_reason) = fast_path_reason(self) {
153 push_rendered_line_prefix_with_base_depth(out, base_indent, depth.saturating_add(1));
154 let _ = write!(out, "fast_path_reason={fast_path_reason}");
155 }
156
157 self.render_text_tree_verbose_node_fields(base_indent, depth.saturating_add(1), out);
159
160 for child in &self.children {
162 child.render_text_tree_verbose_into(
163 base_indent,
164 depth.saturating_add(1),
165 node_id_counter,
166 out,
167 );
168 }
169 }
170
171 fn render_text_tree_verbose_node_fields(
172 &self,
173 base_indent: &str,
174 field_depth: usize,
175 out: &mut String,
176 ) {
177 if let Some(access_strategy) = self.access_strategy.as_ref() {
178 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
179 out.push_str("access_strategy=");
180 write_access_strategy_label(out, access_strategy);
181 }
182 if let Some(predicate_pushdown) = self.predicate_pushdown.as_ref() {
183 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
184 let _ = write!(out, "predicate_pushdown={predicate_pushdown}");
185 }
186 if let Some(residual_predicate) = self.residual_predicate.as_ref() {
187 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
188 let _ = write!(out, "residual_predicate={residual_predicate:?}");
189 }
190 if let Some(projection) = self.projection.as_ref() {
191 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
192 let _ = write!(out, "projection={projection}");
193 }
194 if let Some(ordering_source) = self.ordering_source {
195 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
196 let _ = write!(
197 out,
198 "ordering_source={}",
199 ordering_source_label(ordering_source)
200 );
201 }
202 if let Some(limit) = self.limit {
203 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
204 let _ = write!(out, "limit={limit}");
205 }
206 if let Some(cursor) = self.cursor {
207 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
208 let _ = write!(out, "cursor={cursor}");
209 }
210 if let Some(covering_scan) = self.covering_scan {
211 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
212 let _ = write!(out, "covering_scan={covering_scan}");
213 }
214 if let Some(rows_expected) = self.rows_expected {
215 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
216 let _ = write!(out, "rows_expected={rows_expected}");
217 }
218 if !self.node_properties.is_empty() {
219 push_rendered_line_prefix_with_base_depth(out, base_indent, field_depth);
220 out.push_str("node_properties:");
221
222 for (key, value) in self.node_properties.iter() {
225 push_rendered_line_prefix_with_base_depth(
226 out,
227 base_indent,
228 field_depth.saturating_add(1),
229 );
230 let _ = write!(out, "{key}={value:?}");
231 }
232 }
233 }
234}
235
236fn push_rendered_line_prefix(out: &mut String, depth: usize) {
237 if !out.is_empty() {
238 out.push('\n');
239 }
240
241 for _ in 0..depth {
242 out.push_str(" ");
243 }
244}
245
246fn push_rendered_line_prefix_with_base_depth(out: &mut String, base_indent: &str, depth: usize) {
247 if !out.is_empty() {
248 out.push('\n');
249 }
250 out.push_str(base_indent);
251
252 for _ in 0..depth {
253 out.push_str(" ");
254 }
255}
256
257fn write_node_properties(out: &mut String, node_properties: &ExplainPropertyMap) {
258 let mut first = true;
259 for (key, value) in node_properties.iter() {
260 if first {
261 first = false;
262 } else {
263 out.push(',');
264 }
265 let _ = write!(out, "{key}={value:?}");
266 }
267}