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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// [[file:../database.note::*imports][imports:1]]
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate serde;
#[macro_use]
extern crate diesel_migrations;
#[macro_use]
extern crate derivative;

use std::sync::{Arc, Mutex, MutexGuard};

use diesel::prelude::*;
// imports:1 ends here

// [[file:../database.note::*mods][mods:1]]
mod checkpoint;
mod collection;
mod core;

pub(crate) mod schema;
// mods:1 ends here

// [[file:../database.note::f2af4002][f2af4002]]
use gosh_core::*;
use gut::prelude::*;

embed_migrations!();

#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct DbConnection {
    database_url: String,
    #[derivative(Debug = "ignore")]
    connection: Arc<Mutex<SqliteConnection>>,
}

impl DbConnection {
    /// Eastablish connection to database specified using env var
    /// `GOSH_DATABASE_URL`.
    pub fn establish() -> Result<DbConnection> {
        // read vars from .env file
        dotenvy::dotenv().ok();

        let database_url =
            std::env::var("GOSH_DATABASE_URL").with_context(|| format!("GOSH_DATABASE_URL var not set"))?;
        debug!("Database: {}", database_url);

        Self::connect(&database_url)
    }

    /// Connect to database specified using `database_url`.
    pub fn connect(database_url: &str) -> Result<DbConnection> {
        // diesel accept &str, not Path
        let conn = SqliteConnection::establish(database_url)?;

        // see: https://sqlite.org/faq.html#q19
        //
        // With synchronous OFF, SQLite continues without syncing as soon as
        // it has handed data off to the operating system. If the application
        // running SQLite crashes, the data will be safe, but the database might
        // become corrupted if the operating system crashes or the computer
        // loses power before that data has been written to the disk surface. On
        // the other hand, commits can be orders of magnitude faster with
        // synchronous OFF.
        conn.execute("PRAGMA synchronous = OFF")?;

        let conn = Arc::new(Mutex::new(conn));

        let db = DbConnection {
            database_url: database_url.into(),
            connection: conn.clone(),
        };

        db.migrate()?;

        Ok(db)
    }

    /// Show database url.
    pub fn database_url(&self) -> &str {
        &self.database_url
    }

    pub(crate) fn get(&self) -> MutexGuard<'_, SqliteConnection> {
        self.connection.lock().expect("cannot lock db connection!")
    }

    // for schema migrations, sql tables initialization
    fn migrate(&self) -> Result<()> {
        let conn = self.get();

        // This will run the necessary migrations.
        embedded_migrations::run(&*conn)?;

        Ok(())
    }
}
// f2af4002 ends here

// [[file:../database.note::*exports][exports:1]]
pub mod prelude {
    pub use crate::checkpoint::Checkpoint;
    pub use crate::collection::Collection;
}

pub use crate::checkpoint::CheckpointDb;
// exports:1 ends here