1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//! # SQLite conversions and tooling

use std::fmt::Write;

use rusqlite::params_from_iter;
pub use rusqlite::{Connection, Error, Result};

#[cfg(feature = "sqlite-vtab")]
mod vtab;
#[cfg(feature = "sqlite-vtab")]
pub use vtab::load_module;

use super::mem::Database;

/// Try to export a database to a SQL connection
///
/// This function does the following:
///
/// 1. `BEGIN`s a transaction
/// 2. For every table:
///   a. Run `CREATE TABLE IF NOT EXISTS`
///   b. Prepares an `INSERT` statement
///   c. Runs the insert with data from every row
/// 3. `COMMIT`s the transaction
pub fn try_export_db(conn: &mut Connection, db: Database) -> rusqlite::Result<()> {
    conn.execute("BEGIN", rusqlite::params![])?;

    let tables = db.tables().unwrap();
    for table in tables.iter() {
        let table = table.unwrap();
        let mut create_query = format!("CREATE TABLE IF NOT EXISTS \"{}\"\n(\n", table.name());
        let mut insert_query = format!("INSERT INTO \"{}\" (", table.name());
        let mut first = true;
        for col in table.column_iter() {
            if first {
                first = false;
            } else {
                writeln!(create_query, ",").unwrap();
                write!(insert_query, ", ").unwrap();
            }
            let typ = col.value_type().to_sqlite_type();
            write!(create_query, "    [{}] {}", col.name(), typ).unwrap();
            write!(insert_query, "[{}]", col.name()).unwrap();
        }
        create_query.push_str(");");
        insert_query.push_str(") VALUES (?1");
        for i in 2..=table.column_count() {
            write!(insert_query, ", ?{}", i).unwrap();
        }
        insert_query.push_str(");");
        conn.execute(&create_query, rusqlite::params![])?;

        let mut stmt = conn.prepare(&insert_query)?;
        for row in table.row_iter() {
            stmt.execute(params_from_iter(row.field_iter()))?;
        }
    }

    conn.execute("COMMIT", rusqlite::params![])?;
    Ok(())
}