rust_ef_mysql/
introspection.rs1use rust_ef::error::EFResult;
4use sqlx::Row;
5
6#[derive(Debug, Clone)]
8pub struct DbColumn {
9 pub name: String,
10 pub data_type: String,
11 pub is_nullable: bool,
12 pub is_primary_key: bool,
13 pub max_length: Option<usize>,
14}
15
16#[derive(Debug, Clone)]
18pub struct DbTable {
19 pub name: String,
20 pub columns: Vec<DbColumn>,
21}
22
23pub async fn introspect_mysql(connection_string: &str) -> EFResult<Vec<DbTable>> {
25 let pool = sqlx::MySqlPool::connect(connection_string)
26 .await
27 .map_err(|e| rust_ef::error::EFError::connection(format!("MySQL connect failed: {e}")))?;
28
29 let table_rows = sqlx::query(
30 "SELECT table_name FROM information_schema.tables \
31 WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE' \
32 AND table_name NOT LIKE '__ef_%' \
33 ORDER BY table_name",
34 )
35 .fetch_all(&pool)
36 .await
37 .map_err(|e| rust_ef::error::EFError::query(format!("Table query error: {e}")))?;
38
39 let mut tables = Vec::new();
40 for table_row in table_rows {
41 let table_name: String = table_row.try_get(0).map_err(map_row_err)?;
42
43 let col_rows = sqlx::query(
44 "SELECT c.column_name, c.data_type, c.is_nullable, c.character_maximum_length, \
45 CASE WHEN k.column_name IS NOT NULL THEN 1 ELSE 0 END AS is_pk \
46 FROM information_schema.columns c \
47 LEFT JOIN information_schema.key_column_usage k \
48 ON c.table_schema = k.table_schema \
49 AND c.table_name = k.table_name \
50 AND c.column_name = k.column_name \
51 AND k.constraint_name = 'PRIMARY' \
52 WHERE c.table_schema = DATABASE() AND c.table_name = ? \
53 ORDER BY c.ordinal_position",
54 )
55 .bind(&table_name)
56 .fetch_all(&pool)
57 .await
58 .map_err(|e| rust_ef::error::EFError::query(format!("Column query error: {e}")))?;
59
60 let mut columns = Vec::new();
61 for col_row in col_rows {
62 let col_name: String = col_row.try_get(0).map_err(map_row_err)?;
63 let data_type: String = col_row.try_get(1).map_err(map_row_err)?;
64 let is_nullable_str: String = col_row.try_get(2).map_err(map_row_err)?;
65 let max_length: Option<i64> = col_row.try_get(3).ok();
66 let is_pk: i32 = col_row.try_get(4).map_err(map_row_err)?;
67
68 columns.push(DbColumn {
69 name: col_name,
70 data_type,
71 is_nullable: is_nullable_str == "YES",
72 is_primary_key: is_pk != 0,
73 max_length: max_length.map(|n| n as usize),
74 });
75 }
76
77 tables.push(DbTable {
78 name: table_name,
79 columns,
80 });
81 }
82
83 Ok(tables)
84}
85
86fn map_row_err(e: sqlx::Error) -> rust_ef::error::EFError {
87 rust_ef::error::EFError::query(format!("Row read error: {e}"))
88}