linkrs_query/planning/plan/explain/
description.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Pair {
6 pub key: String,
7 pub value: String,
8}
9
10impl Pair {
11 pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
12 Self {
13 key: key.into(),
14 value: value.into(),
15 }
16 }
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct PlanNodeBranchInfo {
22 pub is_do_branch: bool,
23 pub condition_node_id: i64,
24}
25
26impl PlanNodeBranchInfo {
27 pub fn new(is_do_branch: bool, condition_node_id: i64) -> Self {
28 Self {
29 is_do_branch,
30 condition_node_id,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct ProfilingStats {
38 pub rows: i64,
39 pub exec_duration_in_us: i64,
40 pub total_duration_in_us: i64,
41 pub other_stats: std::collections::HashMap<String, String>,
42}
43
44impl ProfilingStats {
45 pub fn new() -> Self {
46 Self {
47 rows: 0,
48 exec_duration_in_us: 0,
49 total_duration_in_us: 0,
50 other_stats: std::collections::HashMap::new(),
51 }
52 }
53}
54
55impl Default for ProfilingStats {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct PlanNodeDescription {
64 pub name: String,
65 pub id: i64,
66 pub output_var: String,
67 pub description: Option<Vec<Pair>>,
68 pub profiles: Option<Vec<ProfilingStats>>,
69 pub branch_info: Option<PlanNodeBranchInfo>,
70 pub dependencies: Option<Vec<i64>>,
71}
72
73impl PlanNodeDescription {
74 pub fn new(name: impl Into<String>, id: i64) -> Self {
75 Self {
76 name: name.into(),
77 id,
78 output_var: String::new(),
79 description: None,
80 profiles: None,
81 branch_info: None,
82 dependencies: None,
83 }
84 }
85
86 pub fn with_output_var(mut self, output_var: impl Into<String>) -> Self {
87 self.output_var = output_var.into();
88 self
89 }
90
91 pub fn add_description(&mut self, key: impl Into<String>, value: impl Into<String>) {
92 if self.description.is_none() {
93 self.description = Some(Vec::new());
94 }
95 self.description
96 .as_mut()
97 .expect("description should be Some after initialization")
98 .push(Pair::new(key, value));
99 }
100
101 pub fn with_description(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
102 self.add_description(key, value);
103 self
104 }
105
106 pub fn set_dependencies(&mut self, deps: Vec<i64>) {
107 self.dependencies = Some(deps);
108 }
109
110 pub fn with_dependencies(mut self, deps: Vec<i64>) -> Self {
111 self.dependencies = Some(deps);
112 self
113 }
114
115 pub fn set_branch_info(&mut self, branch_info: PlanNodeBranchInfo) {
116 self.branch_info = Some(branch_info);
117 }
118
119 pub fn with_branch_info(mut self, branch_info: PlanNodeBranchInfo) -> Self {
120 self.branch_info = Some(branch_info);
121 self
122 }
123
124 pub fn add_profile(&mut self, profile: ProfilingStats) {
125 if self.profiles.is_none() {
126 self.profiles = Some(Vec::new());
127 }
128 self.profiles
129 .as_mut()
130 .expect("profiles should be Some after initialization")
131 .push(profile);
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct PlanDescription {
138 pub plan_node_descs: Vec<PlanNodeDescription>,
139 pub node_index_map: std::collections::HashMap<i64, usize>,
140 pub format: String,
141 pub optimize_time_in_us: i64,
142 pub partition_spec_description: Option<String>,
143
144 pub requested_workers: usize,
146 pub actual_workers: usize,
147 pub parallel_wall_time_us: u64,
148 pub parallel_work_time_us: u64,
149 pub parallel_buffered_chunks_peak: usize,
150 pub parallel_buffered_bytes_peak: usize,
151 pub parallel_fallback_reason: String,
152
153 pub cbo_notes: Vec<String>,
155
156 pub statistics_summary: String,
158
159 pub columnar_summary: String,
161}
162
163impl PlanDescription {
164 pub fn new() -> Self {
165 Self {
166 plan_node_descs: Vec::new(),
167 node_index_map: std::collections::HashMap::new(),
168 format: String::new(),
169 optimize_time_in_us: 0,
170 partition_spec_description: None,
171 requested_workers: 1,
172 actual_workers: 1,
173 parallel_wall_time_us: 0,
174 parallel_work_time_us: 0,
175 parallel_buffered_chunks_peak: 0,
176 parallel_buffered_bytes_peak: 0,
177 parallel_fallback_reason: String::new(),
178 cbo_notes: Vec::new(),
179 statistics_summary: String::new(),
180 columnar_summary: String::new(),
181 }
182 }
183
184 pub fn add_node_desc(&mut self, desc: PlanNodeDescription) -> usize {
185 let index = self.plan_node_descs.len();
186 let node_id = desc.id;
187 self.plan_node_descs.push(desc);
188 self.node_index_map.insert(node_id, index);
189 index
190 }
191
192 pub fn get_node_desc(&self, node_id: i64) -> Option<&PlanNodeDescription> {
193 self.node_index_map
194 .get(&node_id)
195 .and_then(|&index| self.plan_node_descs.get(index))
196 }
197
198 pub fn get_node_desc_mut(&mut self, node_id: i64) -> Option<&mut PlanNodeDescription> {
199 if let Some(&index) = self.node_index_map.get(&node_id) {
200 self.plan_node_descs.get_mut(index)
201 } else {
202 None
203 }
204 }
205}
206
207impl Default for PlanDescription {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn plan_description_p8_fields_defaults() {
219 let desc = PlanDescription::new();
220 assert_eq!(desc.requested_workers, 1);
221 assert_eq!(desc.actual_workers, 1);
222 assert_eq!(desc.parallel_wall_time_us, 0);
223 assert_eq!(desc.parallel_work_time_us, 0);
224 assert_eq!(desc.parallel_buffered_chunks_peak, 0);
225 assert_eq!(desc.parallel_buffered_bytes_peak, 0);
226 assert!(desc.parallel_fallback_reason.is_empty());
227 }
228
229 #[test]
230 fn plan_description_p8_fields_round_trip_serialize() {
231 let mut desc = PlanDescription::new();
232 desc.requested_workers = 4;
233 desc.actual_workers = 2;
234 desc.parallel_wall_time_us = 1000;
235 desc.parallel_work_time_us = 2000;
236 desc.parallel_buffered_chunks_peak = 3;
237 desc.parallel_buffered_bytes_peak = 8192;
238 desc.parallel_fallback_reason = "test fallback".to_string();
239
240 let json = serde_json::to_string(&desc).expect("serialize");
241 let restored: PlanDescription = serde_json::from_str(&json).expect("deserialize");
242
243 assert_eq!(restored.requested_workers, 4);
244 assert_eq!(restored.actual_workers, 2);
245 assert_eq!(restored.parallel_wall_time_us, 1000);
246 assert_eq!(restored.parallel_work_time_us, 2000);
247 assert_eq!(restored.parallel_buffered_chunks_peak, 3);
248 assert_eq!(restored.parallel_buffered_bytes_peak, 8192);
249 assert_eq!(restored.parallel_fallback_reason, "test fallback");
250 }
251}