diff --git a/src/main.rs b/src/main.rs index 090767a..075ef3a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -316,7 +316,7 @@ mod sqlite { let modified = Some(metadata.modified()?.into()); let created = metadata.created().ok().map(Into::into); - let (tables, indexes, triggers, views, row_counts) = self + let (tables, indexes, triggers, views, row_counts, column_counts) = self .conn .call(move |conn| { let tables = conn.query_row( @@ -358,16 +358,16 @@ mod sqlite { let mut stmt = conn.prepare(r#"SELECT name FROM sqlite_master WHERE type="table""#)?; let table_names = stmt.query_map([], |row| row.get::<_, String>(0))?; + let table_names = table_names.collect::, _>>()?; let mut row_counts = HashMap::with_capacity(tables as usize); - for name in table_names { - let name = name?; + for name in table_names.iter() { let count = conn.query_row(&format!("SELECT count(*) FROM '{name}'"), (), |r| { r.get::<_, i32>(0) })?; - row_counts.insert(name, count); + row_counts.insert(name.to_owned(), count); } let mut row_counts = row_counts @@ -377,7 +377,23 @@ mod sqlite { row_counts.sort_by(|a, b| b.count.cmp(&a.count)); - Ok((tables, indexes, triggers, views, row_counts)) + let mut column_counts = HashMap::with_capacity(tables as usize); + for name in table_names.iter() { + let mut columns = conn.prepare(&format!("PRAGMA table_info('{name}')"))?; + let count = + columns.query_map((), |r| r.get::<_, String>(1))?.count() as i32; + + column_counts.insert(name.to_owned(), count); + } + + let mut column_counts = column_counts + .into_iter() + .map(|(name, count)| responses::Count { name, count }) + .collect::>(); + + column_counts.sort_by(|a, b| b.count.cmp(&a.count)); + + Ok((tables, indexes, triggers, views, row_counts, column_counts)) }) .await?; @@ -392,6 +408,7 @@ mod sqlite { triggers, views, row_counts, + column_counts, }) } @@ -741,7 +758,7 @@ mod libsql { .collect::>(); let mut row_counts = HashMap::with_capacity(table_names.len()); - for name in table_names { + for name in table_names.iter() { let count = conn .query(&format!("SELECT count(*) FROM '{name}'"), ()) .await? @@ -750,7 +767,7 @@ mod libsql { .ok_or_eyre("no row returned from db")? .get::(0)?; - row_counts.insert(name, count); + row_counts.insert(name.to_owned(), count); } let mut row_counts = row_counts @@ -760,6 +777,30 @@ mod libsql { row_counts.sort_by(|a, b| b.count.cmp(&a.count)); + let mut column_counts = HashMap::with_capacity(table_names.len()); + for name in table_names.iter() { + let count = conn + .query(&format!("PRAGMA table_info('{name}')"), ()) + .await? + .into_stream() + .map_ok(|r| r.get::(1)) + .collect::>() + .await + .into_iter() + .filter_map(|r| r.ok()) + .filter_map(|r| r.ok()) + .count() as i32; + + column_counts.insert(name.to_owned(), count); + } + + let mut column_counts = column_counts + .into_iter() + .map(|(name, count)| responses::Count { name, count }) + .collect::>(); + + column_counts.sort_by(|a, b| b.count.cmp(&a.count)); + Ok(responses::Overview { file_name, sqlite_version: Some(sqlite_version), @@ -771,6 +812,7 @@ mod libsql { triggers, views, row_counts, + column_counts, }) } @@ -1137,7 +1179,7 @@ mod postgres { .await? .get(0); - let mut table_counts = self + let mut row_counts = self .client .query( r#" @@ -1155,7 +1197,7 @@ mod postgres { }) .collect::>(); - for table in table_counts.iter_mut() { + for table in row_counts.iter_mut() { let count: i64 = self .client .query_one(&format!(r#"SELECT count(*) FROM "{}""#, table.name), &[]) @@ -1164,7 +1206,48 @@ mod postgres { table.count = count as i32; } - table_counts.sort_by(|a, b| b.count.cmp(&a.count)); + row_counts.sort_by(|a, b| b.count.cmp(&a.count)); + + let mut column_counts = self + .client + .query( + r#" + SELECT relname + FROM pg_stat_user_tables + WHERE schemaname = 'public' + "#, + &[], + ) + .await? + .into_iter() + .map(|r| Count { + name: r.get(0), + count: 0, + }) + .collect::>(); + + for table in column_counts.iter_mut() { + let count: i64 = self + .client + .query_one( + &format!( + r#" + SELECT count(*) + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = '{}' + "#, + table.name + ), + &[], + ) + .await? + .get(0); + + table.count = count as i32; + } + + column_counts.sort_by(|a, b| b.count.cmp(&a.count)); Ok(responses::Overview { file_name, @@ -1176,7 +1259,8 @@ mod postgres { indexes: indexes as i32, triggers: triggers as i32, views: views as i32, - row_counts: table_counts, + row_counts, + column_counts, }) } @@ -1504,6 +1588,32 @@ mod mysql { row_counts.sort_by(|a, b| b.count.cmp(&a.count)); + let mut column_counts = r#" + SELECT TABLE_NAME AS name + FROM information_schema.tables + WHERE table_schema = database() + "# + .with(()) + .map(&mut conn, |name| Count { name, count: 0 }) + .await?; + + for count in column_counts.iter_mut() { + count.count = r#" + SELECT count(*) AS count + FROM information_schema.columns + WHERE table_schema = database() AND table_name = :table_name + "# + .with(params! { + "table_name" => &count.name + }) + .first(&mut conn) + .await? + .map(|count: i32| count) + .ok_or_eyre("couldn't count columns")?; + } + + column_counts.sort_by(|a, b| b.count.cmp(&a.count)); + Ok(responses::Overview { file_name, sqlite_version: None, @@ -1515,6 +1625,7 @@ mod mysql { triggers, views, row_counts, + column_counts, }) } @@ -1793,7 +1904,7 @@ mod duckdb { let created = metadata.created().ok().map(Into::into); let c = self.conn.clone(); - let (tables, indexes, triggers, views, row_counts) = + let (tables, indexes, triggers, views, row_counts, column_counts) = tokio::task::spawn_blocking(move || { let c = c.lock().expect("could not get lock on connection"); @@ -1843,18 +1954,37 @@ mod duckdb { .collect::>(); let mut row_counts = Vec::with_capacity(table_names.len()); - for name in table_names { + for name in table_names.iter() { let count: i32 = c.query_row(&format!(r#"SELECT count(*) FROM "{name}""#), [], |row| { row.get(0) })?; - row_counts.push(Count { name, count }); + row_counts.push(Count { + name: name.to_owned(), + count, + }); } row_counts.sort_by(|a, b| b.count.cmp(&a.count)); - eyre::Ok((tables, indexes, triggers, views, row_counts)) + let mut column_counts = Vec::with_capacity(table_names.len()); + for name in table_names.iter() { + let count: i32 = c.query_row( + "SELECT column_count FROM duckdb_tables WHERE table_name = ?", + [&name], + |row| row.get(0), + )?; + + column_counts.push(Count { + name: name.to_owned(), + count, + }); + } + + column_counts.sort_by(|a, b| b.count.cmp(&a.count)); + + eyre::Ok((tables, indexes, triggers, views, row_counts, column_counts)) }) .await??; @@ -1869,6 +1999,7 @@ mod duckdb { triggers, views, row_counts, + column_counts, }) } @@ -2120,6 +2251,7 @@ mod responses { pub triggers: i32, pub views: i32, pub row_counts: Vec, + pub column_counts: Vec, } #[derive(Serialize)]