• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 //! This module provides interfaces for database management.
17 //! Databases are isolated based on users and protected by locks.
18 
19 use core::ffi::c_void;
20 use std::{ffi::CStr, fs, ptr::null_mut, sync::Mutex};
21 
22 use asset_constants::OwnerType;
23 use asset_definition::{log_throw_error, ErrCode, Extension, Result};
24 use asset_log::loge;
25 
26 use crate::{
27     statement::Statement,
28     table::Table,
29     types::{column, sqlite_err_handle, DbMap, QueryOptions, COLUMN_INFO, SQLITE_OK, TABLE_NAME},
30 };
31 
32 extern "C" {
SqliteOpen(file_name: *const u8, pp_db: *mut *mut c_void) -> i3233     fn SqliteOpen(file_name: *const u8, pp_db: *mut *mut c_void) -> i32;
SqliteCloseV2(db: *mut c_void) -> i3234     fn SqliteCloseV2(db: *mut c_void) -> i32;
SqliteExec(db: *mut c_void, sql: *const u8, msg: *mut *mut u8) -> i3235     fn SqliteExec(db: *mut c_void, sql: *const u8, msg: *mut *mut u8) -> i32;
SqliteFree(data: *mut c_void)36     fn SqliteFree(data: *mut c_void);
SqliteErrMsg(db: *mut c_void) -> *const u837     fn SqliteErrMsg(db: *mut c_void) -> *const u8;
38 }
39 
40 /// each user have a Database file
41 pub(crate) struct UserDbLock {
42     pub(crate) user_id: i32,
43     pub(crate) mtx: Mutex<i32>,
44 }
45 
46 static USER_DB_LOCK_LIST: Mutex<Vec<&'static UserDbLock>> = Mutex::new(Vec::new());
47 
48 /// If the user exists, the reference to the lock is returned.
49 /// Otherwise, a new lock is created and its reference is returned.
get_file_lock_by_user_id(user_id: i32) -> &'static UserDbLock50 fn get_file_lock_by_user_id(user_id: i32) -> &'static UserDbLock {
51     let mut list = USER_DB_LOCK_LIST.lock().unwrap();
52     for f in list.iter() {
53         if f.user_id == user_id {
54             return f;
55         }
56     }
57     let nf = Box::new(UserDbLock { user_id, mtx: Mutex::new(user_id) });
58     // SAFETY: We just push item into USER_DB_LOCK_LIST, never remove item or modify item,
59     // so return a reference of leak item is safe.
60     let nf: &'static UserDbLock = Box::leak(nf);
61     list.push(nf);
62     list[list.len() - 1]
63 }
64 
65 /// Struct used to store database files and connection information.
66 #[repr(C)]
67 pub struct Database {
68     pub(crate) path: String,
69     pub(crate) backup_path: String,
70     pub(crate) handle: usize, // Pointer to the database connection.
71     pub(crate) db_lock: &'static UserDbLock,
72 }
73 
74 /// Callback for database upgrade.
75 pub type UpgradeDbCallback = fn(db: &Database, old_ver: u32, new_ver: u32) -> Result<()>;
76 
77 #[cfg(not(test))]
78 const ROOT_PATH: &str = "/data/service/el1/public/asset_service";
79 #[cfg(test)]
80 const ROOT_PATH: &str = "/data/asset_test";
81 
82 #[inline(always)]
fmt_db_path(user_id: i32) -> String83 fn fmt_db_path(user_id: i32) -> String {
84     format!("{}/{}/asset.db", ROOT_PATH, user_id)
85 }
86 
87 #[inline(always)]
fmt_backup_path(path: &str) -> String88 fn fmt_backup_path(path: &str) -> String {
89     let mut bp = path.to_string();
90     bp.push_str(".backup");
91     bp
92 }
93 
94 impl Database {
95     /// Create a database.
build(user_id: i32) -> Result<Database>96     pub fn build(user_id: i32) -> Result<Database> {
97         let path = fmt_db_path(user_id);
98         let backup_path = fmt_backup_path(path.as_str());
99         let lock = get_file_lock_by_user_id(user_id);
100         let mut db = Database { path, backup_path, handle: 0, db_lock: lock };
101         let _lock = db.db_lock.mtx.lock().unwrap();
102         db.open_and_recovery()?;
103         db.execute_and_backup(true, |e: &Table| e.create(COLUMN_INFO))?;
104         Ok(db)
105     }
106 
107     // Open database connection.
open(&mut self) -> Result<()>108     pub(crate) fn open(&mut self) -> Result<()> {
109         let mut path_c = self.path.clone();
110         path_c.push('\0');
111 
112         let ret = unsafe { SqliteOpen(path_c.as_ptr(), &mut self.handle as *mut usize as _) };
113         if ret == SQLITE_OK {
114             Ok(())
115         } else {
116             self.close();
117             log_throw_error!(sqlite_err_handle(ret), "[FATAL][DB]Open database failed, err={}", ret)
118         }
119     }
120 
121     /// Open the database connection and recovery the database if the connection fails.
open_and_recovery(&mut self) -> Result<()>122     fn open_and_recovery(&mut self) -> Result<()> {
123         let result = self.open();
124         #[cfg(test)]
125         let result = match result {
126             Err(ret) if ret.code == ErrCode::DataCorrupted => self.recovery(),
127             ret => ret,
128         };
129         result
130     }
131 
132     /// Close database connection.
close(&mut self)133     pub(crate) fn close(&mut self) {
134         if self.handle != 0 {
135             unsafe { SqliteCloseV2(self.handle as _) };
136             self.handle = 0;
137         }
138     }
139 
140     // Recovery the corrupt database and reopen it.
141     #[cfg(test)]
recovery(&mut self) -> Result<()>142     pub(crate) fn recovery(&mut self) -> Result<()> {
143         loge!("[WARNING]Database is corrupt, start to recovery, path={}", self.path);
144         self.close();
145         if let Err(e) = fs::copy(&self.backup_path, &self.path) {
146             return log_throw_error!(ErrCode::FileOperationError, "[FATAL][DB]Recovery database failed, err={}", e);
147         }
148         self.open()
149     }
150 
151     /// Get database version, default is 0.
get_version(&self) -> Result<u32>152     pub(crate) fn get_version(&self) -> Result<u32> {
153         let _lock = self.db_lock.mtx.lock().unwrap();
154         let stmt = Statement::prepare("pragma user_version", self)?;
155         stmt.step()?;
156         let version = stmt.query_column_int(0);
157         Ok(version)
158     }
159 
160     /// Update the database version for database upgrade.
161     #[allow(dead_code)]
set_version(&self, ver: u32) -> Result<()>162     pub(crate) fn set_version(&self, ver: u32) -> Result<()> {
163         let sql = format!("pragma user_version = {}", ver);
164         self.exec(sql.as_str())
165     }
166 
167     /// Upgrade database to new version.
168     #[allow(dead_code)]
upgrade(&self, ver: u32, callback: UpgradeDbCallback) -> Result<()>169     pub(crate) fn upgrade(&self, ver: u32, callback: UpgradeDbCallback) -> Result<()> {
170         let version_old = self.get_version()?;
171         callback(self, version_old, ver)
172     }
173 
174     /// Delete database file.
175     #[allow(dead_code)]
delete(user_id: i32) -> Result<()>176     pub(crate) fn delete(user_id: i32) -> Result<()> {
177         let path = fmt_db_path(user_id);
178         let _backup_path = fmt_backup_path(&path);
179         if let Err(e) = fs::remove_file(path) {
180             return log_throw_error!(ErrCode::FileOperationError, "[FATAL][DB]Delete database failed, err={}", e);
181         }
182 
183         #[cfg(test)]
184         if let Err(e) = fs::remove_file(_backup_path) {
185             return log_throw_error!(
186                 ErrCode::FileOperationError,
187                 "[FATAL][DB]Delete backup database failed, err={}",
188                 e
189             );
190         }
191         Ok(())
192     }
193 
194     /// Print the error message of database.
print_db_msg(&self)195     pub(crate) fn print_db_msg(&self) {
196         let msg = unsafe { SqliteErrMsg(self.handle as _) };
197         if !msg.is_null() {
198             let s = unsafe { CStr::from_ptr(msg as _) };
199             if let Ok(rs) = s.to_str() {
200                 loge!("[FATAL][DB]Database error message: {}", rs);
201             }
202         }
203     }
204 
205     /// execute sql without prepare
exec(&self, sql: &str) -> Result<()>206     pub(crate) fn exec(&self, sql: &str) -> Result<()> {
207         let mut sql_s = sql.to_string();
208         sql_s.push('\0');
209         let mut msg: *mut u8 = null_mut();
210         let ret = unsafe { SqliteExec(self.handle as _, sql_s.as_ptr(), &mut msg as _) };
211         if !msg.is_null() {
212             let s = unsafe { CStr::from_ptr(msg as _) };
213             if let Ok(rs) = s.to_str() {
214                 return log_throw_error!(
215                     sqlite_err_handle(ret),
216                     "[FATAL]Database execute sql failed. error code={}, error msg={}",
217                     ret,
218                     rs
219                 );
220             }
221             unsafe { SqliteFree(msg as _) };
222         }
223         if ret == SQLITE_OK {
224             Ok(())
225         } else {
226             log_throw_error!(sqlite_err_handle(ret), "[FATAL]Database execute sql failed. error code={}", ret)
227         }
228     }
229 
230     /// do same operation in backup database when do something in main db
231     /// backup every success operation, recovery every fail operation
execute_and_backup<T, F: Fn(&Table) -> Result<T>>(&mut self, _modified: bool, func: F) -> Result<T>232     pub(crate) fn execute_and_backup<T, F: Fn(&Table) -> Result<T>>(&mut self, _modified: bool, func: F) -> Result<T> {
233         let table = Table::new(TABLE_NAME, self);
234         let result = func(&table);
235         #[cfg(test)]
236         let result = match result {
237             Err(ret) if ret.code == ErrCode::DataCorrupted => {
238                 self.recovery()?;
239                 let table = Table::new(TABLE_NAME, self); // Database handle will be changed.
240                 func(&table)
241             },
242             ret => ret,
243         };
244 
245         #[cfg(test)]
246         if result.is_ok() && _modified && fs::copy(&self.path, &self.backup_path).is_err() {
247             loge!("[WARNING]Backup database {} failed", self.backup_path);
248         }
249         result
250     }
251 
252     /// Insert datas into database.
253     /// The datas is a map of column-data pair.
254     /// If the operation is successful, the number of inserted data is returned.
255     ///
256     /// # Examples
257     ///
258     /// ```
259     /// use asset_definition::Value;
260     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
261     ///
262     /// // SQL: insert into table_name(Owner,OwnerType,Alias,value) values('owner',1,'alias','insert_value')
263     /// let datas = DbMap::new();
264     /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
265     /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
266     /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
267     /// datas.insert("value", Value::Bytes(b"insert_value".to_vec()));
268     /// let user_id = 100;
269     /// let ret = Database::build(user_id)?.insert_datas(&datas);
270     /// ```
271     ///
272     #[inline(always)]
insert_datas(&mut self, datas: &DbMap) -> Result<i32>273     pub fn insert_datas(&mut self, datas: &DbMap) -> Result<i32> {
274         let _lock: std::sync::MutexGuard<'_, i32> = self.db_lock.mtx.lock().unwrap();
275         let closure = |e: &Table| {
276             let mut query = DbMap::new();
277             query.insert_attr(column::ALIAS, datas.get_bytes_attr(&column::ALIAS)?.clone());
278             query.insert_attr(column::OWNER, datas.get_bytes_attr(&column::OWNER)?.clone());
279             query.insert_attr(column::OWNER_TYPE, datas.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?);
280             if e.is_data_exists(&query)? {
281                 log_throw_error!(ErrCode::Duplicated, "[FATAL]The data with the specified alias already exists.")
282             } else {
283                 e.insert_row(datas)
284             }
285         };
286         self.execute_and_backup(true, closure)
287     }
288 
289     /// Delete datas from database.
290     /// The condition is a map of column-data pair.
291     /// If the operation is successful, the number of deleted data is returned.
292     ///
293     /// # Examples
294     ///
295     /// ```
296     /// use asset_definition::Value;
297     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
298     ///
299     /// // SQL: delete from table_name where Owner='owner' and OwnerType=1 and Alias='alias' and value='delete_value'
300     /// let datas = DbMap::new();
301     /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
302     /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
303     /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
304     /// datas.insert("value", Value::Bytes(b"delete_value".to_vec()));
305     /// let user_id = 100;
306     /// let ret = Database::build(user_id)?.delete_datas(&cond);
307     /// ```
308     ///
309     ///
310     #[inline(always)]
delete_datas(&mut self, condition: &DbMap) -> Result<i32>311     pub fn delete_datas(&mut self, condition: &DbMap) -> Result<i32> {
312         let _lock = self.db_lock.mtx.lock().unwrap();
313         let closure = |e: &Table| e.delete_row(condition);
314         self.execute_and_backup(true, closure)
315     }
316 
317     /// Update datas in database.
318     /// The datas is a map of column-data pair.
319     /// If the operation is successful, the number of updated data is returned.
320     ///
321     /// # Examples
322     ///
323     /// ```
324     /// use asset_definition::Value;
325     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
326     ///
327     /// // SQL: update table_name set alias='update_value' where Owner='owner' and OwnerType=1 and Alias='alias'
328     /// let cond = DbMap.new();
329     /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
330     /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
331     /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
332     /// let datas = DbMap::from([("alias", Value::Bytes(b"update_value".to_vec()))]);
333     /// let user_id = 100;
334     /// let ret = Database::build(user_id)?.update_datas(&condition, &datas);
335     /// ```
336     #[inline(always)]
update_datas(&mut self, condition: &DbMap, datas: &DbMap) -> Result<i32>337     pub fn update_datas(&mut self, condition: &DbMap, datas: &DbMap) -> Result<i32> {
338         let _lock = self.db_lock.mtx.lock().unwrap();
339         let closure = |e: &Table| e.update_row(condition, datas);
340         self.execute_and_backup(true, closure)
341     }
342 
343     /// Check whether data exists in the database.
344     ///
345     /// # Examples
346     ///
347     /// ```
348     /// use asset_definition::Value;
349     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
350     ///
351     /// // SQL: select count(*) as count from table_name where Owner='owner' and OwnerType=1 and Alias='alias'
352     /// let datas = DbMap::new();
353     /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
354     /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
355     /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
356     /// let user_id = 100;
357     /// let exist = Database::build(user_id)?.is_data_exists(&datas);
358     /// ```
359     #[inline(always)]
is_data_exists(&mut self, condition: &DbMap) -> Result<bool>360     pub fn is_data_exists(&mut self, condition: &DbMap) -> Result<bool> {
361         let _lock = self.db_lock.mtx.lock().unwrap();
362         let closure = |e: &Table| e.is_data_exists(condition);
363         self.execute_and_backup(false, closure)
364     }
365 
366     /// Query data that meets specified conditions(can be empty) from the database.
367     /// If the operation is successful, the resultSet is returned.
368     ///
369     /// # Examples
370     ///
371     /// ```
372     /// use asset_definition::Value;
373     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
374     ///
375     /// // SQL: select * from table_name where Owner='owner' and OwnerType=1 and Alias='alias'
376     /// let cond = DbMap::new();
377     /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
378     /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
379     /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
380     /// let user_id = 100;
381     /// let ret = Database::build(user_id)?.query_datas(&vec![], &cond, None);
382     /// ```
383     #[inline(always)]
query_datas( &mut self, columns: &Vec<&'static str>, condition: &DbMap, query_options: Option<&QueryOptions>, ) -> Result<Vec<DbMap>>384     pub fn query_datas(
385         &mut self,
386         columns: &Vec<&'static str>,
387         condition: &DbMap,
388         query_options: Option<&QueryOptions>,
389     ) -> Result<Vec<DbMap>> {
390         let _lock = self.db_lock.mtx.lock().unwrap();
391         let closure = |e: &Table| e.query_row(columns, condition, query_options, COLUMN_INFO);
392         self.execute_and_backup(false, closure)
393     }
394 
395     /// Delete old data and insert new data.
replace_datas(&mut self, condition: &DbMap, datas: &DbMap) -> Result<()>396     pub fn replace_datas(&mut self, condition: &DbMap, datas: &DbMap) -> Result<()> {
397         let _lock = self.db_lock.mtx.lock().unwrap();
398         let closure = |e: &Table| e.replace_row(condition, datas);
399         self.execute_and_backup(true, closure)
400     }
401 }
402 
403 impl Drop for Database {
drop(&mut self)404     fn drop(&mut self) {
405         self.close()
406     }
407 }
408