Skip to content

Commit

Permalink
Merge branch 'query-result-view-on-submit' -- show error trace in sys…
Browse files Browse the repository at this point in the history
…tem.errors

* query-result-view-on-submit:
  Show full error trace on submit action
  Allow arbitrary number of whitespaces in QueryResultView column expressions
  Do not show private columns (starts with "_") in QueryResultView
  Make query_result_view::Row fully public
  Add ability to specify additional settings for QueryResultView
  Implement on_submit for QueryResultView
  • Loading branch information
azat committed Oct 23, 2023
2 parents 2277982 + f6165ce commit d1c069a
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub use process_view::ProcessView;
pub use processes_view::ProcessesView;
pub use processes_view::Type as ProcessesType;
pub use query_result_view::QueryResultView;
pub use query_result_view::Row as QueryResultRow;
pub use summary_view::SummaryView;

pub use ext_table_view::ExtTableView;
Expand Down
127 changes: 103 additions & 24 deletions src/view/navigation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use cursive::{
Cursive, {Rect, Vec2},
};
use cursive_flexi_logger_view::toggle_flexi_logger_debug_console;
use std::collections::HashMap;

fn make_menu_text() -> StyledString {
let mut text = StyledString::new();
Expand Down Expand Up @@ -69,14 +70,17 @@ pub trait Navigation {
fn show_clickhouse_errors(&mut self, context: ContextArc);
fn show_clickhouse_backups(&mut self, context: ContextArc);

fn show_query_result_view(
fn show_query_result_view<F>(
&mut self,
context: ContextArc,
table: &'static str,
filter: Option<&'static str>,
sort_by: &'static str,
columns: &mut Vec<&'static str>,
);
on_submit: Option<F>,
settings: &HashMap<&str, &str>,
) where
F: Fn(&mut Cursive, view::QueryResultRow) + 'static;

// TODO: move into separate trait
fn call_on_name_or_render_error<V, F>(&mut self, name: &str, callback: F)
Expand All @@ -85,6 +89,8 @@ pub trait Navigation {
F: FnOnce(&mut V) -> Result<()>;
}

const QUERY_RESULT_VIEW_NOP_CALLBACK: Option<fn(&mut Cursive, view::QueryResultRow)> = None;

impl Navigation for Cursive {
fn has_view(&mut self, name: &str) -> bool {
return self.focus_name(name).is_ok();
Expand Down Expand Up @@ -648,7 +654,15 @@ impl Navigation for Cursive {
];

// TODO: on_submit show last related log messages
self.show_query_result_view(context, table, None, "elapsed", &mut columns);
self.show_query_result_view(
context,
table,
None,
"elapsed",
&mut columns,
QUERY_RESULT_VIEW_NOP_CALLBACK,
&HashMap::new(),
);
}

fn show_clickhouse_mutations(&mut self, context: ContextArc) {
Expand All @@ -674,6 +688,8 @@ impl Navigation for Cursive {
Some(&"is_done = 0"),
"latest_fail_time",
&mut columns,
QUERY_RESULT_VIEW_NOP_CALLBACK,
&HashMap::new(),
);
}

Expand All @@ -692,7 +708,15 @@ impl Navigation for Cursive {
];

// TODO: on_submit show last related log messages
self.show_query_result_view(context, table, None, "tries", &mut columns);
self.show_query_result_view(
context,
table,
None,
"tries",
&mut columns,
QUERY_RESULT_VIEW_NOP_CALLBACK,
&HashMap::new(),
);
}

fn show_clickhouse_replicated_fetches(&mut self, context: ContextArc) {
Expand All @@ -708,7 +732,15 @@ impl Navigation for Cursive {
];

// TODO: on_submit show last related log messages
self.show_query_result_view(context, table, None, "elapsed", &mut columns);
self.show_query_result_view(
context,
table,
None,
"elapsed",
&mut columns,
QUERY_RESULT_VIEW_NOP_CALLBACK,
&HashMap::new(),
);
}

fn show_clickhouse_replicas(&mut self, context: ContextArc) {
Expand All @@ -724,7 +756,15 @@ impl Navigation for Cursive {
];

// TODO: on_submit show last related log messages
self.show_query_result_view(context, table, None, "queue", &mut columns);
self.show_query_result_view(
context,
table,
None,
"queue",
&mut columns,
QUERY_RESULT_VIEW_NOP_CALLBACK,
&HashMap::new(),
);
}

fn show_clickhouse_errors(&mut self, context: ContextArc) {
Expand All @@ -733,12 +773,24 @@ impl Navigation for Cursive {
"name",
"value",
"last_error_time error_time",
// TODO: on_submit show:
// - last_error_message
// - last_error_trace
"last_error_message _error_message",
"arrayStringConcat(arrayMap(addr -> concat(addressToLine(addr), '::', demangle(addressToSymbol(addr))), last_error_trace), '\n') _error_trace",
];

self.show_query_result_view(context, table, None, "value", &mut columns);
// TODO: on submit show logs from system.query_log/system.text_log, but we need to
// implement wrapping before
self.show_query_result_view(
context,
table,
None,
"value",
&mut columns,
Some(|siv: &mut Cursive, row: view::QueryResultRow| {
let trace = row.0.iter().last().unwrap();
siv.add_layer(Dialog::info(trace.to_string()).title("Error trace"));
}),
&HashMap::from([("allow_introspection_functions", "1")]),
);
}

fn show_clickhouse_backups(&mut self, context: ContextArc) {
Expand All @@ -755,17 +807,29 @@ impl Navigation for Cursive {
// TODO:
// - order by elapsed time
// - on submit - show log entries from text_log
self.show_query_result_view(context, table, None, "total_size", &mut columns);
self.show_query_result_view(
context,
table,
None,
"total_size",
&mut columns,
QUERY_RESULT_VIEW_NOP_CALLBACK,
&HashMap::new(),
);
}

fn show_query_result_view(
fn show_query_result_view<F>(
&mut self,
context: ContextArc,
table: &'static str,
filter: Option<&'static str>,
sort_by: &'static str,
columns: &mut Vec<&'static str>,
) {
on_submit: Option<F>,
settings: &HashMap<&str, &str>,
) where
F: Fn(&mut Cursive, view::QueryResultRow) + 'static,
{
if self.has_view(table) {
return;
}
Expand All @@ -776,25 +840,40 @@ impl Navigation for Cursive {
}

let dbtable = context.lock().unwrap().clickhouse.get_table_name(table);
let settings = if settings.is_empty() {
"".to_string()
} else {
format!(
" SETTINGS {}",
settings
.into_iter()
.map(|kv| format!("{}='{}'", kv.0, kv.1.replace('\'', "\\\'")))
.collect::<Vec<String>>()
.join(",")
)
.to_string()
};
let query = format!(
"select {} from {}{}",
"select {} from {}{}{}",
columns.join(", "),
dbtable,
filter
.and_then(|x| Some(format!(" WHERE {}", x)))
.unwrap_or_default()
.unwrap_or_default(),
settings,
);

self.drop_main_view();
self.set_main_view(
Dialog::around(
view::QueryResultView::new(context.clone(), table, sort_by, columns.clone(), query)
.expect(&format!("Cannot get {}", table))
.with_name(table)
.full_screen(),
)
.title(table),
);

let mut view =
view::QueryResultView::new(context.clone(), table, sort_by, columns.clone(), query)
.expect(&format!("Cannot get {}", table));
if let Some(on_submit) = on_submit {
view.set_on_submit(on_submit);
}
let view = view.with_name(table).full_screen();

self.set_main_view(Dialog::around(view).title(table));
self.focus_name(table).unwrap();
}

Expand Down
42 changes: 33 additions & 9 deletions src/view/query_result_view.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::cmp::Ordering;
use std::rc::Rc;

use anyhow::{anyhow, Result};
use size::{Base, SizeFormatter, Style};
Expand All @@ -7,6 +8,7 @@ use crate::interpreter::{clickhouse::Columns, BackgroundRunner, ContextArc, Work
use crate::view::{ExtTableView, TableViewItem};
use crate::wrap_impl_no_move;
use cursive::view::ViewWrapper;
use cursive::Cursive;

use chrono::DateTime;
use chrono_tz::Tz;
Expand Down Expand Up @@ -59,7 +61,7 @@ impl ToString for Field {
}

#[derive(Clone, Default, Debug)]
pub struct Row(Vec<Field>);
pub struct Row(pub Vec<Field>);

impl PartialEq<Row> for Row {
fn eq(&self, other: &Self) -> bool {
Expand Down Expand Up @@ -103,10 +105,13 @@ impl TableViewItem<u8> for Row {
}
}

type RowCallback = Rc<dyn Fn(&mut Cursive, Row)>;

pub struct QueryResultView {
table: ExtTableView<Row, u8>,

columns: Vec<&'static str>,
on_submit: Option<RowCallback>,

#[allow(unused)]
bg_runner: BackgroundRunner,
Expand Down Expand Up @@ -152,6 +157,13 @@ impl QueryResultView {
return Ok(());
}

pub fn set_on_submit<F>(&mut self, cb: F)
where
F: Fn(&mut Cursive, Row) + 'static,
{
self.on_submit = Some(Rc::new(cb));
}

pub fn new(
context: ContextArc,
view_name: &'static str,
Expand All @@ -175,6 +187,10 @@ impl QueryResultView {
let mut table = ExtTableView::<Row, u8>::default();
let inner_table = table.get_inner_mut().get_inner_mut();
for (i, column) in columns.iter().enumerate() {
// Private column
if column.starts_with("_") {
continue;
}
inner_table.add_column(i as u8, column.to_string(), |c| c);
}
let sort_by_column = columns
Expand All @@ -183,6 +199,18 @@ impl QueryResultView {
.find_map(|(i, c)| if *c == sort_by { Some(i) } else { None })
.expect("sort_by column not found in columns");
inner_table.sort_by(sort_by_column as u8, Ordering::Greater);
inner_table.set_on_submit(|siv, _row, index| {
let (on_submit, item) = siv
.call_on_name(view_name, |table: &mut QueryResultView| {
let inner_table = table.table.get_inner().get_inner();
let item = inner_table.borrow_item(index).unwrap();
return (table.on_submit.clone(), item.clone());
})
.unwrap();
if let Some(on_submit) = on_submit {
on_submit(siv, item);
}
});

let bg_runner_cv = context.lock().unwrap().background_runner_cv.clone();
let mut bg_runner = BackgroundRunner::new(delay, bg_runner_cv);
Expand All @@ -191,6 +219,7 @@ impl QueryResultView {
let view = QueryResultView {
table,
columns,
on_submit: None,
bg_runner,
};
return Ok(view);
Expand All @@ -204,14 +233,9 @@ impl ViewWrapper for QueryResultView {
fn parse_columns(columns: &Vec<&'static str>) -> Vec<&'static str> {
let mut result = Vec::new();
for column in columns.iter() {
let column_parts = column.split(' ').collect::<Vec<&str>>();
let column_name;
match column_parts.len() {
1 => column_name = column,
2 => column_name = &column_parts[1],
_ => unreachable!("Only 'X' or 'X alias_X' is supported in columns list"),
}
result.push(*column_name);
// NOTE: this is broken for "x AS `foo bar`"
let column_name = column.split(' ').last().unwrap();
result.push(column_name);
}
return result;
}

0 comments on commit d1c069a

Please sign in to comment.