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 = *node_id_counter;
33 *node_id_counter = node_id_counter.saturating_add(1);
34 let mut object = JsonWriter::begin_object(out);
35
36 object.field_u64("node_id", node_id);
37 object.field_str("node_type", node.node_type().as_str());
38 object.field_str("layer", node.node_type().layer_label());
39 object.field_str(
40 "execution_mode",
41 execution_mode_label(node.execution_mode()),
42 );
43 object.field_str(
44 "execution_mode_detail",
45 execution_mode_detail_label(node.execution_mode()),
46 );
47 object.field_with("access_strategy", |out| {
48 match node.access_strategy().as_ref() {
49 Some(access) => write_access_json(access, out),
50 None => out.push_str("null"),
51 }
52 });
53 object.field_str("predicate_pushdown_mode", predicate_pushdown_mode(node));
54 match node.predicate_pushdown() {
55 Some(predicate_pushdown) => object.field_str("predicate_pushdown", predicate_pushdown),
56 None => object.field_null("predicate_pushdown"),
57 }
58 match fast_path_selected(node) {
59 Some(selected) => object.field_bool("fast_path_selected", selected),
60 None => object.field_null("fast_path_selected"),
61 }
62 match fast_path_reason(node) {
63 Some(reason) => object.field_str("fast_path_reason", reason),
64 None => object.field_null("fast_path_reason"),
65 }
66 match node.residual_predicate() {
67 Some(residual_predicate) => {
68 object.field_value_debug("residual_predicate", residual_predicate);
69 }
70 None => object.field_null("residual_predicate"),
71 }
72 match node.projection() {
73 Some(projection) => object.field_str("projection", projection),
74 None => object.field_null("projection"),
75 }
76 match node.ordering_source() {
77 Some(ordering_source) => {
78 object.field_str("ordering_source", ordering_source_label(ordering_source));
79 }
80 None => object.field_null("ordering_source"),
81 }
82 match node.limit() {
83 Some(limit) => object.field_u64("limit", u64::from(limit)),
84 None => object.field_null("limit"),
85 }
86 match node.cursor() {
87 Some(cursor) => object.field_bool("cursor", cursor),
88 None => object.field_null("cursor"),
89 }
90 match node.covering_scan() {
91 Some(covering_scan) => object.field_bool("covering_scan", covering_scan),
92 None => object.field_null("covering_scan"),
93 }
94 match node.rows_expected() {
95 Some(rows_expected) => object.field_u64("rows_expected", rows_expected),
96 None => object.field_null("rows_expected"),
97 }
98 object.field_with("children", |out| {
99 out.push('[');
100 for (index, child) in node.children().iter().enumerate() {
101 if index > 0 {
102 out.push(',');
103 }
104 write_execution_node_json(child, node_id_counter, out);
105 }
106 out.push(']');
107 });
108 object.field_debug_map("node_properties", node.node_properties());
109
110 object.finish();
111}