Skip to content

Commit ddb0ade

Browse files
azatclaude
andcommitted
Implement per-part logs (from merges/mutations) for system.tables/part_log
Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 218cbf5 commit ddb0ade

File tree

2 files changed

+191
-35
lines changed

2 files changed

+191
-35
lines changed

src/view/providers/part_log.rs

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use crate::{
2-
interpreter::{ContextArc, options::ChDigViews},
3-
view::{self, Navigation, ViewProvider},
2+
actions::ActionDescription,
3+
common::RelativeDateTime,
4+
interpreter::{ContextArc, TextLogArguments, options::ChDigViews},
5+
utils::fuzzy_actions,
6+
view::{self, Navigation, TextLogView, ViewProvider},
47
};
58
use cursive::{
69
Cursive,
10+
event::Event,
711
view::{Nameable, Resizable},
8-
views::{Dialog, DummyView, LinearLayout, TextView},
12+
views::{Dialog, DummyView, LinearLayout, NamedView, TextView},
913
};
1014
use std::collections::HashMap;
1115

@@ -128,7 +132,8 @@ fn build_query(context: &ContextArc, filters: &FilterParams, is_dialog: bool) ->
128132
size_in_bytes,
129133
duration_ms,
130134
peak_memory_usage,
131-
exception"#
135+
exception,
136+
table_uuid::String _table_uuid"#
132137
} else {
133138
r#"event_time,
134139
event_type::String event_type,
@@ -141,7 +146,8 @@ fn build_query(context: &ContextArc, filters: &FilterParams, is_dialog: bool) ->
141146
size_in_bytes,
142147
duration_ms,
143148
peak_memory_usage,
144-
exception"#
149+
exception,
150+
table_uuid::String _table_uuid"#
145151
};
146152

147153
format!(
@@ -177,6 +183,7 @@ fn get_columns(is_dialog: bool) -> (Vec<&'static str>, Vec<&'static str>) {
177183
"duration_ms",
178184
"peak_memory_usage",
179185
"exception",
186+
"_table_uuid",
180187
]
181188
} else {
182189
vec![
@@ -192,12 +199,48 @@ fn get_columns(is_dialog: bool) -> (Vec<&'static str>, Vec<&'static str>) {
192199
"duration_ms",
193200
"peak_memory_usage",
194201
"exception",
202+
"_table_uuid",
195203
]
196204
};
197205
let columns_to_compare = vec!["event_time", "event_type", "part_name"];
198206
(columns, columns_to_compare)
199207
}
200208

