assembly_data/fdb/
sqlite.rs

1//! # SQLite conversions and tooling
2
3use std::fmt::Write;
4
5use rusqlite::{types::ToSqlOutput, ToSql};
6pub use rusqlite::{Connection, Error, Result};
7
8use super::{
9    common::ValueType,
10    mem::{Database, Field},
11};
12
13impl<'a> ToSql for Field<'a> {
14    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
15        use rusqlite::types::Value;
16        let r = match *self {
17            Field::Nothing => Value::Null,
18            Field::Integer(i) => Value::Integer(i.into()),
19            Field::Float(f) => Value::Real(f.into()),
20            Field::Text(s) => Value::Text(s.decode().into_owned()),
21            Field::Boolean(b) => Value::Integer(if b { 1 } else { 0 }),
22            Field::BigInt(i) => Value::Integer(i),
23            Field::VarChar(b) => Value::Text(b.decode().into_owned()),
24        };
25        Ok(ToSqlOutput::Owned(r))
26    }
27}
28
29/// Try to export a database to a SQL connection
30///
31/// This function does the following:
32///
33/// 1. `BEGIN`s a transaction
34/// 2. For every table:
35///   a. Run `CREATE TABLE IF NOT EXISTS`
36///   b. Prepares an `INSERT` statement
37///   c. Runs the insert with data from every row
38/// 3. `COMMIT`s the transaction
39pub fn try_export_db(conn: &mut Connection, db: Database) -> rusqlite::Result<()> {
40    conn.execute("BEGIN", rusqlite::params![])?;
41
42    let tables = db.tables().unwrap();
43    for table in tables.iter() {
44        let table = table.unwrap();
45        let mut create_query = format!("CREATE TABLE IF NOT EXISTS \"{}\"\n(\n", table.name());
46        let mut insert_query = format!("INSERT INTO \"{}\" (", table.name());
47        let mut first = true;
48        for col in table.column_iter() {
49            if first {
50                first = false;
51            } else {
52                writeln!(create_query, ",").unwrap();
53                write!(insert_query, ", ").unwrap();
54            }
55            let typ = match col.value_type() {
56                ValueType::Nothing => "NULL",
57                ValueType::Integer => "INTEGER",
58                ValueType::Float => "REAL",
59                ValueType::Text => "TEXT",
60                ValueType::Boolean => "INTEGER",
61                ValueType::BigInt => "INTEGER",
62                ValueType::VarChar => "BLOB",
63            };
64            write!(create_query, "    [{}] {}", col.name(), typ).unwrap();
65            write!(insert_query, "[{}]", col.name()).unwrap();
66        }
67        create_query.push_str(");");
68        insert_query.push_str(") VALUES (?1");
69        for i in 2..=table.column_count() {
70            write!(insert_query, ", ?{}", i).unwrap();
71        }
72        insert_query.push_str(");");
73        conn.execute(&create_query, rusqlite::params![])?;
74
75        let mut stmt = conn.prepare(&insert_query)?;
76        for row in table.row_iter() {
77            stmt.execute(row)?;
78        }
79    }
80
81    conn.execute("COMMIT", rusqlite::params![])?;
82    Ok(())
83}