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 #[error("pushdown plan: {0}")]
26 Plan(String),
27}
28
29pub struct SqliteAdapter {
31 model: RelationalModel,
32}
33
34fn to_value(v: ValueRef<'_>) -> Value {
35 match v {
36 ValueRef::Null => Value::Null,
37 ValueRef::Integer(n) => Value::Int(n),
38 ValueRef::Real(f) => Value::Float(f),
39 ValueRef::Text(t) => Value::Str(String::from_utf8_lossy(t).into_owned()),
40 ValueRef::Blob(b) => Value::Str(format!("<blob {} bytes>", b.len())),
41 }
42}
43
44fn quote_ident(name: &str) -> String {
48 format!("\"{}\"", name.replace('"', "\"\""))
49}
50
51fn specs(conn: &Connection) -> Result<Vec<TableSpec>, SqliteError> {
53 let names: Vec<String> = conn
54 .prepare(
55 "SELECT name FROM sqlite_master WHERE type = 'table' \
56 AND name NOT LIKE 'sqlite_%' ORDER BY name",
57 )?
58 .query_map([], |r| r.get(0))?
59 .collect::<Result<_, _>>()?;
60
61 let mut out = Vec::new();
62 for name in names {
63 let mut columns = Vec::new();
66 let mut pk_cols: Vec<(i64, usize)> = Vec::new();
67 {
68 let mut stmt = conn.prepare(&format!("PRAGMA table_info({})", quote_ident(&name)))?;
69 let mut rows = stmt.query([])?;
70 while let Some(r) = rows.next()? {
71 let col: String = r.get(1)?;
72 let pk: i64 = r.get(5)?;
73 if pk > 0 {
74 pk_cols.push((pk, columns.len()));
75 }
76 columns.push(col);
77 }
78 }
79 pk_cols.sort();
80 let pk = (pk_cols.len() == 1).then(|| pk_cols[0].1);
81
82 let mut fks = Vec::new();
85 {
86 let mut stmt =
87 conn.prepare(&format!("PRAGMA foreign_key_list({})", quote_ident(&name)))?;
88 let mut rows = stmt.query([])?;
89 while let Some(r) = rows.next()? {
90 let target: String = r.get(2)?;
91 let from: String = r.get(3)?;
92 let to: Option<String> = r.get(4)?;
93 if let Some(idx) = columns.iter().position(|c| *c == from) {
94 fks.push((idx, target, to.unwrap_or_default()));
95 }
96 }
97 }
98 out.push(TableSpec {
99 name,
100 columns,
101 pk,
102 fks,
103 });
104 }
105 Ok(out)
106}
107
108fn fetch_rows_where(
111 conn: &Connection,
112 spec: &TableSpec,
113 where_sql: Option<&str>,
114) -> Result<Vec<RowSpec>, SqliteError> {
115 let cols = spec
116 .columns
117 .iter()
118 .map(|c| quote_ident(c))
119 .collect::<Vec<_>>()
120 .join(", ");
121 let table = quote_ident(&spec.name);
122 let filter = match where_sql {
123 Some(w) => format!(" WHERE {w}"),
124 None => String::new(),
125 };
126 let with_rowid = format!("SELECT rowid, {cols} FROM {table}{filter} ORDER BY rowid");
132 if let Ok(mut stmt) = conn.prepare(&with_rowid) {
133 let mut rows = stmt.query([])?;
134 let mut out = Vec::new();
135 while let Some(r) = rows.next()? {
136 let rowid: i64 = r.get(0)?;
137 let values: Vec<Value> = (0..spec.columns.len())
138 .map(|i| to_value(r.get_ref(i + 1).expect("column in range")))
139 .collect();
140 out.push(RowSpec { rowid, values });
141 }
142 return Ok(out);
143 }
144 let order = match spec.pk {
148 Some(i) => quote_ident(&spec.columns[i]),
149 None => cols.clone(),
150 };
151 let mut stmt = conn.prepare(&format!(
152 "SELECT {cols} FROM {table}{filter} ORDER BY {order}"
153 ))?;
154 let mut rows = stmt.query([])?;
155 let mut out = Vec::new();
156 let mut rowid = 0i64;
157 while let Some(r) = rows.next()? {
158 rowid += 1;
159 let values: Vec<Value> = (0..spec.columns.len())
160 .map(|i| to_value(r.get_ref(i).expect("column in range")))
161 .collect();
162 out.push(RowSpec { rowid, values });
163 }
164 Ok(out)
165}
166
167impl SqliteAdapter {
168 pub fn open(path: &std::path::Path) -> Result<Self, SqliteError> {
172 Self::open_impl(path, None)
173 }
174
175 pub fn open_filtered(
178 path: &std::path::Path,
179 table: &str,
180 where_sql: &str,
181 ) -> Result<Self, SqliteError> {
182 Self::open_impl(path, Some((table.to_string(), where_sql.to_string())))
183 }
184
185 fn open_impl(
186 path: &std::path::Path,
187 filter: Option<(String, String)>,
188 ) -> Result<Self, SqliteError> {
189 let conn = Connection::open_with_flags(path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)?;
190 let specs = specs(&conn)?;
191 let model = RelationalModel::lazy(
192 specs,
193 Box::new(move |_, spec| {
194 let w = filter
195 .as_ref()
196 .filter(|(t, _)| *t == spec.name)
197 .map(|(_, w)| w.as_str());
198 fetch_rows_where(&conn, spec, w).map_err(|e| e.to_string())
199 }),
200 );
201 Ok(SqliteAdapter { model })
202 }
203
204 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SqliteError> {
209 let mut conn = Connection::open_in_memory()?;
210 conn.deserialize_read_exact(rusqlite::MAIN_DB, bytes, bytes.len(), true)?;
211 Self::load(&conn)
212 }
213
214 pub fn load(conn: &Connection) -> Result<Self, SqliteError> {
217 let mut input = Vec::new();
218 for spec in specs(conn)? {
219 let rows = fetch_rows_where(conn, &spec, None)?;
220 input.push((spec, rows));
221 }
222 Ok(SqliteAdapter {
223 model: RelationalModel::build(input),
224 })
225 }
226
227 pub fn locator(&self, node: NodeId) -> String {
229 self.model.locator(node)
230 }
231}
232
233fn unique_key(conn: &Connection, table: &str, cols: &[String]) -> Result<bool, SqliteError> {
241 use std::collections::HashSet;
242 let want: HashSet<&str> = cols.iter().map(|s| s.as_str()).collect();
243 if want.is_empty() {
244 return Ok(false);
245 }
246 let mut stmt = conn.prepare(&format!("PRAGMA table_info({})", quote_ident(table)))?;
248 let mut pk: HashSet<String> = HashSet::new();
249 let mut rows = stmt.query([])?;
250 while let Some(r) = rows.next()? {
251 let name: String = r.get(1)?;
252 let ord: i64 = r.get(5)?;
253 if ord > 0 {
254 pk.insert(name);
255 }
256 }
257 if !pk.is_empty() && pk.iter().all(|c| want.contains(c.as_str())) {
258 return Ok(true);
259 }
260 let mut stmt = conn.prepare(&format!("PRAGMA index_list({})", quote_ident(table)))?;
262 let mut uniques: Vec<String> = Vec::new();
263 let mut rows = stmt.query([])?;
264 while let Some(r) = rows.next()? {
265 let name: String = r.get(1)?;
266 let is_unique: i64 = r.get(2)?;
267 let partial: i64 = r.get(4).unwrap_or(0);
268 if is_unique == 1 && partial == 0 {
269 uniques.push(name);
270 }
271 }
272 for idx in uniques {
273 let mut stmt = conn.prepare(&format!("PRAGMA index_info({})", quote_ident(&idx)))?;
274 let mut cols_of: Vec<String> = Vec::new();
275 let mut rows = stmt.query([])?;
276 while let Some(r) = rows.next()? {
277 cols_of.push(r.get(2)?);
278 }
279 if !cols_of.is_empty() && cols_of.iter().all(|c| want.contains(c.as_str())) {
280 return Ok(true);
281 }
282 }
283 Ok(false)
284}
285
286pub fn raw_query(
287 path: &std::path::Path,
288 sql: &str,
289 order_table: Option<&str>,
290 join_left: Option<(&str, &[String])>,
291) -> Result<(Vec<String>, Vec<Vec<Value>>), SqliteError> {
292 let conn = Connection::open_with_flags(path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)?;
293 if let Some((table, cols)) = join_left
298 && !unique_key(&conn, table, cols)?
299 {
300 return Err(SqliteError::Plan(format!(
301 "join ON does not bind {table} by a unique key; \
302 the SQL JOIN would multiply rows"
303 )));
304 }
305 let sql = match order_table {
306 Some(t) => {
307 let key = specs(&conn)?
308 .into_iter()
309 .find(|s| s.name == t)
310 .and_then(|s| s.pk.map(|i| s.columns[i].clone()))
311 .unwrap_or_else(|| "rowid".to_string());
312 format!("{sql} ORDER BY {t}.{key}")
313 }
314 None => sql.to_string(),
315 };
316 let mut stmt = conn.prepare(&sql)?;
317 let cols: Vec<String> = stmt.column_names().iter().map(|c| c.to_string()).collect();
318 let n = cols.len();
319 let mut rows = stmt.query([])?;
320 let mut out = Vec::new();
321 while let Some(r) = rows.next()? {
322 out.push(
323 (0..n)
324 .map(|i| to_value(r.get_ref(i).expect("column in range")))
325 .collect(),
326 );
327 }
328 Ok((cols, out))
329}
330
331impl AstAdapter for SqliteAdapter {
332 fn root(&self) -> NodeId {
333 self.model.root()
334 }
335 fn children(&self, node: NodeId) -> Vec<NodeId> {
336 self.model.children(node)
337 }
338 fn name(&self, node: NodeId) -> Option<String> {
339 self.model.name(node)
340 }
341 fn parent(&self, node: NodeId) -> Option<NodeId> {
342 self.model.parent(node)
343 }
344 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
345 self.model.property(node, name)
346 }
347 fn default_value(&self, node: NodeId) -> Option<Value> {
348 self.model.default_value(node)
349 }
350 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
351 self.model.metadata(node, key)
352 }
353 fn resolve(&self, node: NodeId, property: &str, hint: Option<&str>) -> Option<NodeId> {
354 self.model.resolve(node, property, hint)
355 }
356 fn links(&self, node: NodeId) -> Vec<(String, NodeId)> {
357 self.model.links(node)
358 }
359 fn backlinks(&self, node: NodeId) -> Vec<(String, NodeId)> {
360 self.model.backlinks(node)
361 }
362}