From 6d55e9d7a98a729297e2e198a370c3326f061351 Mon Sep 17 00:00:00 2001 From: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:23:56 -0700 Subject: [PATCH 1/5] feat(queries): add result_id to list table, database_id to detail view - queries list now shows RESULT_ID column alongside query ID, replacing DURATION_MS (still visible in queries get detail view) - SQL truncation reduced from 60 to 40 chars to fit the wider table - QueryRun struct gains database_id field (serde default) so the detail view will surface it automatically once the API starts returning it --- src/queries.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/queries.rs b/src/queries.rs index a007780..378a568 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -125,6 +125,8 @@ struct QueryRun { sql_hash: String, sql_text: String, result_id: Option, + #[serde(default)] + database_id: Option, error_message: Option, warning_message: Option, trace_id: Option, @@ -193,18 +195,22 @@ pub fn list( r.id.clone(), color_status(&r.status), crate::util::format_date(&r.created_at), - r.execution_time_ms - .map(|ms| ms.to_string()) - .unwrap_or_else(|| "-".to_string()), r.row_count .map(|n| n.to_string()) .unwrap_or_else(|| "-".to_string()), - truncate_sql(&r.sql_text, 60), + r.result_id + .as_deref() + .map(|id| { + let prefix: String = id.chars().take(8).collect(); + format!("{prefix}…") + }) + .unwrap_or_else(|| "-".to_string()), + truncate_sql(&r.sql_text, 40), ] }) .collect(); crate::table::print( - &["ID", "STATUS", "CREATED", "DURATION_MS", "ROWS", "SQL"], + &["ID", "STATUS", "CREATED", "ROWS", "RESULT", "SQL"], &rows, ); } @@ -259,6 +265,9 @@ fn print_detail(r: &QueryRun, format: &str) { if let Some(ref id) = r.result_id { println!("{}{}", label("result id:"), id); } + if let Some(ref id) = r.database_id { + println!("{}{}", label("database id:"), id); + } if let Some(ref id) = r.saved_query_id { let version = r .saved_query_version From 782a5e0ab6a72730ed1333bb099986f9a069ce2d Mon Sep 17 00:00:00 2001 From: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:29:58 -0700 Subject: [PATCH 2/5] feat(queries): show full result_id in queries list table --- src/queries.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/queries.rs b/src/queries.rs index 378a568..81f1893 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -198,19 +198,13 @@ pub fn list( r.row_count .map(|n| n.to_string()) .unwrap_or_else(|| "-".to_string()), - r.result_id - .as_deref() - .map(|id| { - let prefix: String = id.chars().take(8).collect(); - format!("{prefix}…") - }) - .unwrap_or_else(|| "-".to_string()), + r.result_id.as_deref().unwrap_or("-").to_string(), truncate_sql(&r.sql_text, 40), ] }) .collect(); crate::table::print( - &["ID", "STATUS", "CREATED", "ROWS", "RESULT", "SQL"], + &["ID", "STATUS", "CREATED", "ROWS", "RESULT_ID", "SQL"], &rows, ); } From 4b58809d308668b96bf2d30108e6499a11b627a5 Mon Sep 17 00:00:00 2001 From: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:32:35 -0700 Subject: [PATCH 3/5] feat(results): color status and add adaptive columns to results list --- src/results.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/results.rs b/src/results.rs index d1ff56f..c7434b2 100644 --- a/src/results.rs +++ b/src/results.rs @@ -1,4 +1,5 @@ use crate::api::ApiClient; +use crossterm::style::{Color, Stylize}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] @@ -6,6 +7,12 @@ struct ResultEntry { id: String, status: String, created_at: String, + #[serde(default)] + row_count: Option, + #[serde(default)] + query_run_id: Option, + #[serde(default)] + expires_at: Option, } #[derive(Deserialize)] @@ -15,6 +22,17 @@ struct ListResponse { has_more: bool, } +fn color_status(status: &str) -> String { + let color = match status { + "ready" => Color::Green, + "failed" => Color::Red, + "pending" | "processing" => Color::Yellow, + "expired" => Color::DarkGrey, + _ => Color::Reset, + }; + status.with(color).to_string() +} + pub fn list(workspace_id: &str, limit: Option, offset: Option, format: &str) { let api = ApiClient::new(Some(workspace_id)); @@ -29,25 +47,48 @@ pub fn list(workspace_id: &str, limit: Option, offset: Option, format: "yaml" => print!("{}", serde_yaml::to_string(&body.results).unwrap()), "table" => { if body.results.is_empty() { - use crossterm::style::Stylize; eprintln!("{}", "No results found.".dark_grey()); } else { + let has_rows = body.results.iter().any(|r| r.row_count.is_some()); + let has_query_run = body.results.iter().any(|r| r.query_run_id.is_some()); + let has_expires = body.results.iter().any(|r| r.expires_at.is_some()); + let rows: Vec> = body .results .iter() .map(|r| { - vec![ + let mut row = vec![ r.id.clone(), - r.status.clone(), + color_status(&r.status), crate::util::format_date(&r.created_at), - ] + ]; + if has_rows { + row.push(r.row_count.map(|n| n.to_string()).unwrap_or_else(|| "-".to_string())); + } + if has_query_run { + row.push(r.query_run_id.as_deref().unwrap_or("-").to_string()); + } + if has_expires { + row.push( + r.expires_at + .as_deref() + .map(|s| crate::util::format_date(s)) + .unwrap_or_else(|| "-".to_string()), + ); + } + row }) .collect(); - crate::table::print(&["ID", "STATUS", "CREATED AT"], &rows); + + let mut headers = vec!["ID", "STATUS", "CREATED"]; + if has_rows { headers.push("ROWS"); } + if has_query_run { headers.push("QUERY_RUN_ID"); } + if has_expires { headers.push("EXPIRES"); } + + crate::table::print(&headers, &rows); } if body.has_more { let next = offset.unwrap_or(0) + body.count as u32; - use crossterm::style::Stylize; eprintln!( "{}", format!( From ece6dea9ec84df8eed834bf18d1c1c5d456442d7 Mon Sep 17 00:00:00 2001 From: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:33:11 -0700 Subject: [PATCH 4/5] feat(queries): add execution time (MS) column to queries list --- src/queries.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/queries.rs b/src/queries.rs index 81f1893..065215b 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -195,6 +195,9 @@ pub fn list( r.id.clone(), color_status(&r.status), crate::util::format_date(&r.created_at), + r.execution_time_ms + .map(|ms| ms.to_string()) + .unwrap_or_else(|| "-".to_string()), r.row_count .map(|n| n.to_string()) .unwrap_or_else(|| "-".to_string()), @@ -204,7 +207,7 @@ pub fn list( }) .collect(); crate::table::print( - &["ID", "STATUS", "CREATED", "ROWS", "RESULT_ID", "SQL"], + &["ID", "STATUS", "CREATED", "MS", "ROWS", "RESULT_ID", "SQL"], &rows, ); } From 5a3de00a35a627546f292f9a3c385c94ec9be4bb Mon Sep 17 00:00:00 2001 From: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:37:25 -0700 Subject: [PATCH 5/5] refactor: consolidate color_status into util, remove duplicate in queries/results --- src/queries.rs | 16 +++------------- src/results.rs | 15 ++------------- src/util.rs | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/queries.rs b/src/queries.rs index 065215b..d130b2c 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -1,5 +1,5 @@ use crate::api::ApiClient; -use crossterm::style::{Color, Stylize}; +use crossterm::style::Stylize; use serde::{Deserialize, Serialize}; const SQL_KEYWORDS: &[&str] = &[ @@ -141,16 +141,6 @@ struct ListResponse { next_cursor: Option, } -fn color_status(status: &str) -> String { - let color = match status { - "succeeded" => Color::Green, - "failed" => Color::Red, - "running" | "queued" | "pending" => Color::Yellow, - _ => Color::Reset, - }; - status.with(color).to_string() -} - fn truncate_sql(sql: &str, max: usize) -> String { let flat = sql.split_whitespace().collect::>().join(" "); if flat.chars().count() <= max { @@ -193,7 +183,7 @@ pub fn list( .map(|r| { vec![ r.id.clone(), - color_status(&r.status), + crate::util::color_status(&r.status), crate::util::format_date(&r.created_at), r.execution_time_ms .map(|ms| ms.to_string()) @@ -241,7 +231,7 @@ fn print_detail(r: &QueryRun, format: &str) { "table" => { let label = |l: &str| format!("{:<14}", l).dark_grey().to_string(); println!("{}{}", label("id:"), r.id); - println!("{}{}", label("status:"), color_status(&r.status)); + println!("{}{}", label("status:"), crate::util::color_status(&r.status)); println!( "{}{}", label("created:"), diff --git a/src/results.rs b/src/results.rs index c7434b2..66f7688 100644 --- a/src/results.rs +++ b/src/results.rs @@ -1,5 +1,5 @@ use crate::api::ApiClient; -use crossterm::style::{Color, Stylize}; +use crossterm::style::Stylize; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] @@ -22,17 +22,6 @@ struct ListResponse { has_more: bool, } -fn color_status(status: &str) -> String { - let color = match status { - "ready" => Color::Green, - "failed" => Color::Red, - "pending" | "processing" => Color::Yellow, - "expired" => Color::DarkGrey, - _ => Color::Reset, - }; - status.with(color).to_string() -} - pub fn list(workspace_id: &str, limit: Option, offset: Option, format: &str) { let api = ApiClient::new(Some(workspace_id)); @@ -59,7 +48,7 @@ pub fn list(workspace_id: &str, limit: Option, offset: Option, format: .map(|r| { let mut row = vec![ r.id.clone(), - color_status(&r.status), + crate::util::color_status(&r.status), crate::util::format_date(&r.created_at), ]; if has_rows { diff --git a/src/util.rs b/src/util.rs index 8bf3b8b..0f422eb 100644 --- a/src/util.rs +++ b/src/util.rs @@ -328,6 +328,20 @@ fn colorize_json_value(v: &str) -> String { format!("{colored}{comma}") } +/// Color a status string for terminal output. Covers vocabulary from both +/// query runs (succeeded/failed/running/queued/pending) and results (ready/expired/processing). +pub fn color_status(status: &str) -> String { + use crossterm::style::{Color, Stylize}; + let color = match status { + "succeeded" | "ready" => Color::Green, + "failed" => Color::Red, + "running" | "queued" | "pending" | "processing" => Color::Yellow, + "expired" => Color::DarkGrey, + _ => Color::Reset, + }; + status.with(color).to_string() +} + /// Format an ISO date string compactly: "2024-03-15 14:23" (no seconds, no timezone). pub fn format_date(s: &str) -> String { let s = s.split('.').next().unwrap_or(s).trim_end_matches('Z');