• 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::{collections::HashMap, ffi::CStr, fs, ptr::null_mut, sync::{Arc, Mutex}};
21 
22 use asset_common::{CallingInfo, OwnerType};
23 use asset_crypto_manager::secret_key::rename_key_alias;
24 use asset_db_key_operator::DbKey;
25 use asset_definition::{log_throw_error, AssetMap, ErrCode, Extension, Result, Tag, Value};
26 use asset_file_operator::{ce_operator::remove_ce_files, common::is_file_exist};
27 use asset_log::{loge, logi};
28 use lazy_static::lazy_static;
29 
30 use crate::{
31     database_file_upgrade::{check_and_split_db, construct_splited_db_name},
32     statement::Statement,
33     table::Table,
34     types::{
35         column, sqlite_err_handle, DbMap, QueryOptions, ADAPT_CLOUD_COLUMN_INFO, ADAPT_CLOUD_TABLE, COLUMN_INFO, COMBINE_COLUMN_INFO, DB_UPGRADE_VERSION, DB_UPGRADE_VERSION_V1, DB_UPGRADE_VERSION_V2, DB_UPGRADE_VERSION_V3, DB_UPGRADE_VERSION_V4, SQLITE_OK, TABLE_NAME, UPGRADE_COLUMN_INFO, UPGRADE_COLUMN_INFO_V2, UPGRADE_COLUMN_INFO_V3, UPGRADE_COLUMN_INFO_V4
36     },
37 };
38 
39 extern "C" {
SqliteOpen(file_name: *const u8, pp_db: *mut *mut c_void) -> i3240     fn SqliteOpen(file_name: *const u8, pp_db: *mut *mut c_void) -> i32;
SqliteCloseV2(db: *mut c_void) -> i3241     fn SqliteCloseV2(db: *mut c_void) -> i32;
SqliteExec(db: *mut c_void, sql: *const u8, msg: *mut *mut u8) -> i3242     fn SqliteExec(db: *mut c_void, sql: *const u8, msg: *mut *mut u8) -> i32;
SqliteFree(data: *mut c_void)43     fn SqliteFree(data: *mut c_void);
SqliteErrMsg(db: *mut c_void) -> *const u844     fn SqliteErrMsg(db: *mut c_void) -> *const u8;
SqliteKey(db: *mut c_void, pKey: *const c_void, nKey: i32) -> i3245     fn SqliteKey(db: *mut c_void, pKey: *const c_void, nKey: i32) -> i32;
46 }
47 
48 /// each user have a Database file
49 pub(crate) struct UserDbLock {
50     pub(crate) mtx: Arc<Mutex<i32>>,
51 }
52 
53 pub(crate) static OLD_DB_NAME: &str = "asset";
54 
55 lazy_static! {
56     static ref SPLIT_DB_LOCK_MAP: Mutex<HashMap<i32, &'static UserDbLock>> = Mutex::new(HashMap::new());
57     static ref USER_DB_LOCK_MAP: Mutex<HashMap<(i32, String), &'static UserDbLock>> = Mutex::new(HashMap::new());
58 }
59 
get_split_db_lock_by_user_id(user_id: i32) -> &'static UserDbLock60 pub(crate) fn get_split_db_lock_by_user_id(user_id: i32) -> &'static UserDbLock {
61     let mut map = SPLIT_DB_LOCK_MAP.lock().unwrap();
62     if let Some(&lock) = map.get(&user_id) {
63         return lock;
64     }
65 
66     let nf = Box::new(UserDbLock { mtx: Arc::new(Mutex::new(user_id)) });
67     // SAFETY: We just push item into SPLIT_DB_LOCK_MAP, never remove item or modify item,
68     // so return a reference of leak item is safe.
69     let nf: &'static UserDbLock = Box::leak(nf);
70     map.insert(user_id, nf);
71     nf
72 }
73 
74 /// If the user exists, the reference to the lock is returned.
75 /// Otherwise, a new lock is created and its reference is returned.
get_file_lock_by_user_id_db_file_name(user_id: i32, db_file_name: String) -> &'static UserDbLock76 pub(crate) fn get_file_lock_by_user_id_db_file_name(user_id: i32, db_file_name: String) -> &'static UserDbLock {
77     let mut map = USER_DB_LOCK_MAP.lock().unwrap();
78 
79     if let Some(&lock) = map.get(&(user_id, db_file_name.clone())) {
80         return lock;
81     }
82 
83     let nf = Box::new(UserDbLock { mtx: Arc::new(Mutex::new(user_id)) });
84     // SAFETY: We just push item into USER_DB_LOCK_MAP, never remove item or modify item,
85     // so return a reference of leak item is safe.
86     let nf: &'static UserDbLock = Box::leak(nf);
87     map.insert((user_id, db_file_name), nf);
88     nf
89 }
90 
91 /// Struct used to store database files and connection information.
92 #[repr(C)]
93 pub struct Database {
94     pub(crate) path: String,
95     pub(crate) backup_path: String,
96     pub(crate) handle: usize, // Pointer to the database connection.
97     pub(crate) db_lock: &'static UserDbLock,
98     pub(crate) db_name: String,
99     pub(crate) use_lock: bool,
100 }
101 
102 /// Callback for database upgrade.
103 pub type UpgradeDbCallback = fn(db: &Database, old_ver: u32, new_ver: u32) -> Result<()>;
104 
105 #[cfg(not(test))]
106 pub(crate) const DE_ROOT_PATH: &str = "/data/service/el1/public/asset_service";
107 #[cfg(test)]
108 pub(crate) const DE_ROOT_PATH: &str = "/data/asset_test";
109 
110 pub(crate) const CE_ROOT_PATH: &str = "/data/service/el2";
111 
112 #[inline(always)]
fmt_backup_path(path: &str) -> String113 pub(crate) fn fmt_backup_path(path: &str) -> String {
114     let mut bp = path.to_string();
115     bp.push_str(".backup");
116     bp
117 }
118 
119 /// Get asset storage path.
get_path() -> String120 pub fn get_path() -> String {
121     DE_ROOT_PATH.to_string()
122 }
123 
124 #[inline(always)]
fmt_ce_db_path_with_name(user_id: i32, db_name: &str) -> String125 pub(crate) fn fmt_ce_db_path_with_name(user_id: i32, db_name: &str) -> String {
126     format!("data/service/el2/{}/asset_service/{}.db", user_id, db_name)
127 }
128 
129 #[inline(always)]
fmt_de_db_path_with_name(user_id: i32, db_name: &str) -> String130 pub(crate) fn fmt_de_db_path_with_name(user_id: i32, db_name: &str) -> String {
131     format!("{}/{}/{}.db", DE_ROOT_PATH, user_id, db_name)
132 }
133 
check_validity_of_db_key(path: &str, user_id: i32) -> Result<()>134 fn check_validity_of_db_key(path: &str, user_id: i32) -> Result<()> {
135     if is_file_exist(path)? && !DbKey::check_existance(user_id)? {
136         loge!("[FATAL]There is database bot no database key. Now all data should be cleared and restart over.");
137         remove_ce_files(user_id)?;
138         return log_throw_error!(ErrCode::DataCorrupted, "[FATAL]All data is cleared in {}.", user_id);
139     }
140     Ok(())
141 }
142 
get_db_by_type_without_lock( user_id: i32, db_name: &str, db_path: String, db_key: Option<&DbKey>, ) -> Result<Database>143 pub(crate) fn get_db_by_type_without_lock(
144     user_id: i32,
145     db_name: &str,
146     db_path: String,
147     db_key: Option<&DbKey>,
148 ) -> Result<Database> {
149     let backup_path = fmt_backup_path(&db_path);
150     let lock = get_file_lock_by_user_id_db_file_name(user_id, db_name.to_string().clone());
151     let mut db = Database { path: db_path, backup_path, handle: 0, db_lock: lock, db_name: db_name.to_string(), use_lock: false };
152     db.open_and_restore(db_key)?;
153     // when create db table always use newest version.
154     db.restore_if_exec_fail(|e: &Table| e.create_with_version(COLUMN_INFO, DB_UPGRADE_VERSION))?;
155     db.upgrade(user_id, DB_UPGRADE_VERSION, |_, _, _| Ok(()))?;
156     Ok(db)
157 }
158 
get_db_by_type( user_id: i32, db_name: &str, db_path: String, db_key: Option<&DbKey>, ) -> Result<Database>159 pub(crate) fn get_db_by_type(
160     user_id: i32,
161     db_name: &str,
162     db_path: String,
163     db_key: Option<&DbKey>,
164 ) -> Result<Database> {
165     let backup_path = fmt_backup_path(&db_path);
166     let lock = get_file_lock_by_user_id_db_file_name(user_id, db_name.to_string().clone());
167     let mut db = Database { path: db_path, backup_path, handle: 0, db_lock: lock, db_name: db_name.to_string(), use_lock: true };
168     let _lock = db.db_lock.mtx.lock().unwrap();
169     db.open_and_restore(db_key)?;
170     // when create db table always use newest version.
171     db.restore_if_exec_fail(|e: &Table| e.create_with_version(COLUMN_INFO, DB_UPGRADE_VERSION))?;
172     db.upgrade(user_id, DB_UPGRADE_VERSION, |_, _, _| Ok(()))?;
173     Ok(db)
174 }
175 
get_specific_db_version(user_id: i32, db_name: &str, db_path: String) -> Result<u32>176 pub(crate) fn get_specific_db_version(user_id: i32, db_name: &str, db_path: String) -> Result<u32> {
177     let backup_path = fmt_backup_path(&db_path);
178     let lock = get_file_lock_by_user_id_db_file_name(user_id, db_name.to_string().clone());
179     let mut db = Database { path: db_path, backup_path, handle: 0, db_lock: lock, db_name: db_name.to_string(), use_lock: true };
180     let _lock = db.db_lock.mtx.lock().unwrap();
181     db.open()?;
182     db.get_db_version()
183 }
184 
get_db(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database>185 pub(crate) fn get_db(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database> {
186     let (db_path, db_key) = if is_ce {
187         let db_path = fmt_ce_db_path_with_name(user_id, db_name);
188         check_validity_of_db_key(&db_path, user_id)?;
189         let db_key = match DbKey::get_db_key(user_id) {
190             Ok(db_key) => Some(db_key),
191             Err(e) if e.code == ErrCode::NotFound || e.code == ErrCode::DataCorrupted => {
192                 loge!(
193                     "[FATAL]The key is corrupted. Now all data should be cleared and restart over, err is {}.",
194                     e.code
195                 );
196                 remove_ce_files(user_id)?;
197                 return log_throw_error!(ErrCode::DataCorrupted, "[FATAL]All data is cleared in {}.", user_id);
198             },
199             Err(e) => return Err(e),
200         };
201         (db_path, db_key)
202     } else {
203         let db_path = fmt_de_db_path_with_name(user_id, db_name);
204         (db_path, None)
205     };
206 
207     get_db_by_type(user_id, db_name, db_path, db_key.as_ref())
208 }
209 
get_db_without_lock(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database>210 pub(crate) fn get_db_without_lock(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database> {
211     let (db_path, db_key) = if is_ce {
212         let db_path = fmt_ce_db_path_with_name(user_id, db_name);
213         check_validity_of_db_key(&db_path, user_id)?;
214         let db_key = match DbKey::get_db_key(user_id) {
215             Ok(db_key) => Some(db_key),
216             Err(e) if e.code == ErrCode::NotFound || e.code == ErrCode::DataCorrupted => {
217                 loge!(
218                     "[FATAL]The key is corrupted. Now all data should be cleared and restart over, err is {}.",
219                     e.code
220                 );
221                 remove_ce_files(user_id)?;
222                 return log_throw_error!(ErrCode::DataCorrupted, "[FATAL]All data is cleared in {}.", user_id);
223             },
224             Err(e) => return Err(e),
225         };
226         (db_path, db_key)
227     } else {
228         let db_path = fmt_de_db_path_with_name(user_id, db_name);
229         (db_path, None)
230     };
231 
232     get_db_by_type_without_lock(user_id, db_name, db_path, db_key.as_ref())
233 }
234 
235 /// Create ce db instance if the value of tag "RequireAttrEncrypted" is set to true.
236 /// Create de db instance if the value of tag "RequireAttrEncrypted" is not specified or set to false.
build_db(attributes: &AssetMap, calling_info: &CallingInfo) -> Result<Database>237 pub fn build_db(attributes: &AssetMap, calling_info: &CallingInfo) -> Result<Database> {
238     match attributes.get(&Tag::RequireAttrEncrypted) {
239         Some(Value::Bool(true)) => Ok(Database::build(calling_info, true)?),
240         _ => Ok(Database::build(calling_info, false)?),
241     }
242 }
243 
244 impl Database {
245     /// Create a database without a given file name.
build(calling_info: &CallingInfo, is_ce: bool) -> Result<Database>246     pub fn build(calling_info: &CallingInfo, is_ce: bool) -> Result<Database> {
247         if !is_ce {
248             // DE database needs trigger the upgrade action.
249             check_and_split_db(calling_info.user_id())?;
250         }
251         get_db(calling_info.user_id(), &construct_splited_db_name(calling_info, is_ce)?, is_ce)
252     }
253 
254     /// Create a database from a file name.
build_with_file_name(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database>255     pub fn build_with_file_name(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database> {
256         check_and_split_db(user_id)?;
257         get_db(user_id, db_name, is_ce)
258     }
259 
260     /// Create a database from a file name without lock in full process.
build_with_file_name_without_lock(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database>261     pub fn build_with_file_name_without_lock(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database> {
262         // run here db must has been splited.
263         get_db_without_lock(user_id, db_name, is_ce)
264     }
265 
266     /// Check whether db is ok
check_db_accessible(path: String, user_id: i32, db_name: String, db_key: Option<&DbKey>) -> Result<()>267     pub fn check_db_accessible(path: String, user_id: i32, db_name: String, db_key: Option<&DbKey>) -> Result<()> {
268         let lock = get_file_lock_by_user_id_db_file_name(user_id, db_name.clone());
269         let mut db = Database { path: path.clone(), backup_path: path, handle: 0, db_lock: lock, db_name, use_lock: true };
270         if db_key.is_some() {
271             db.open_and_restore(db_key)?
272         } else {
273             db.open()?;
274         }
275         let table = Table::new(TABLE_NAME, &db);
276         table.create(COLUMN_INFO)
277     }
278 
279     /// Open database connection.
open(&mut self) -> Result<()>280     pub(crate) fn open(&mut self) -> Result<()> {
281         let mut path_c = self.path.clone();
282         path_c.push('\0');
283 
284         let ret = unsafe { SqliteOpen(path_c.as_ptr(), &mut self.handle as *mut usize as _) };
285         if ret == SQLITE_OK {
286             Ok(())
287         } else {
288             self.close();
289             log_throw_error!(sqlite_err_handle(ret), "[FATAL][DB]Open database failed, err={}", ret)
290         }
291     }
292 
293     /// Open the database connection and restore the database if the connection fails.
open_and_restore(&mut self, db_key: Option<&DbKey>) -> Result<()>294     pub(crate) fn open_and_restore(&mut self, db_key: Option<&DbKey>) -> Result<()> {
295         let result = self.open();
296         if let Some(db_key) = db_key {
297             self.set_db_key(db_key)?;
298         }
299         let result = match result {
300             Err(ret) if ret.code == ErrCode::DataCorrupted => self.restore(),
301             ret => ret,
302         };
303         result
304     }
305 
306     /// Get db name.
get_db_name(&mut self) -> &str307     pub(crate) fn get_db_name(&mut self) -> &str {
308         &self.db_name
309     }
310 
311     /// Close database connection.
close(&mut self)312     fn close(&mut self) {
313         if self.handle != 0 {
314             unsafe { SqliteCloseV2(self.handle as _) };
315             self.handle = 0;
316         }
317     }
318 
319     /// Close database connection.
close_db(&mut self)320     pub(crate) fn close_db(&mut self) {
321         if self.use_lock {
322             let _lock = self.db_lock.mtx.lock().unwrap();
323             self.close()
324         } else {
325             self.close()
326         }
327     }
328 
329     /// Encrypt/Decrypt CE database.
set_db_key(&mut self, p_key: &DbKey) -> Result<()>330     pub fn set_db_key(&mut self, p_key: &DbKey) -> Result<()> {
331         let ret =
332             unsafe { SqliteKey(self.handle as _, p_key.db_key.as_ptr() as *const c_void, p_key.db_key.len() as i32) };
333         if ret == SQLITE_OK {
334             Ok(())
335         } else {
336             log_throw_error!(sqlite_err_handle(ret), "[FATAL][DB]Set database key failed, err={}", ret)
337         }
338     }
339 
340     // Recovery the corrupt database and reopen it.
restore(&mut self) -> Result<()>341     pub(crate) fn restore(&mut self) -> Result<()> {
342         loge!("[WARNING]Database is corrupt, start to restore");
343         self.close();
344         if let Err(e) = fs::copy(&self.backup_path, &self.path) {
345             return log_throw_error!(ErrCode::FileOperationError, "[FATAL][DB]Recovery database failed, err={}", e);
346         }
347         self.open()
348     }
349 
350     /// Get database version, default is 0.
get_db_version(&self) -> Result<u32>351     fn get_db_version(&self) -> Result<u32> {
352         let stmt = Statement::prepare("pragma user_version", self)?;
353         stmt.step()?;
354         let version = stmt.query_column_int(0);
355         Ok(version)
356     }
357 
358     /// Get database version, default is 0.
359     #[allow(dead_code)]
get_version(&self) -> Result<u32>360     pub(crate) fn get_version(&self) -> Result<u32> {
361         let _lock = self.db_lock.mtx.lock().unwrap();
362         self.get_db_version()
363     }
364 
365     /// Update the database version for database upgrade.
366     #[allow(dead_code)]
set_version(&self, ver: u32) -> Result<()>367     pub(crate) fn set_version(&self, ver: u32) -> Result<()> {
368         let sql = format!("pragma user_version = {}", ver);
369         self.exec(sql.as_str())
370     }
371 
372     /// Upgrade database to new version.
373     #[allow(dead_code)]
upgrade(&mut self, user_id: i32, target_ver: u32, callback: UpgradeDbCallback) -> Result<()>374     pub fn upgrade(&mut self, user_id: i32, target_ver: u32, callback: UpgradeDbCallback) -> Result<()> {
375         let mut current_ver = self.get_db_version()?;
376         if current_ver >= target_ver {
377             return Ok(());
378         }
379         logi!("current database version: {}, target version: {}", current_ver, target_ver);
380         while current_ver < target_ver {
381             match current_ver {
382                 DB_UPGRADE_VERSION_V1 => {
383                     self.restore_if_exec_fail(|e: &Table| e.upgrade(DB_UPGRADE_VERSION_V2, UPGRADE_COLUMN_INFO_V2))?;
384                     current_ver += 1;
385                 },
386                 DB_UPGRADE_VERSION_V2 => {
387                     self.restore_if_exec_fail(|e: &Table| e.upgrade(DB_UPGRADE_VERSION_V3, UPGRADE_COLUMN_INFO_V3))?;
388                     current_ver += 1;
389                 },
390                 DB_UPGRADE_VERSION_V3 => {
391                     if self.upgrade_key_alias(user_id)? {
392                         self.restore_if_exec_fail(|e: &Table| {
393                             e.upgrade(DB_UPGRADE_VERSION_V4, UPGRADE_COLUMN_INFO_V4)
394                         })?;
395                         current_ver += 1;
396                     } else {
397                         break;
398                     }
399                 },
400                 DB_UPGRADE_VERSION_V4 => {
401                     self.restore_if_exec_fail(|e: &Table| e.upgrade(DB_UPGRADE_VERSION, UPGRADE_COLUMN_INFO))?;
402                     current_ver += 1;
403                 },
404                 _ => break,
405             }
406         }
407 
408         callback(self, current_ver, target_ver)
409     }
410 
upgrade_key_alias(&mut self, user_id: i32) -> Result<bool>411     fn upgrade_key_alias(&mut self, user_id: i32) -> Result<bool> {
412         let query_results = self.query_data_without_lock(
413             &vec![
414                 column::OWNER_TYPE,
415                 column::OWNER,
416                 column::AUTH_TYPE,
417                 column::ACCESSIBILITY,
418                 column::REQUIRE_PASSWORD_SET,
419             ],
420             &DbMap::new(),
421             None,
422             true,
423         )?;
424 
425         let mut upgrade_result = true;
426         for query_result in query_results {
427             let owner_type = query_result.get_enum_attr(&column::OWNER_TYPE)?;
428             let owner_info = query_result.get_bytes_attr(&column::OWNER)?;
429             let calling_info = CallingInfo::new(user_id, owner_type, owner_info.to_vec(), None);
430             let auth_type = query_result.get_enum_attr(&column::AUTH_TYPE)?;
431             let access_type = query_result.get_enum_attr(&column::ACCESSIBILITY)?;
432             let require_password_set = query_result.get_bool_attr(&column::REQUIRE_PASSWORD_SET)?;
433             // upgrade_result is set to false as long as any call in the loop for renaming key alias returned false.
434             upgrade_result &= rename_key_alias(&calling_info, auth_type, access_type, require_password_set);
435         }
436 
437         Ok(upgrade_result)
438     }
439 
440     /// Delete database file.
441     #[allow(dead_code)]
delete(user_id: i32, db_name: &str) -> Result<()>442     pub(crate) fn delete(user_id: i32, db_name: &str) -> Result<()> {
443         let path = fmt_de_db_path_with_name(user_id, db_name);
444         let backup_path = fmt_backup_path(&path);
445         if let Err(e) = fs::remove_file(path) {
446             return log_throw_error!(ErrCode::FileOperationError, "[FATAL][DB]Delete database failed, err={}", e);
447         }
448 
449         if let Err(e) = fs::remove_file(backup_path) {
450             return log_throw_error!(
451                 ErrCode::FileOperationError,
452                 "[FATAL][DB]Delete backup database failed, err={}",
453                 e
454             );
455         }
456         Ok(())
457     }
458 
459     /// Print the error message of database.
print_db_msg(&self)460     pub(crate) fn print_db_msg(&self) {
461         let msg = unsafe { SqliteErrMsg(self.handle as _) };
462         if !msg.is_null() {
463             let s = unsafe { CStr::from_ptr(msg as _) };
464             if let Ok(rs) = s.to_str() {
465                 loge!("[FATAL][DB]Database error message: {}", rs);
466             }
467         }
468     }
469 
470     /// execute sql without prepare
exec(&self, sql: &str) -> Result<()>471     pub fn exec(&self, sql: &str) -> Result<()> {
472         let mut sql_s = sql.to_string();
473         sql_s.push('\0');
474         let mut msg: *mut u8 = null_mut();
475         let ret = unsafe { SqliteExec(self.handle as _, sql_s.as_ptr(), &mut msg as _) };
476         if !msg.is_null() {
477             let s = unsafe { CStr::from_ptr(msg as _) };
478             if let Ok(rs) = s.to_str() {
479                 return log_throw_error!(
480                     sqlite_err_handle(ret),
481                     "[FATAL]Database execute sql failed. error code={}, error msg={}",
482                     ret,
483                     rs
484                 );
485             }
486             unsafe { SqliteFree(msg as _) };
487         }
488         if ret == SQLITE_OK {
489             Ok(())
490         } else {
491             log_throw_error!(sqlite_err_handle(ret), "[FATAL]Database execute sql failed. error code={}", ret)
492         }
493     }
494 
495     /// execute func in db, if failed and error code is data corrupted then restore
restore_if_exec_fail<T, F: Fn(&Table) -> Result<T>>(&mut self, func: F) -> Result<T>496     pub(crate) fn restore_if_exec_fail<T, F: Fn(&Table) -> Result<T>>(&mut self, func: F) -> Result<T> {
497         let table = Table::new(TABLE_NAME, self);
498         let result = func(&table);
499         match result {
500             Err(ret) if ret.code == ErrCode::DataCorrupted => {
501                 self.restore()?;
502                 let table = Table::new(TABLE_NAME, self); // Database handle will be changed.
503                 func(&table)
504             },
505             ret => ret,
506         }
507     }
508 
509     /// Create adapt cloud table for adaptation.
create_adapt_cloud_table(&mut self) -> Result<()>510     pub fn create_adapt_cloud_table(&mut self) -> Result<()> {
511         let table = Table::new(ADAPT_CLOUD_TABLE, self);
512         if table.exist()? {
513             return Ok(())
514         }
515         table.create(ADAPT_CLOUD_COLUMN_INFO)
516     }
517 
518     /// Insert datas into database.
519     /// The datas is a map of column-data pair.
520     /// If the operation is successful, the number of inserted data is returned.
521     ///
522     /// # Examples
523     ///
524     /// ```
525     /// use asset_definition::Value;
526     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
527     ///
528     /// // SQL: insert into table_name(Owner,OwnerType,Alias,value) values('owner',1,'alias','insert_value')
529     /// let datas = DbMap::new();
530     /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
531     /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
532     /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
533     /// datas.insert("value", Value::Bytes(b"insert_value".to_vec()));
534     /// let user_id = 100;
535     /// let ret = Database::build(user_id)?.insert_datas(&datas);
536     /// ```
537     ///
538     #[inline(always)]
insert_datas(&mut self, datas: &DbMap) -> Result<i32>539     pub fn insert_datas(&mut self, datas: &DbMap) -> Result<i32> {
540         let _lock: std::sync::MutexGuard<'_, i32> = self.db_lock.mtx.lock().unwrap();
541         let closure = |e: &Table| {
542             let mut query = DbMap::new();
543             query.insert_attr(column::ALIAS, datas.get_bytes_attr(&column::ALIAS)?.clone());
544             query.insert_attr(column::OWNER, datas.get_bytes_attr(&column::OWNER)?.clone());
545             query.insert_attr(column::OWNER_TYPE, datas.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?);
546             if e.is_data_exists(&query, false)? {
547                 log_throw_error!(ErrCode::Duplicated, "[FATAL]The data with the specified alias already exists.")
548             } else {
549                 e.insert_row(datas)
550             }
551         };
552         self.restore_if_exec_fail(closure)
553     }
554 
555     /// Insert data in asset and adapt table.
insert_cloud_adapt_data_without_lock(&mut self, datas: &DbMap, adapt_attributes: &DbMap) -> Result<i32>556     pub fn insert_cloud_adapt_data_without_lock(&mut self, datas: &DbMap, adapt_attributes: &DbMap) -> Result<i32> {
557         let closure = |e: &Table| {
558             let mut query = DbMap::new();
559             query.insert_attr(column::ALIAS, datas.get_bytes_attr(&column::ALIAS)?.clone());
560             query.insert_attr(column::OWNER, datas.get_bytes_attr(&column::OWNER)?.clone());
561             query.insert_attr(column::OWNER_TYPE, datas.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?);
562             if e.is_data_exists(&query, false)? {
563                 log_throw_error!(ErrCode::Duplicated, "[FATAL]The data with the specified alias already exists.")
564             } else {
565                 e.insert_adapt_data_row(datas, adapt_attributes)
566             }
567         };
568         self.restore_if_exec_fail(closure)
569     }
570 
571     /// Insert data in asset and adapt table.
insert_cloud_adapt_data(&mut self, datas: &DbMap, adapt_attributes: &DbMap) -> Result<i32>572     pub fn insert_cloud_adapt_data(&mut self, datas: &DbMap, adapt_attributes: &DbMap) -> Result<i32> {
573         let _lock: std::sync::MutexGuard<'_, i32> = self.db_lock.mtx.lock().unwrap();
574         self.insert_cloud_adapt_data_without_lock(datas, adapt_attributes)
575     }
576 
577     /// Delete datas from database.
578     /// The condition is a map of column-data pair.
579     /// If the operation is successful, the number of deleted data is returned.
580     ///
581     /// # Examples
582     ///
583     /// ```
584     /// use asset_definition::Value;
585     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
586     ///
587     /// // SQL: delete from table_name where Owner='owner' and OwnerType=1 and Alias='alias' and value='delete_value'
588     /// let datas = DbMap::new();
589     /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
590     /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
591     /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
592     /// datas.insert("value", Value::Bytes(b"delete_value".to_vec()));
593     /// let user_id = 100;
594     /// let ret = Database::build(user_id)?.delete_datas(&datas, None, false);
595     /// ```
596     ///
597     ///
598     #[inline(always)]
delete_datas( &mut self, condition: &DbMap, reverse_condition: Option<&DbMap>, is_filter_sync: bool, ) -> Result<i32>599     pub fn delete_datas(
600         &mut self,
601         condition: &DbMap,
602         reverse_condition: Option<&DbMap>,
603         is_filter_sync: bool,
604     ) -> Result<i32> {
605         let _lock = self.db_lock.mtx.lock().unwrap();
606         let closure = |e: &Table| e.delete_row(condition, reverse_condition, is_filter_sync);
607         self.restore_if_exec_fail(closure)
608     }
609 
610     /// Delete datas from database with specific condition.
611     /// If the operation is successful, the number of deleted data is returned.
612     #[inline(always)]
delete_specific_condition_datas(&mut self, specific_cond: &str, condition_value: &[Value]) -> Result<i32>613     pub fn delete_specific_condition_datas(&mut self, specific_cond: &str, condition_value: &[Value]) -> Result<i32> {
614         let _lock = self.db_lock.mtx.lock().unwrap();
615         let closure = |e: &Table| e.delete_with_specific_cond(specific_cond, condition_value);
616         self.restore_if_exec_fail(closure)
617     }
618 
619     /// Delete datas from database with specific condition.
620     /// If the operation is successful, the number of deleted data is returned.
621     #[inline(always)]
delete_batch_datas(&mut self, condition: &DbMap, update_datas: &DbMap, aliases: &[Vec<u8>]) -> Result<i32>622     pub fn delete_batch_datas(&mut self, condition: &DbMap, update_datas: &DbMap, aliases: &[Vec<u8>]) -> Result<i32> {
623         let _lock = self.db_lock.mtx.lock().unwrap();
624         let closure = |e: &Table| e.local_delete_batch_datas(condition, update_datas, aliases);
625         self.restore_if_exec_fail(closure)
626     }
627 
628     /// Delete datas from database with specific condition.
delete_adapt_data_without_lock( &mut self, condition: Option<&DbMap>, adapt_attributes: Option<&DbMap>, ) -> Result<i32>629     pub fn delete_adapt_data_without_lock(
630         &mut self,
631         condition: Option<&DbMap>,
632         adapt_attributes: Option<&DbMap>,
633     ) -> Result<i32> {
634         let closure = |e: &Table| e.delete_adapt_data_row(condition, adapt_attributes);
635         self.restore_if_exec_fail(closure)
636     }
637 
638     /// Delete datas from database with specific condition.
delete_adapt_data( &mut self, condition: Option<&DbMap>, adapt_attributes: Option<&DbMap>, ) -> Result<i32>639     pub fn delete_adapt_data(
640         &mut self,
641         condition: Option<&DbMap>,
642         adapt_attributes: Option<&DbMap>,
643     ) -> Result<i32> {
644         let _lock = self.db_lock.mtx.lock().unwrap();
645         self.delete_adapt_data_without_lock(condition, adapt_attributes)
646     }
647 
648     /// Update datas in database.
649     /// The datas is a map of column-data pair.
650     /// If the operation is successful, the number of updated data is returned.
651     ///
652     /// # Examples
653     ///
654     /// ```
655     /// use asset_definition::Value;
656     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
657     ///
658     /// // SQL: update table_name set alias='update_value' where Owner='owner' and OwnerType=1 and Alias='alias'
659     /// let cond = DbMap.new();
660     /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
661     /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
662     /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
663     /// let datas = DbMap::from([("alias", Value::Bytes(b"update_value".to_vec()))]);
664     /// let user_id = 100;
665     /// let ret = Database::build(user_id)?.update_datas(&condition, true, &datas);
666     /// ```
667     #[inline(always)]
update_datas(&mut self, condition: &DbMap, is_filter_sync: bool, datas: &DbMap) -> Result<i32>668     pub fn update_datas(&mut self, condition: &DbMap, is_filter_sync: bool, datas: &DbMap) -> Result<i32> {
669         let _lock = self.db_lock.mtx.lock().unwrap();
670         let closure = |e: &Table| e.update_row(condition, is_filter_sync, datas);
671         self.restore_if_exec_fail(closure)
672     }
673 
674     /// Check whether data exists in the database.
675     ///
676     /// # Examples
677     ///
678     /// ```
679     /// use asset_definition::Value;
680     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
681     ///
682     /// // SQL: select count(*) as count from table_name where Owner='owner' and OwnerType=1 and Alias='alias'
683     /// let datas = DbMap::new();
684     /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
685     /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
686     /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
687     /// let user_id = 100;
688     /// let exist = Database::build(user_id)?.is_data_exists(&datas, false);
689     /// ```
690     #[inline(always)]
is_data_exists(&mut self, condition: &DbMap, is_filter_sync: bool) -> Result<bool>691     pub fn is_data_exists(&mut self, condition: &DbMap, is_filter_sync: bool) -> Result<bool> {
692         let _lock = self.db_lock.mtx.lock().unwrap();
693         let closure = |e: &Table| e.is_data_exists(condition, is_filter_sync);
694         self.restore_if_exec_fail(closure)
695     }
696 
697     /// Query data that meets specified conditions(can be empty) from the database.
698     /// If the operation is successful, the resultSet is returned.
699     ///
700     /// # Examples
701     ///
702     /// ```
703     /// use asset_definition::Value;
704     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
705     ///
706     /// // SQL: select * from table_name where Owner='owner' and OwnerType=1 and Alias='alias'
707     /// let cond = DbMap::new();
708     /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
709     /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
710     /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
711     /// let user_id = 100;
712     /// let ret = Database::build(user_id)?.query_datas(&vec![], &cond, None, false);
713     /// ```
714     #[inline(always)]
query_datas( &mut self, columns: &Vec<&'static str>, condition: &DbMap, query_options: Option<&QueryOptions>, is_filter_sync: bool, ) -> Result<Vec<DbMap>>715     pub fn query_datas(
716         &mut self,
717         columns: &Vec<&'static str>,
718         condition: &DbMap,
719         query_options: Option<&QueryOptions>,
720         is_filter_sync: bool,
721     ) -> Result<Vec<DbMap>> {
722         let _lock = self.db_lock.mtx.lock().unwrap();
723         let closure = |e: &Table| e.query_row(columns, condition, query_options, is_filter_sync, COLUMN_INFO);
724         self.restore_if_exec_fail(closure)
725     }
726 
727     /// Query datas from database with connect table.
728     #[inline(always)]
query_datas_with_connect_table_without_lock(&mut self, columns: &Vec<&'static str>, condition: &DbMap, query_options: Option<&QueryOptions>, is_filter_sync: bool ) -> Result<Vec<DbMap>>729     pub fn query_datas_with_connect_table_without_lock(&mut self,
730         columns: &Vec<&'static str>,
731         condition: &DbMap,
732         query_options: Option<&QueryOptions>,
733         is_filter_sync: bool
734     ) -> Result<Vec<DbMap>> {
735         let closure = |e: &Table| e.query_connect_table_row(columns, condition, query_options, is_filter_sync, COMBINE_COLUMN_INFO);
736         self.restore_if_exec_fail(closure)
737     }
738 
739     /// Query datas from database with connect table.
740     #[inline(always)]
query_datas_with_connect_table(&mut self, columns: &Vec<&'static str>, condition: &DbMap, query_options: Option<&QueryOptions>, is_filter_sync: bool ) -> Result<Vec<DbMap>>741     pub fn query_datas_with_connect_table(&mut self,
742         columns: &Vec<&'static str>,
743         condition: &DbMap,
744         query_options: Option<&QueryOptions>,
745         is_filter_sync: bool
746     ) -> Result<Vec<DbMap>> {
747         let _lock = self.db_lock.mtx.lock().unwrap();
748         self.query_datas_with_connect_table_without_lock(columns, condition, query_options, is_filter_sync)
749     }
750 
751     /// Query data that meets specified conditions(can be empty) from the database.
752     /// If the operation is successful, the resultSet is returned.
753     ///
754     /// # Examples
755     ///
756     /// ```
757     /// use asset_definition::Value;
758     /// use asset_db_operator::{database::Database, types::{column, DbMap}};
759     ///
760     /// // SQL: select * from table_name where Owner='owner' and OwnerType=1 and Alias='alias'
761     /// let cond = DbMap::new();
762     /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver()));
763     /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32));
764     /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver()));
765     /// let user_id = 100;
766     /// let ret = Database::build(user_id)?.query_data_without_lock(&vec![], &cond, None, false);
767     /// ```
query_data_without_lock( &mut self, columns: &Vec<&'static str>, condition: &DbMap, query_options: Option<&QueryOptions>, is_filter_sync: bool, ) -> Result<Vec<DbMap>>768     pub fn query_data_without_lock(
769         &mut self,
770         columns: &Vec<&'static str>,
771         condition: &DbMap,
772         query_options: Option<&QueryOptions>,
773         is_filter_sync: bool,
774     ) -> Result<Vec<DbMap>> {
775         let closure = |e: &Table| e.query_row(columns, condition, query_options, is_filter_sync, COLUMN_INFO);
776         self.restore_if_exec_fail(closure)
777     }
778 
779     /// query how many data fit the query condition
query_data_count(&mut self, condition: &DbMap) -> Result<u32>780     pub fn query_data_count(&mut self, condition: &DbMap) -> Result<u32> {
781         let _lock = self.db_lock.mtx.lock().unwrap();
782         let closure = |e: &Table| e.count_datas(condition, false);
783         self.restore_if_exec_fail(closure)
784     }
785 
786     /// Delete old data and insert new data.
replace_datas(&mut self, condition: &DbMap, is_filter_sync: bool, datas: &DbMap) -> Result<()>787     pub fn replace_datas(&mut self, condition: &DbMap, is_filter_sync: bool, datas: &DbMap) -> Result<()> {
788         let _lock = self.db_lock.mtx.lock().unwrap();
789         let closure = |e: &Table| e.replace_row(condition, is_filter_sync, datas);
790         self.restore_if_exec_fail(closure)
791     }
792 
793     /// Get db lock.
get_db_lock(&self) -> Result<Arc<Mutex<i32>>>794     pub fn get_db_lock(&self) -> Result<Arc<Mutex<i32>>> {
795         Ok(Arc::clone(&self.db_lock.mtx))
796     }
797 }
798 
799 impl Drop for Database {
drop(&mut self)800     fn drop(&mut self) {
801         self.close_db()
802     }
803 }
804