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 =
60 conn.query_row("SELECT sqlite_version()", [], |row| row.get(0))?;
61
62 let mut stmt = conn.prepare(
64 "SELECT name, type, sql FROM sqlite_master
65 WHERE type IN ('table', 'view')
66 AND name NOT LIKE 'sqlite_%'
67 AND name NOT LIKE 'refinery_%'
68 ORDER BY type DESC, name",
69 )?;
70
71 let table_names: Vec<(String, String, Option<String>)> = stmt
72 .query_map([], |row| {
73 Ok((
74 row.get::<_, String>(0)?,
75 row.get::<_, String>(1)?,
76 row.get::<_, Option<String>>(2)?,
77 ))
78 })?
79 .collect::<Result<Vec<_>, _>>()?;
80
81 let mut tables = Vec::new();
82
83 for (table_name, table_type, sql) in table_names {
84 let columns = self.get_table_columns(conn, &table_name)?;
86
87 let indexes = self.get_table_indexes(conn, &table_name)?;
89
90 let foreign_keys = self.get_table_foreign_keys(conn, &table_name)?;
92
93 tables.push(TableInfo {
94 name: table_name,
95 table_type,
96 columns,
97 indexes,
98 foreign_keys,
99 sql: if include_sql { sql } else { None },
100 });
101 }
102
103 Ok(DatabaseSchema {
104 tables,
105 sqlite_version,
106 })
107 })
108 }
109
110 fn get_table_columns(
112 &self,
113 conn: &rusqlite::Connection,
114 table_name: &str,
115 ) -> Result<Vec<ColumnInfo>> {
116 let mut stmt = conn.prepare(&format!("PRAGMA table_info('{}')", table_name))?;
117
118 let columns: Vec<ColumnInfo> = stmt
119 .query_map([], |row| {
120 Ok(ColumnInfo {
121 name: row.get(1)?,
122 data_type: row.get::<_, String>(2)?.to_uppercase(),
123 nullable: row.get::<_, i32>(3)? == 0,
124 default_value: row.get(4)?,
125 primary_key: row.get::<_, i32>(5)? > 0,
126 })
127 })?
128 .collect::<Result<Vec<_>, _>>()?;
129
130 Ok(columns)
131 }
132
133 fn get_table_indexes(
135 &self,
136 conn: &rusqlite::Connection,
137 table_name: &str,
138 ) -> Result<Vec<IndexInfo>> {
139 let mut stmt = conn.prepare(&format!("PRAGMA index_list('{}')", table_name))?;
141
142 let index_list: Vec<(String, bool)> = stmt
143 .query_map([], |row| {
144 Ok((
145 row.get::<_, String>(1)?,
146 row.get::<_, i32>(2)? == 1, ))
148 })?
149 .collect::<Result<Vec<_>, _>>()?;
150
151 let mut indexes = Vec::new();
152
153 for (index_name, unique) in index_list {
154 if index_name.starts_with("sqlite_autoindex_") {
156 continue;
157 }
158
159 let mut stmt = conn.prepare(&format!("PRAGMA index_info('{}')", index_name))?;
161
162 let columns: Vec<String> = stmt
163 .query_map([], |row| row.get(2))?
164 .collect::<Result<Vec<_>, _>>()?;
165
166 indexes.push(IndexInfo {
167 name: index_name,
168 table_name: table_name.to_string(),
169 unique,
170 columns,
171 });
172 }
173
174 Ok(indexes)
175 }
176
177 fn get_table_foreign_keys(
179 &self,
180 conn: &rusqlite::Connection,
181 table_name: &str,
182 ) -> Result<Vec<ForeignKeyInfo>> {
183 let mut stmt = conn.prepare(&format!("PRAGMA foreign_key_list('{}')", table_name))?;
184
185 let foreign_keys: Vec<ForeignKeyInfo> = stmt
186 .query_map([], |row| {
187 Ok(ForeignKeyInfo {
188 from_column: row.get(3)?,
189 to_table: row.get(2)?,
190 to_column: row.get(4)?,
191 on_update: row.get(5)?,
192 on_delete: row.get(6)?,
193 })
194 })?
195 .collect::<Result<Vec<_>, _>>()?;
196
197 Ok(foreign_keys)
198 }
199
200 pub fn get_table_names(&self) -> Result<Vec<String>> {
202 self.with_conn(|conn| {
203 let mut stmt = conn.prepare(
204 "SELECT name FROM sqlite_master
205 WHERE type = 'table'
206 AND name NOT LIKE 'sqlite_%'
207 AND name NOT LIKE 'refinery_%'
208 ORDER BY name",
209 )?;
210
211 let names: Vec<String> = stmt
212 .query_map([], |row| row.get(0))?
213 .collect::<Result<Vec<_>, _>>()?;
214
215 Ok(names)
216 })
217 }
218}