1use quarb::{AstAdapter, NodeId, Value};
16use quarb_relational::{RelationalModel, RowSpec, TableSpec};
17use rusqlite::Connection;
18use rusqlite::types::ValueRef;
19
20#[derive(Debug, thiserror::Error)]
22pub enum SqliteError {
23 #[error("sqlite: {0}")]
24 Sqlite(#[from] rusqlite::Error),
25}
26
27pub struct SqliteAdapter {
29 model: RelationalModel,
30}
31
32fn to_value(v: ValueRef<'_>) -> Value {
33 match v {
34 ValueRef::Null => Value::Null,
35 ValueRef::Integer(n) => Value::Int(n),
36 ValueRef::Real(f) => Value::Float(f),
37 ValueRef::Text(t) => Value::Str(String::from_utf8_lossy(t).into_owned()),
38 ValueRef::Blob(b) => Value::Str(format!("<blob {} bytes>", b.len())),
39 }
40}
41
42fn quote_ident(name: &str) -> String {
46 format!("\"{}\"", name.replace('"', "\"\""))
47}
48
49fn specs(conn: &Connection) -> Result<Vec<TableSpec>, SqliteError> {
51 let names: Vec<String> = conn
52 .prepare(
53 "SELECT name FROM sqlite_master WHERE type = 'table' \
54 AND name NOT LIKE 'sqlite_%' ORDER BY name",
55 )?
56 .query_map([], |r| r.get(0))?
57 .collect::<Result<_, _>>()?;
58
59 let mut out = Vec::new();
60 for name in names {
61 let mut columns = Vec::new();
64 let mut pk_cols: Vec<(i64, usize)> = Vec::new();
65 {
66 let mut stmt =
67 conn.prepare(&format!("PRAGMA table_info({})", quote_ident(&name)))?;
68 let mut rows = stmt.query([])?;
69 while let Some(r) = rows.next()? {
70 let col: String = r.get(1)?;
71 let pk: i64 = r.get(5)?;
72 if pk > 0 {
73 pk_cols.push((pk, columns.len()));
74 }
75 columns.push(col);
76 }
77 }
78 pk_cols.sort();
79 let pk = (pk_cols.len() == 1).then(|| pk_cols[0].1);
80
81 let mut fks = Vec::new();
84 {
85 let mut stmt =
86 conn.prepare(&format!("PRAGMA foreign_key_list({})", quote_ident(&name)))?;
87 let mut rows = stmt.query([])?;
88 while let Some(r) = rows.next()? {
89 let target: String = r.get(2)?;
90 let from: String = r.get(3)?;
91 let to: Option<String> = r.get(4)?;
92 if let Some(idx) = columns.iter().position(|c| *c == from) {
93 fks.push((idx, target, to.unwrap_or_default()));
94 }
95 }
96 }
97 out.push(TableSpec {
98 name,
99 columns,
100 pk,
101 fks,
102 });
103 }
104 Ok(out)
105}
106
107fn fetch_rows_where(
110 conn: &Connection,
111 spec: &TableSpec,
112 where_sql: Option<&str>,
113) -> Result<Vec<RowSpec>, SqliteError> {
114 let cols = spec
115 .columns
116 .iter()
117 .map(|c| quote_ident(c))
118 .collect::<Vec<_>>()
119 .join(", ");
120 let table = quote_ident(&spec.name);
121 let filter = match where_sql {
122 Some(w) => format!(" WHERE {w}"),
123 None => String::new(),
124 };
125 let with_rowid = format!("SELECT rowid, {cols} FROM {table}{filter} ORDER BY rowid");
131 if let Ok(mut stmt) = conn.prepare(&with_rowid) {
132 let mut rows = stmt.query([])?;
133 let mut out = Vec::new();
134 while let Some(r) = rows.next()? {
135 let rowid: i64 = r.get(0)?;
136 let values: Vec<Value> = (0..spec.columns.len())
137 .map(|i| to_value(r.get_ref(i + 1).expect("column in range")))
138 .collect();
139 out.push(RowSpec { rowid, values });
140 }
141 return Ok(out);
142 }
143 let order = match spec.pk {
147 Some(i) => quote_ident(&spec.columns[i]),
148 None => cols.clone(),
149 };
150 let mut stmt = conn.prepare(&format!("SELECT {cols} FROM {table}{filter} ORDER BY {order}"))?;
151 let mut rows = stmt.query([])?;
152 let mut out = Vec::new();
153 let mut rowid = 0i64;
154 while let Some(r) = rows.next()? {
155 rowid += 1;
156 let values: Vec<Value> = (0..spec.columns.len())
157 .map(|i| to_value(r.get_ref(i).expect("column in range")))
158 .collect();
159 out.push(RowSpec { rowid, values });
160 }
161 Ok(out)
162}
163
164impl SqliteAdapter {
165 pub fn open(path: &std::path::Path) -> Result<Self, SqliteError> {
169 Self::open_impl(path, None)
170 }
171
172 pub fn open_filtered(
175 path: &std::path::Path,
176 table: &str,
177 where_sql: &str,
178 ) -> Result<Self, SqliteError> {
179 Self::open_impl(path, Some((table.to_string(), where_sql.to_string())))
180 }
181
182 fn open_impl(
183 path: &std::path::Path,
184 filter: Option<(String, String)>,
185 ) -> Result<Self, SqliteError> {
186 let conn = Connection::open_with_flags(path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)?;
187 let specs = specs(&conn)?;
188 let model = RelationalModel::lazy(
189 specs,
190 Box::new(move |_, spec| {
191 let w = filter
192 .as_ref()
193 .filter(|(t, _)| *t == spec.name)
194 .map(|(_, w)| w.as_str());
195 fetch_rows_where(&conn, spec, w).map_err(|e| e.to_string())
196 }),
197 );
198 Ok(SqliteAdapter { model })
199 }
200
201 pub fn load(conn: &Connection) -> Result<Self, SqliteError> {
204 let mut input = Vec::new();
205 for spec in specs(conn)? {
206 let rows = fetch_rows_where(conn, &spec, None)?;
207 input.push((spec, rows));
208 }
209 Ok(SqliteAdapter {
210 model: RelationalModel::build(input),
211 })
212 }
213
214 pub fn locator(&self, node: NodeId) -> String {
216 self.model.locator(node)
217 }
218}
219
220pub fn raw_query(
225 path: &std::path::Path,
226 sql: &str,
227 order_table: Option<&str>,
228) -> Result<(Vec<String>, Vec<Vec<Value>>), SqliteError> {
229 let conn = Connection::open_with_flags(path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)?;
230 let sql = match order_table {
231 Some(t) => {
232 let key = specs(&conn)?
233 .into_iter()
234 .find(|s| s.name == t)
235 .and_then(|s| s.pk.map(|i| s.columns[i].clone()))
236 .unwrap_or_else(|| "rowid".to_string());
237 format!("{sql} ORDER BY {t}.{key}")
238 }
239 None => sql.to_string(),
240 };
241 let mut stmt = conn.prepare(&sql)?;
242 let cols: Vec<String> = stmt.column_names().iter().map(|c| c.to_string()).collect();
243 let n = cols.len();
244 let mut rows = stmt.query([])?;
245 let mut out = Vec::new();
246 while let Some(r) = rows.next()? {
247 out.push(
248 (0..n)
249 .map(|i| to_value(r.get_ref(i).expect("column in range")))
250 .collect(),
251 );
252 }
253 Ok((cols, out))
254}
255
256impl AstAdapter for SqliteAdapter {
257 fn root(&self) -> NodeId {
258 self.model.root()
259 }
260 fn children(&self, node: NodeId) -> Vec<NodeId> {
261 self.model.children(node)
262 }
263 fn name(&self, node: NodeId) -> Option<String> {
264 self.model.name(node)
265 }
266 fn parent(&self, node: NodeId) -> Option<NodeId> {
267 self.model.parent(node)
268 }
269 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
270 self.model.property(node, name)
271 }
272 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
273 self.model.metadata(node, key)
274 }
275 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
276 self.model.resolve(node, property, hint)
277 }
278 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
279 self.model.links(node)
280 }
281 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
282 self.model.backlinks(node)
283 }
284}