icydb_core/db/query/explain/
json.rs1use crate::db::query::explain::{
7 ExplainExecutionNodeDescriptor,
8 access_projection::write_access_json,
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 writer::JsonWriter,
14};
15
16impl ExplainExecutionNodeDescriptor {
17 #[must_use]
19 pub fn render_json_canonical(&self) -> String {
20 let mut out = String::new();
21 let mut node_id_counter = 0_u64;
22 write_execution_node_json(self, &mut node_id_counter, &mut out);
23 out
24 }
25}
26
27fn write_execution_node_json(
28 node: &ExplainExecutionNodeDescriptor,
29 node_id_counter: &mut u64,
30 out: &mut String,
31) {
32 let node_id = next_node_id(node_id_counter);
33 let mut object = JsonWriter::begin_object(out);
34
35 object.field_u64("node_id", node_id);
36 object.field_str("node_type", node.node_type().as_str());
37 object.field_str("layer", node.node_type().layer_label());
38 object.field_str(
39 "execution_mode",
40 execution_mode_label(node.execution_mode()),
41 );
42 object.field_str(
43 "execution_mode_detail",
44 execution_mode_detail_label(node.execution_mode()),
45 );
46 object.field_with("access_strategy", |out| {
47 match node.access_strategy().as_ref() {
48 Some(access) => write_access_json(access, out),
49 None => out.push_str("null"),
50 }
51 });
52 object.field_str("predicate_pushdown_mode", predicate_pushdown_mode(node));
53 match node.predicate_pushdown() {
54 Some(predicate_pushdown) => object.field_str("predicate_pushdown", predicate_pushdown),
55 None => object.field_null("predicate_pushdown"),
56 }
57 match fast_path_selected(node) {
58 Some(selected) => object.field_bool("fast_path_selected", selected),
59 None => object.field_null("fast_path_selected"),
60 }
61 match fast_path_reason(node) {
62 Some(reason) => object.field_str("fast_path_reason", reason),
63 None => object.field_null("fast_path_reason"),
64 }
65 match node.residual_predicate() {
66 Some(residual_predicate) => {
67 object.field_value_debug("residual_predicate", residual_predicate);
68 }
69 None => object.field_null("residual_predicate"),
70 }
71 match node.projection() {
72 Some(projection) => object.field_str("projection", projection),
73 None => object.field_null("projection"),
74 }
75 match node.ordering_source() {
76 Some(ordering_source) => {
77 object.field_str("ordering_source", ordering_source_label(ordering_source));
78 }
79 None => object.field_null("ordering_source"),
80 }
81 match node.limit() {
82 Some(limit) => object.field_u64("limit", u64::from(limit)),
83 None => object.field_null("limit"),
84 }
85 match node.cursor() {
86 Some(cursor) => object.field_bool("cursor", cursor),
87 None => object.field_null("cursor"),
88 }
89 match node.covering_scan() {
90 Some(covering_scan) => object.field_bool("covering_scan", covering_scan),
91 None => object.field_null("covering_scan"),
92 }
93 match node.rows_expected() {
94 Some(rows_expected) => object.field_u64("rows_expected", rows_expected),
95 None => object.field_null("rows_expected"),
96 }
97 object.field_with("children", |out| {
98 out.push('[');
99 for (index, child) in node.children().iter().enumerate() {
100 if index > 0 {
101 out.push(',');
102 }
103 write_execution_node_json(child, node_id_counter, out);
104 }
105 out.push(']');
106 });
107 object.field_debug_map("node_properties", node.node_properties());
108
109 object.finish();
110}
111
112const fn next_node_id(node_id_counter: &mut u64) -> u64 {
113 let node_id = *node_id_counter;
114 *node_id_counter = node_id_counter.saturating_add(1);
115 node_id
116}