209+
fn show_part_logs(siv: &mut Cursive, columns: Vec<&'static str>, row: view::QueryResultRow) {
210+
let mut map = HashMap::new();
211+
columns.iter().zip(row.0.iter()).for_each(|(c, r)| {
212+
map.insert(c.to_string(), r);
213+
});
214+
215+
let context = siv.user_data::<ContextArc>().unwrap().clone();
216+
siv.add_layer(Dialog::around(
217+
LinearLayout::vertical()
218+
.child(TextView::new("Logs:").center())
219+
.child(DummyView.fixed_height(1))
220+
.child(NamedView::new(
221+
"part_logs",
222+
TextLogView::new(
223+
"part_logs",
224+
context,
225+
TextLogArguments {
226+
query_ids: Some(vec![format!(
227+
"{}::{}",
228+
map["_table_uuid"].to_string(),
229+
map["part_name"].to_string()
230+
)]),
231+
logger_names: None,
232+
hostname: None,
233+
message_filter: None,
234+
max_level: None,
235+
start: map["event_time"].as_datetime().unwrap(),
236+
end: RelativeDateTime::new(None),
237+
},
238+
),
239+
)),
240+
));
241+
siv.focus_name("part_logs").unwrap();
242+
}
243+
201244
fn show_part_details(siv: &mut Cursive, columns: Vec<&'static str>, row: view::QueryResultRow) {
202245
let row_data = row.0;
203246
let mut map = HashMap::<String, String>::new();
@@ -217,6 +260,36 @@ fn show_part_details(siv: &mut Cursive, columns: Vec<&'static str>, row: view::Q
217260
siv.add_layer(Dialog::info(info).title("Part Log Details"));
218261
}
219262

263+
fn part_log_action_callback(
264+
siv: &mut Cursive,
265+
columns: Vec<&'static str>,
266+
row: view::QueryResultRow,
267+
) {
268+
let actions = vec![
269+
ActionDescription {
270+
text: "Show part logs",
271+
event: Event::Unknown(vec![]),
272+
},
273+
ActionDescription {
274+
text: "Show part details",
275+
event: Event::Unknown(vec![]),
276+
},
277+
];
278+
279+
let columns_clone = columns.clone();
280+
let row_clone = row.clone();
281+
282+
fuzzy_actions(siv, actions, move |siv, selected| match selected.as_str() {
283+
"Show part logs" => {
284+
show_part_logs(siv, columns_clone.clone(), row_clone.clone());
285+
}
286+
"Show part details" => {
287+
show_part_details(siv, columns_clone.clone(), row_clone.clone());
288+
}
289+
_ => {}
290+
});
291+
}
292+
220293
pub fn show_part_log(
221294
siv: &mut Cursive,
222295
context: ContextArc,
@@ -249,7 +322,7 @@ pub fn show_part_log(
249322
)
250323
.unwrap_or_else(|_| panic!("Cannot create {}", view_name));
251324

252-
view.get_inner_mut().set_on_submit(show_part_details);
325+
view.get_inner_mut().set_on_submit(part_log_action_callback);
253326

254327
let title = filters.build_title(false);
255328

@@ -289,7 +362,9 @@ pub fn show_part_log_dialog(
289362
)
290363
.unwrap_or_else(|_| panic!("Cannot create {}", view_name));
291364

292-
sql_view.get_inner_mut().set_on_submit(show_part_details);
365+
sql_view
366+
.get_inner_mut()
367+
.set_on_submit(part_log_action_callback);
293368

294369
let title = filters.build_title(true);
295370

src/view/providers/table_parts.rs

Lines changed: 109 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use crate::{
2-
interpreter::{ContextArc, options::ChDigViews},
3-
view::{self, Navigation, ViewProvider},
2+
actions::ActionDescription,
3+
common::RelativeDateTime,
4+
interpreter::{ContextArc, TextLogArguments, options::ChDigViews},
5+
utils::fuzzy_actions,
6+
view::{self, Navigation, TextLogView, ViewProvider},
47
};
58
use cursive::{
69
Cursive,
10+
event::Event,
711
view::{Nameable, Resizable},
8-
views::{Dialog, DummyView, LinearLayout, TextView},
12+
views::{Dialog, DummyView, LinearLayout, NamedView, TextView},
913
};
1014
use std::collections::HashMap;
1115

@@ -30,11 +34,12 @@ fn build_query(
3034
filters: &super::TableFilterParams,
3135
is_dialog: bool,
3236
) -> String {
33-
let (limit, dbtable, clickhouse, selected_host) = {
37+
let (limit, parts_dbtable, tables_dbtable, clickhouse, selected_host) = {
3438
let ctx = context.lock().unwrap();
3539
(
3640
ctx.options.clickhouse.limit,
3741
ctx.clickhouse.get_table_name("system", "parts"),
42+
ctx.clickhouse.get_table_name("system", "tables"),
3843
ctx.clickhouse.clone(),
3944
ctx.selected_host.clone(),
4045
)
@@ -50,42 +55,48 @@ fn build_query(
5055
let where_clause = if where_clauses.is_empty() {
5156
String::new()
5257
} else {
53-
format!("WHERE {}", where_clauses.join(" AND "))
58+
format!(" WHERE {}", where_clauses.join(" AND "))
5459
};
5560

5661
let select_clause = if is_dialog {
57-
r#"name,
58-
partition,
59-
rows,
60-
bytes_on_disk,
61-
data_compressed_bytes,
62-
data_uncompressed_bytes,
63-
modification_time,
64-
active"#
62+
r#"parts.name,
63+
parts.partition,
64+
parts.rows,
65+
parts.bytes_on_disk,
66+
parts.data_compressed_bytes,
67+
parts.data_uncompressed_bytes,
68+
parts.modification_time,
69+
parts.active,
70+
tables.uuid::String _table_uuid"#
6571
} else {
66-
r#"database,
67-
table,
68-
name,
69-
partition,
70-
rows,
71-
bytes_on_disk,
72-
data_compressed_bytes,
73-
data_uncompressed_bytes,
74-
modification_time,
75-
active"#
72+
r#"parts.database,
73+
parts.table,
74+
parts.name,
75+
parts.partition,
76+
parts.rows,
77+
parts.bytes_on_disk,
78+
parts.data_compressed_bytes,
79+
parts.data_uncompressed_bytes,
80+
parts.modification_time,
81+
parts.active,
82+
tables.uuid::String _table_uuid"#
7683
};
7784

7885
format!(
7986
r#"
8087
SELECT
8188
{select_clause}
82-
FROM {dbtable}
89+
FROM {parts_dbtable} as parts
90+
LEFT JOIN (SELECT DISTINCT ON (database, name) database, name, uuid FROM {tables_dbtable}) tables
91+
ON parts.database = tables.database AND parts.table = tables.name
8392
{where_clause}
84-
ORDER BY modification_time DESC
93+
ORDER BY parts.modification_time DESC
8594
LIMIT {limit}
95+
SETTINGS allow_experimental_analyzer=1
8696
"#,
8797
select_clause = select_clause,
88-
dbtable = dbtable,
98+
parts_dbtable = parts_dbtable,
99+
tables_dbtable = tables_dbtable,
89100
where_clause = where_clause,
90101
limit = limit,
91102
)
@@ -102,6 +113,7 @@ fn get_columns(is_dialog: bool) -> (Vec<&'static str>, Vec<&'static str>) {
102113
"data_uncompressed_bytes",
103114
"modification_time",
104115
"active",
116+
"_table_uuid",
105117
]
106118
} else {
107119
vec![
@@ -115,12 +127,48 @@ fn get_columns(is_dialog: bool) -> (Vec<&'static str>, Vec<&'static str>) {
115127
"data_uncompressed_bytes",
116128
"modification_time",
117129
"active",
130+
"_table_uuid",
118131
]
119132
};
120133
let columns_to_compare = vec!["name"];
121134
(columns, columns_to_compare)
122135
}
123136

137+
fn show_part_logs(siv: &mut Cursive, columns: Vec<&'static str>, row: view::QueryResultRow) {
138+
let mut map = HashMap::new();
139+
columns.iter().zip(row.0.iter()).for_each(|(c, r)| {
140+
map.insert(c.to_string(), r);
141+
});
142+
143+
let context = siv.user_data::<ContextArc>().unwrap().clone();
144+
siv.add_layer(Dialog::around(
145+
LinearLayout::vertical()
146+
.child(TextView::new("Logs:").center())
147+
.child(DummyView.fixed_height(1))
148+
.child(NamedView::new(
149+
"part_logs",
150+
TextLogView::new(
151+
"part_logs",
152+
context,
153+
TextLogArguments {
154+
query_ids: Some(vec![format!(
155+
"{}::{}",
156+
map["_table_uuid"].to_string(),
157+
map["name"].to_string()
158+
)]),
159+
logger_names: None,
160+
hostname: None,
161+
message_filter: None,
162+
max_level: None,
163+
start: map["modification_time"].as_datetime().unwrap(),
164+
end: RelativeDateTime::new(None),
165+
},
166+
),
167+
)),
168+
));
169+
siv.focus_name("part_logs").unwrap();
170+
}
171+
124172
fn show_part_details(siv: &mut Cursive, columns: Vec<&'static str>, row: view::QueryResultRow) {
125173
let row_data = row.0;
126174
let mut map = HashMap::<String, String>::new();
@@ -140,6 +188,36 @@ fn show_part_details(siv: &mut Cursive, columns: Vec<&'static str>, row: view::Q
140188
siv.add_layer(Dialog::info(info).title("Part Details"));
141189
}
142190

191+
fn table_parts_action_callback(
192+
siv: &mut Cursive,
193+
columns: Vec<&'static str>,
194+
row: view::QueryResultRow,
195+
) {
196+
let actions = vec![
197+
ActionDescription {
198+
text: "Show part logs",
199+
event: Event::Unknown(vec![]),
200+
},
201+
ActionDescription {
202+
text: "Show part details",
203+
event: Event::Unknown(vec![]),
204+
},
205+
];
206+
207+
let columns_clone = columns.clone();
208+
let row_clone = row.clone();
209+
210+
fuzzy_actions(siv, actions, move |siv, selected| match selected.as_str() {
211+
"Show part logs" => {
212+
show_part_logs(siv, columns_clone.clone(), row_clone.clone());
213+
}
214+
"Show part details" => {
215+
show_part_details(siv, columns_clone.clone(), row_clone.clone());
216+
}
217+
_ => {}
218+
});
219+
}
220+
143221
pub fn show_table_parts(
144222
siv: &mut Cursive,
145223
context: ContextArc,
@@ -167,7 +245,8 @@ pub fn show_table_parts(
167245
)
168246
.unwrap_or_else(|_| panic!("Cannot create {}", view_name));
169247

170-
view.get_inner_mut().set_on_submit(show_part_details);
248+
view.get_inner_mut()
249+
.set_on_submit(table_parts_action_callback);
171250

172251
let title = filters.build_title(false);
173252

@@ -202,7 +281,9 @@ pub fn show_table_parts_dialog(
202281
)
203282
.unwrap_or_else(|_| panic!("Cannot create {}", view_name));
204283

205-
sql_view.get_inner_mut().set_on_submit(show_part_details);
284+
sql_view
285+
.get_inner_mut()
286+
.set_on_submit(table_parts_action_callback);
206287

207288
let title = filters.build_title(true);
208289

0 commit comments

Comments
 (0)