1use super::Database;
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ColumnInfo {
10 pub name: String,
11 pub data_type: String,
12 pub nullable: bool,
13 pub default_value: Option<String>,
14 pub primary_key: bool,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct IndexInfo {
20 pub name: String,
21 pub table_name: String,
22 pub unique: bool,
23 pub columns: Vec<String>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ForeignKeyInfo {
29 pub from_column: String,
30 pub to_table: String,
31 pub to_column: String,
32 pub on_update: String,
33 pub on_delete: String,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct TableInfo {
39 pub name: String,
40 pub table_type: String, pub columns: Vec<ColumnInfo>,
42 pub indexes: Vec<IndexInfo>,
43 pub foreign_keys: Vec<ForeignKeyInfo>,
44 pub sql: Option<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct DatabaseSchema {
50 pub tables: Vec<TableInfo>,
51 pub sqlite_version: String,
52}
53
54impl Database {
55 pub fn get_schema(&self, include_sql: bool) -> Result<DatabaseSchema> {
57 self.with_conn(|conn| {
58 let sqlite_version: String = conn.query_row(
60 "SELECT sqlite_version()",
61 [],
62 |row| row.get(0),
63 )?;
64
65 let mut stmt = conn.prepare(
67 "SELECT name, type, sql FROM sqlite_master
68 WHERE type IN ('table', 'view')
69 AND name NOT LIKE 'sqlite_%'
70 AND name NOT LIKE 'refinery_%'
71 ORDER BY type DESC, name"
72 )?;
73
74 let table_names: Vec<(String, String, Option<String>)> = stmt
75 .query_map([], |row| {
76 Ok((
77 row.get::<_, String>(0)?,
78 row.get::<_, String>(1)?,
79 row.get::<_, Option<String>>(2)?,
80 ))
81 })?
82 .collect::<Result<Vec<_>, _>>()?;
83
84 let mut tables = Vec::new();
85
86 for (table_name, table_type, sql) in table_names {
87 let columns = self.get_table_columns(conn, &table_name)?;
89
90 let indexes = self.get_table_indexes(conn, &table_name)?;
92
93 let foreign_keys = self.get_table_foreign_keys(conn, &table_name)?;
95
96 tables.push(TableInfo {
97 name: table_name,
98 table_type,
99 columns,
100 indexes,
101 foreign_keys,
102 sql: if include_sql { sql } else { None },
103 });
104 }
105
106 Ok(DatabaseSchema {
107 tables,
108 sqlite_version,
109 })
110 })
111 }
112
113 fn get_table_columns(&self, conn: &rusqlite::Connection, table_name: &str) -> Result<Vec<ColumnInfo>> {
115 let mut stmt = conn.prepare(&format!("PRAGMA table_info('{}')", table_name))?;
116
117 let columns: Vec<ColumnInfo> = stmt
118 .query_map([], |row| {
119 Ok(ColumnInfo {
120 name: row.get(1)?,
121 data_type: row.get::<_, String>(2)?.to_uppercase(),
122 nullable: row.get::<_, i32>(3)? == 0,
123 default_value: row.get(4)?,
124 primary_key: row.get::<_, i32>(5)? > 0,
125 })
126 })?
127 .collect::<Result<Vec<_>, _>>()?;
128
129 Ok(columns)
130 }
131
132 fn get_table_indexes(&self, conn: &rusqlite::Connection, table_name: &str) -> Result<Vec<IndexInfo>> {
134 let mut stmt = conn.prepare(&format!("PRAGMA index_list('{}')", table_name))?;
136
137 let index_list: Vec<(String, bool)> = stmt
138 .query_map([], |row| {
139 Ok((
140 row.get::<_, String>(1)?,
141 row.get::<_, i32>(2)? == 1, ))
143 })?
144 .collect::<Result<Vec<_>, _>>()?;
145
146 let mut indexes = Vec::new();
147
148 for (index_name, unique) in index_list {
149 if index_name.starts_with("sqlite_autoindex_") {
151 continue;
152 }
153
154 let mut stmt = conn.prepare(&format!("PRAGMA index_info('{}')", index_name))?;
156
157 let columns: Vec<String> = stmt
158 .query_map([], |row| row.get(2))?
159 .collect::<Result<Vec<_>, _>>()?;
160
161 indexes.push(IndexInfo {
162 name: index_name,
163 table_name: table_name.to_string(),
164 unique,
165 columns,
166 });
167 }
168
169 Ok(indexes)
170 }
171
172 fn get_table_foreign_keys(&self, conn: &rusqlite::Connection, table_name: &str) -> Result<Vec<ForeignKeyInfo>> {
174 let mut stmt = conn.prepare(&format!("PRAGMA foreign_key_list('{}')", table_name))?;
175
176 let foreign_keys: Vec<ForeignKeyInfo> = stmt
177 .query_map([], |row| {
178 Ok(ForeignKeyInfo {
179 from_column: row.get(3)?,
180 to_table: row.get(2)?,
181 to_column: row.get(4)?,
182 on_update: row.get(5)?,
183 on_delete: row.get(6)?,
184 })
185 })?
186 .collect::<Result<Vec<_>, _>>()?;
187
188 Ok(foreign_keys)
189 }
190
191 pub fn get_table_names(&self) -> Result<Vec<String>> {
193 self.with_conn(|conn| {
194 let mut stmt = conn.prepare(
195 "SELECT name FROM sqlite_master
196 WHERE type = 'table'
197 AND name NOT LIKE 'sqlite_%'
198 AND name NOT LIKE 'refinery_%'
199 ORDER BY name"
200 )?;
201
202 let names: Vec<String> = stmt
203 .query_map([], |row| row.get(0))?
204 .collect::<Result<Vec<_>, _>>()?;
205
206 Ok(names)
207 })
208 }
209}