luna_orm/database/
sqlite.rs

1use crate::database::lib::Database;
2use crate::database::lib::DatabaseType;
3use crate::database::DB;
4use crate::{error::LunaOrmError, LunaOrmResult};
5
6use luna_orm_trait::LastRowId;
7use sqlx::any::AnyConnectOptions;
8use sqlx::any::AnyPoolOptions;
9use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqliteSynchronous};
10use sqlx::AnyPool;
11
12use std::fs;
13use std::path::Path;
14use std::str::FromStr;
15
16use crate::command_executor::CommandExecutor;
17use crate::sql_executor::SqlExecutor;
18use crate::sql_generator::DefaultSqlGenerator;
19use crate::sql_generator::SqlGenerator;
20use luna_orm_trait::Entity;
21use luna_orm_trait::SelectedEntity;
22use sqlx::any::AnyArguments;
23use sqlx::any::AnyRow;
24use tracing::debug;
25
26use path_absolutize::*;
27
28pub struct SqliteLocalConfig {
29    pub work_dir: String,
30    pub db_file: String,
31}
32
33impl SqliteLocalConfig {
34    pub fn new<S>(work_dir: S, db_file: S) -> Self
35    where
36        S: Into<String>,
37    {
38        Self {
39            work_dir: work_dir.into(),
40            db_file: db_file.into(),
41        }
42    }
43}
44
45#[derive(Debug, Clone)]
46pub struct SqliteDatabase {
47    database_type: DatabaseType,
48    pool: AnyPool,
49    sql_generator: DefaultSqlGenerator,
50}
51
52impl SqlExecutor for SqliteDatabase {
53    fn get_pool(&self) -> LunaOrmResult<&AnyPool> {
54        Ok(&self.pool)
55    }
56}
57
58impl CommandExecutor for SqliteDatabase {
59    type G = DefaultSqlGenerator;
60
61    fn get_generator(&self) -> &Self::G {
62        &self.sql_generator
63    }
64
65    // sqlx sqlite driver has bug #2099, it returns result before the actual commit on insert returning clause
66    // the work around is create a transaction
67    async fn create<'a>(&mut self, entity: &'a mut dyn Entity) -> LunaOrmResult<bool> {
68        debug!(target: "luna_orm2", command = "create",  entity = ?entity);
69        let sql = self.get_generator().get_create_sql(entity);
70        debug!(target: "luna_orm", command = "create", sql = sql);
71        let args = entity.any_arguments_of_insert();
72        if entity.get_auto_increment_field().is_some() {
73            let last_row_id: LastRowId = self.fetch_one(&sql, args).await?;
74            entity.set_auto_increment_field(Some(last_row_id.id));
75        } else {
76            self.execute(&sql, args).await?;
77        }
78        debug!(target: "luna_orm", command = "create", result = ?entity);
79
80        // the work around
81        let trx = self.pool.begin().await?;
82        // query the record
83        trx.commit().await?;
84        return Ok(true);
85    }
86}
87
88impl Database for SqliteDatabase {
89    fn get_type(&self) -> &DatabaseType {
90        &self.database_type
91    }
92}
93
94impl From<SqliteDatabase> for DB<SqliteDatabase> {
95    fn from(value: SqliteDatabase) -> Self {
96        Self(value)
97    }
98}
99
100impl SqliteDatabase {
101    pub async fn init_local_sqlite(workspace_dir: &str, db_file: &str) -> LunaOrmResult<AnyPool> {
102        let workspace = Path::new(workspace_dir);
103        let workspace_absolute = workspace
104            .absolutize()
105            .map_err(|_e| LunaOrmError::DatabaseInitFail("workdir absolute fail".to_string()))?;
106
107        fs::create_dir_all(&workspace_absolute)
108            .map_err(|_e| LunaOrmError::DatabaseInitFail("create dir fail".to_string()))?;
109        let db_file_path = workspace_absolute.join(db_file);
110        {
111            let options = SqliteConnectOptions::new()
112                .filename(db_file_path.clone())
113                .synchronous(SqliteSynchronous::Full)
114                .journal_mode(SqliteJournalMode::Wal)
115                .create_if_missing(true);
116            let _ = SqlitePool::connect_with(options).await.map_err(|_e| {
117                LunaOrmError::DatabaseInitFail("create is missing fail".to_string())
118            })?;
119        }
120
121        sqlx::any::install_default_drivers();
122        let url = format!("sqlite:{}", db_file_path.to_str().unwrap());
123        let any_options = AnyConnectOptions::from_str(&url).unwrap();
124        let any_pool = AnyPool::connect_with(any_options)
125            .await
126            .map_err(|_e| LunaOrmError::DatabaseInitFail("init pool fail".to_string()))?;
127        return Ok(any_pool);
128    }
129
130    pub async fn build(config: SqliteLocalConfig) -> LunaOrmResult<Self> {
131        let pool = SqliteDatabase::init_local_sqlite(&config.work_dir, &config.db_file).await?;
132        let generator = DefaultSqlGenerator::new();
133        let database = SqliteDatabase {
134            database_type: DatabaseType::SqliteLocal,
135            pool,
136            sql_generator: generator,
137        };
138        return Ok(database);
139    }
140
141    /*
142    pub async fn from_sqlite_pool(pool: SqlitePool) -> Self {
143        let generator = DefaultSqlGenerator::new();
144
145        Self {
146            database_type: DatabaseType::SqliteLocal,
147            pool:
148            sql_generator: generator,
149        }
150    }
151    */
152}