1dfe32fa1Soh_ci/* 2dfe32fa1Soh_ci * Copyright (c) 2023 Huawei Device Co., Ltd. 3dfe32fa1Soh_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4dfe32fa1Soh_ci * you may not use this file except in compliance with the License. 5dfe32fa1Soh_ci * You may obtain a copy of the License at 6dfe32fa1Soh_ci * 7dfe32fa1Soh_ci * http://www.apache.org/licenses/LICENSE-2.0 8dfe32fa1Soh_ci * 9dfe32fa1Soh_ci * Unless required by applicable law or agreed to in writing, software 10dfe32fa1Soh_ci * distributed under the License is distributed on an "AS IS" BASIS, 11dfe32fa1Soh_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12dfe32fa1Soh_ci * See the License for the specific language governing permissions and 13dfe32fa1Soh_ci * limitations under the License. 14dfe32fa1Soh_ci */ 15dfe32fa1Soh_ci 16dfe32fa1Soh_ci//! This module provides interfaces for database management. 17dfe32fa1Soh_ci//! Databases are isolated based on users and protected by locks. 18dfe32fa1Soh_ci 19dfe32fa1Soh_ciuse core::ffi::c_void; 20dfe32fa1Soh_ciuse std::{collections::HashMap, ffi::CStr, fs, ptr::null_mut, sync::Mutex}; 21dfe32fa1Soh_ci 22dfe32fa1Soh_ciuse asset_common::{CallingInfo, OwnerType}; 23dfe32fa1Soh_ciuse asset_crypto_manager::secret_key::rename_key_alias; 24dfe32fa1Soh_ciuse asset_db_key_operator::DbKey; 25dfe32fa1Soh_ciuse asset_definition::{log_throw_error, AssetMap, ErrCode, Extension, Result, Tag, Value}; 26dfe32fa1Soh_ciuse asset_file_operator::{ce_operator::remove_ce_files, common::is_file_exist}; 27dfe32fa1Soh_ciuse asset_log::{loge, logi}; 28dfe32fa1Soh_ciuse lazy_static::lazy_static; 29dfe32fa1Soh_ci 30dfe32fa1Soh_ciuse crate::{ 31dfe32fa1Soh_ci database_file_upgrade::{check_and_split_db, construct_splited_db_name}, 32dfe32fa1Soh_ci statement::Statement, 33dfe32fa1Soh_ci table::Table, 34dfe32fa1Soh_ci types::{ 35dfe32fa1Soh_ci column, sqlite_err_handle, DbMap, QueryOptions, COLUMN_INFO, DB_UPGRADE_VERSION, DB_UPGRADE_VERSION_V1, 36dfe32fa1Soh_ci DB_UPGRADE_VERSION_V2, DB_UPGRADE_VERSION_V3, SQLITE_OK, TABLE_NAME, UPGRADE_COLUMN_INFO, 37dfe32fa1Soh_ci UPGRADE_COLUMN_INFO_V2, UPGRADE_COLUMN_INFO_V3, 38dfe32fa1Soh_ci }, 39dfe32fa1Soh_ci}; 40dfe32fa1Soh_ci 41dfe32fa1Soh_ciextern "C" { 42dfe32fa1Soh_ci fn SqliteOpen(file_name: *const u8, pp_db: *mut *mut c_void) -> i32; 43dfe32fa1Soh_ci fn SqliteCloseV2(db: *mut c_void) -> i32; 44dfe32fa1Soh_ci fn SqliteExec(db: *mut c_void, sql: *const u8, msg: *mut *mut u8) -> i32; 45dfe32fa1Soh_ci fn SqliteFree(data: *mut c_void); 46dfe32fa1Soh_ci fn SqliteErrMsg(db: *mut c_void) -> *const u8; 47dfe32fa1Soh_ci fn SqliteKey(db: *mut c_void, pKey: *const c_void, nKey: i32) -> i32; 48dfe32fa1Soh_ci} 49dfe32fa1Soh_ci 50dfe32fa1Soh_ci/// each user have a Database file 51dfe32fa1Soh_cipub(crate) struct UserDbLock { 52dfe32fa1Soh_ci pub(crate) mtx: Mutex<i32>, 53dfe32fa1Soh_ci} 54dfe32fa1Soh_ci 55dfe32fa1Soh_cipub(crate) static OLD_DB_NAME: &str = "asset"; 56dfe32fa1Soh_ci 57dfe32fa1Soh_cilazy_static! { 58dfe32fa1Soh_ci static ref SPLIT_DB_LOCK_MAP: Mutex<HashMap<i32, &'static UserDbLock>> = Mutex::new(HashMap::new()); 59dfe32fa1Soh_ci static ref USER_DB_LOCK_MAP: Mutex<HashMap<(i32, String), &'static UserDbLock>> = Mutex::new(HashMap::new()); 60dfe32fa1Soh_ci} 61dfe32fa1Soh_ci 62dfe32fa1Soh_cipub(crate) fn get_split_db_lock_by_user_id(user_id: i32) -> &'static UserDbLock { 63dfe32fa1Soh_ci let mut map = SPLIT_DB_LOCK_MAP.lock().unwrap(); 64dfe32fa1Soh_ci if let Some(&lock) = map.get(&user_id) { 65dfe32fa1Soh_ci return lock; 66dfe32fa1Soh_ci } 67dfe32fa1Soh_ci 68dfe32fa1Soh_ci let nf = Box::new(UserDbLock { mtx: Mutex::new(user_id) }); 69dfe32fa1Soh_ci // SAFETY: We just push item into SPLIT_DB_LOCK_MAP, never remove item or modify item, 70dfe32fa1Soh_ci // so return a reference of leak item is safe. 71dfe32fa1Soh_ci let nf: &'static UserDbLock = Box::leak(nf); 72dfe32fa1Soh_ci map.insert(user_id, nf); 73dfe32fa1Soh_ci nf 74dfe32fa1Soh_ci} 75dfe32fa1Soh_ci 76dfe32fa1Soh_ci/// If the user exists, the reference to the lock is returned. 77dfe32fa1Soh_ci/// Otherwise, a new lock is created and its reference is returned. 78dfe32fa1Soh_cipub(crate) fn get_file_lock_by_user_id_db_file_name(user_id: i32, db_file_name: String) -> &'static UserDbLock { 79dfe32fa1Soh_ci let mut map = USER_DB_LOCK_MAP.lock().unwrap(); 80dfe32fa1Soh_ci 81dfe32fa1Soh_ci if let Some(&lock) = map.get(&(user_id, db_file_name.clone())) { 82dfe32fa1Soh_ci return lock; 83dfe32fa1Soh_ci } 84dfe32fa1Soh_ci 85dfe32fa1Soh_ci let nf = Box::new(UserDbLock { mtx: Mutex::new(user_id) }); 86dfe32fa1Soh_ci // SAFETY: We just push item into USER_DB_LOCK_MAP, never remove item or modify item, 87dfe32fa1Soh_ci // so return a reference of leak item is safe. 88dfe32fa1Soh_ci let nf: &'static UserDbLock = Box::leak(nf); 89dfe32fa1Soh_ci map.insert((user_id, db_file_name), nf); 90dfe32fa1Soh_ci nf 91dfe32fa1Soh_ci} 92dfe32fa1Soh_ci 93dfe32fa1Soh_ci/// Struct used to store database files and connection information. 94dfe32fa1Soh_ci#[repr(C)] 95dfe32fa1Soh_cipub struct Database { 96dfe32fa1Soh_ci pub(crate) path: String, 97dfe32fa1Soh_ci pub(crate) backup_path: String, 98dfe32fa1Soh_ci pub(crate) handle: usize, // Pointer to the database connection. 99dfe32fa1Soh_ci pub(crate) db_lock: &'static UserDbLock, 100dfe32fa1Soh_ci pub(crate) db_name: String, 101dfe32fa1Soh_ci} 102dfe32fa1Soh_ci 103dfe32fa1Soh_ci/// Callback for database upgrade. 104dfe32fa1Soh_cipub type UpgradeDbCallback = fn(db: &Database, old_ver: u32, new_ver: u32) -> Result<()>; 105dfe32fa1Soh_ci 106dfe32fa1Soh_ci#[cfg(not(test))] 107dfe32fa1Soh_cipub(crate) const DE_ROOT_PATH: &str = "/data/service/el1/public/asset_service"; 108dfe32fa1Soh_ci#[cfg(test)] 109dfe32fa1Soh_cipub(crate) const DE_ROOT_PATH: &str = "/data/asset_test"; 110dfe32fa1Soh_ci 111dfe32fa1Soh_cipub(crate) const CE_ROOT_PATH: &str = "/data/service/el2"; 112dfe32fa1Soh_ci 113dfe32fa1Soh_ci#[inline(always)] 114dfe32fa1Soh_cipub(crate) fn fmt_backup_path(path: &str) -> String { 115dfe32fa1Soh_ci let mut bp = path.to_string(); 116dfe32fa1Soh_ci bp.push_str(".backup"); 117dfe32fa1Soh_ci bp 118dfe32fa1Soh_ci} 119dfe32fa1Soh_ci 120dfe32fa1Soh_ci/// Get asset storage path. 121dfe32fa1Soh_cipub fn get_path() -> String { 122dfe32fa1Soh_ci DE_ROOT_PATH.to_string() 123dfe32fa1Soh_ci} 124dfe32fa1Soh_ci 125dfe32fa1Soh_ci#[inline(always)] 126dfe32fa1Soh_cipub(crate) fn fmt_ce_db_path_with_name(user_id: i32, db_name: &str) -> String { 127dfe32fa1Soh_ci format!("data/service/el2/{}/asset_service/{}.db", user_id, db_name) 128dfe32fa1Soh_ci} 129dfe32fa1Soh_ci 130dfe32fa1Soh_ci#[inline(always)] 131dfe32fa1Soh_cipub(crate) fn fmt_de_db_path_with_name(user_id: i32, db_name: &str) -> String { 132dfe32fa1Soh_ci format!("{}/{}/{}.db", DE_ROOT_PATH, user_id, db_name) 133dfe32fa1Soh_ci} 134dfe32fa1Soh_ci 135dfe32fa1Soh_cifn check_validity_of_db_key(path: &str, user_id: i32) -> Result<()> { 136dfe32fa1Soh_ci if is_file_exist(path)? && !DbKey::check_existance(user_id)? { 137dfe32fa1Soh_ci loge!("[FATAL]There is database bot no database key. Now all data should be cleared and restart over."); 138dfe32fa1Soh_ci remove_ce_files(user_id)?; 139dfe32fa1Soh_ci return log_throw_error!(ErrCode::DataCorrupted, "[FATAL]All data is cleared in {}.", user_id); 140dfe32fa1Soh_ci } 141dfe32fa1Soh_ci Ok(()) 142dfe32fa1Soh_ci} 143dfe32fa1Soh_ci 144dfe32fa1Soh_cipub(crate) fn get_db(user_id: i32, db_name: &str, upgrade_db_version: u32, db_key: Option<&DbKey>) -> Result<Database> { 145dfe32fa1Soh_ci let path = if db_key.is_some() { 146dfe32fa1Soh_ci fmt_ce_db_path_with_name(user_id, db_name) 147dfe32fa1Soh_ci } else { 148dfe32fa1Soh_ci fmt_de_db_path_with_name(user_id, db_name) 149dfe32fa1Soh_ci }; 150dfe32fa1Soh_ci let backup_path = fmt_backup_path(path.as_str()); 151dfe32fa1Soh_ci let lock = get_file_lock_by_user_id_db_file_name(user_id, db_name.to_string().clone()); 152dfe32fa1Soh_ci let mut db = Database { path, backup_path, handle: 0, db_lock: lock, db_name: db_name.to_string() }; 153dfe32fa1Soh_ci let _lock = db.db_lock.mtx.lock().unwrap(); 154dfe32fa1Soh_ci db.open_and_restore(db_key)?; 155dfe32fa1Soh_ci db.restore_if_exec_fail(|e: &Table| e.create_with_version(COLUMN_INFO, upgrade_db_version))?; 156dfe32fa1Soh_ci db.upgrade(user_id, upgrade_db_version, |_, _, _| Ok(()))?; 157dfe32fa1Soh_ci Ok(db) 158dfe32fa1Soh_ci} 159dfe32fa1Soh_ci 160dfe32fa1Soh_cipub(crate) fn get_normal_db(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database> { 161dfe32fa1Soh_ci let path = 162dfe32fa1Soh_ci if is_ce { fmt_ce_db_path_with_name(user_id, db_name) } else { fmt_de_db_path_with_name(user_id, db_name) }; 163dfe32fa1Soh_ci let db_key = if is_ce { 164dfe32fa1Soh_ci check_validity_of_db_key(&path, user_id)?; 165dfe32fa1Soh_ci match DbKey::get_db_key(user_id) { 166dfe32fa1Soh_ci Ok(res) => Some(res), 167dfe32fa1Soh_ci Err(e) if e.code == ErrCode::NotFound || e.code == ErrCode::DataCorrupted => { 168dfe32fa1Soh_ci loge!( 169dfe32fa1Soh_ci "[FATAL]The key is corrupted. Now all data should be cleared and restart over, err is {}.", 170dfe32fa1Soh_ci e.code 171dfe32fa1Soh_ci ); 172dfe32fa1Soh_ci remove_ce_files(user_id)?; 173dfe32fa1Soh_ci return log_throw_error!(ErrCode::DataCorrupted, "[FATAL]All data is cleared in {}.", user_id); 174dfe32fa1Soh_ci }, 175dfe32fa1Soh_ci Err(e) => return Err(e), 176dfe32fa1Soh_ci } 177dfe32fa1Soh_ci } else { 178dfe32fa1Soh_ci None 179dfe32fa1Soh_ci }; 180dfe32fa1Soh_ci get_db(user_id, db_name, DB_UPGRADE_VERSION, db_key.as_ref()) 181dfe32fa1Soh_ci} 182dfe32fa1Soh_ci 183dfe32fa1Soh_ci/// Create de db instance if the value of tag "RequireAttrEncrypted" is not specified or set to false. 184dfe32fa1Soh_ci/// Create ce db instance if true. 185dfe32fa1Soh_cipub fn create_db_instance(attributes: &AssetMap, calling_info: &CallingInfo) -> Result<Database> { 186dfe32fa1Soh_ci match attributes.get(&Tag::RequireAttrEncrypted) { 187dfe32fa1Soh_ci Some(Value::Bool(true)) => { 188dfe32fa1Soh_ci let db = Database::build( 189dfe32fa1Soh_ci calling_info.user_id(), 190dfe32fa1Soh_ci calling_info.owner_type_enum(), 191dfe32fa1Soh_ci calling_info.owner_info(), 192dfe32fa1Soh_ci true, 193dfe32fa1Soh_ci )?; 194dfe32fa1Soh_ci Ok(db) 195dfe32fa1Soh_ci }, 196dfe32fa1Soh_ci _ => { 197dfe32fa1Soh_ci let db = Database::build( 198dfe32fa1Soh_ci calling_info.user_id(), 199dfe32fa1Soh_ci calling_info.owner_type_enum(), 200dfe32fa1Soh_ci calling_info.owner_info(), 201dfe32fa1Soh_ci false, 202dfe32fa1Soh_ci )?; 203dfe32fa1Soh_ci Ok(db) 204dfe32fa1Soh_ci }, 205dfe32fa1Soh_ci } 206dfe32fa1Soh_ci} 207dfe32fa1Soh_ci 208dfe32fa1Soh_ciimpl Database { 209dfe32fa1Soh_ci /// Create a database. 210dfe32fa1Soh_ci pub fn build(user_id: i32, owner_type: OwnerType, owner_info: &[u8], is_ce: bool) -> Result<Database> { 211dfe32fa1Soh_ci if !is_ce { 212dfe32fa1Soh_ci // DE database needs trigger the upgrade action. 213dfe32fa1Soh_ci check_and_split_db(user_id)?; 214dfe32fa1Soh_ci } 215dfe32fa1Soh_ci get_normal_db(user_id, &construct_splited_db_name(owner_type, owner_info, is_ce)?, is_ce) 216dfe32fa1Soh_ci } 217dfe32fa1Soh_ci 218dfe32fa1Soh_ci /// Create a database from a file name. 219dfe32fa1Soh_ci pub fn build_with_file_name(user_id: i32, db_name: &str, is_ce: bool) -> Result<Database> { 220dfe32fa1Soh_ci check_and_split_db(user_id)?; 221dfe32fa1Soh_ci get_normal_db(user_id, db_name, is_ce) 222dfe32fa1Soh_ci } 223dfe32fa1Soh_ci 224dfe32fa1Soh_ci /// Check whether db is ok 225dfe32fa1Soh_ci pub fn check_db_accessible(path: String, user_id: i32, db_name: String, db_key: Option<&DbKey>) -> Result<()> { 226dfe32fa1Soh_ci let lock = get_file_lock_by_user_id_db_file_name(user_id, db_name.clone()); 227dfe32fa1Soh_ci let mut db = Database { path: path.clone(), backup_path: path, handle: 0, db_lock: lock, db_name }; 228dfe32fa1Soh_ci if db_key.is_some() { 229dfe32fa1Soh_ci db.open_and_restore(db_key)? 230dfe32fa1Soh_ci } else { 231dfe32fa1Soh_ci db.open()?; 232dfe32fa1Soh_ci } 233dfe32fa1Soh_ci let table = Table::new(TABLE_NAME, &db); 234dfe32fa1Soh_ci table.create(COLUMN_INFO) 235dfe32fa1Soh_ci } 236dfe32fa1Soh_ci 237dfe32fa1Soh_ci /// Open database connection. 238dfe32fa1Soh_ci pub(crate) fn open(&mut self) -> Result<()> { 239dfe32fa1Soh_ci let mut path_c = self.path.clone(); 240dfe32fa1Soh_ci path_c.push('\0'); 241dfe32fa1Soh_ci 242dfe32fa1Soh_ci let ret = unsafe { SqliteOpen(path_c.as_ptr(), &mut self.handle as *mut usize as _) }; 243dfe32fa1Soh_ci if ret == SQLITE_OK { 244dfe32fa1Soh_ci Ok(()) 245dfe32fa1Soh_ci } else { 246dfe32fa1Soh_ci self.close(); 247dfe32fa1Soh_ci log_throw_error!(sqlite_err_handle(ret), "[FATAL][DB]Open database failed, err={}", ret) 248dfe32fa1Soh_ci } 249dfe32fa1Soh_ci } 250dfe32fa1Soh_ci 251dfe32fa1Soh_ci /// Open the database connection and restore the database if the connection fails. 252dfe32fa1Soh_ci pub(crate) fn open_and_restore(&mut self, db_key: Option<&DbKey>) -> Result<()> { 253dfe32fa1Soh_ci let result = self.open(); 254dfe32fa1Soh_ci if let Some(db_key) = db_key { 255dfe32fa1Soh_ci self.set_db_key(db_key)?; 256dfe32fa1Soh_ci } 257dfe32fa1Soh_ci let result = match result { 258dfe32fa1Soh_ci Err(ret) if ret.code == ErrCode::DataCorrupted => self.restore(), 259dfe32fa1Soh_ci ret => ret, 260dfe32fa1Soh_ci }; 261dfe32fa1Soh_ci result 262dfe32fa1Soh_ci } 263dfe32fa1Soh_ci 264dfe32fa1Soh_ci /// Get db name. 265dfe32fa1Soh_ci pub(crate) fn get_db_name(&mut self) -> &str { 266dfe32fa1Soh_ci &self.db_name 267dfe32fa1Soh_ci } 268dfe32fa1Soh_ci 269dfe32fa1Soh_ci /// Close database connection. 270dfe32fa1Soh_ci fn close(&mut self) { 271dfe32fa1Soh_ci if self.handle != 0 { 272dfe32fa1Soh_ci unsafe { SqliteCloseV2(self.handle as _) }; 273dfe32fa1Soh_ci self.handle = 0; 274dfe32fa1Soh_ci } 275dfe32fa1Soh_ci } 276dfe32fa1Soh_ci 277dfe32fa1Soh_ci /// Close database connection. 278dfe32fa1Soh_ci pub(crate) fn close_db(&mut self) { 279dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 280dfe32fa1Soh_ci self.close() 281dfe32fa1Soh_ci } 282dfe32fa1Soh_ci 283dfe32fa1Soh_ci /// Encrypt/Decrypt CE database. 284dfe32fa1Soh_ci pub fn set_db_key(&mut self, p_key: &DbKey) -> Result<()> { 285dfe32fa1Soh_ci let ret = 286dfe32fa1Soh_ci unsafe { SqliteKey(self.handle as _, p_key.db_key.as_ptr() as *const c_void, p_key.db_key.len() as i32) }; 287dfe32fa1Soh_ci if ret == SQLITE_OK { 288dfe32fa1Soh_ci Ok(()) 289dfe32fa1Soh_ci } else { 290dfe32fa1Soh_ci log_throw_error!(sqlite_err_handle(ret), "[FATAL][DB]Set database key failed, err={}", ret) 291dfe32fa1Soh_ci } 292dfe32fa1Soh_ci } 293dfe32fa1Soh_ci 294dfe32fa1Soh_ci // Recovery the corrupt database and reopen it. 295dfe32fa1Soh_ci pub(crate) fn restore(&mut self) -> Result<()> { 296dfe32fa1Soh_ci loge!("[WARNING]Database is corrupt, start to restore"); 297dfe32fa1Soh_ci self.close(); 298dfe32fa1Soh_ci if let Err(e) = fs::copy(&self.backup_path, &self.path) { 299dfe32fa1Soh_ci return log_throw_error!(ErrCode::FileOperationError, "[FATAL][DB]Recovery database failed, err={}", e); 300dfe32fa1Soh_ci } 301dfe32fa1Soh_ci self.open() 302dfe32fa1Soh_ci } 303dfe32fa1Soh_ci 304dfe32fa1Soh_ci /// Get database version, default is 0. 305dfe32fa1Soh_ci fn get_db_version(&self) -> Result<u32> { 306dfe32fa1Soh_ci let stmt = Statement::prepare("pragma user_version", self)?; 307dfe32fa1Soh_ci stmt.step()?; 308dfe32fa1Soh_ci let version = stmt.query_column_int(0); 309dfe32fa1Soh_ci Ok(version) 310dfe32fa1Soh_ci } 311dfe32fa1Soh_ci 312dfe32fa1Soh_ci /// Get database version, default is 0. 313dfe32fa1Soh_ci #[allow(dead_code)] 314dfe32fa1Soh_ci pub(crate) fn get_version(&self) -> Result<u32> { 315dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 316dfe32fa1Soh_ci self.get_db_version() 317dfe32fa1Soh_ci } 318dfe32fa1Soh_ci 319dfe32fa1Soh_ci /// Update the database version for database upgrade. 320dfe32fa1Soh_ci #[allow(dead_code)] 321dfe32fa1Soh_ci pub(crate) fn set_version(&self, ver: u32) -> Result<()> { 322dfe32fa1Soh_ci let sql = format!("pragma user_version = {}", ver); 323dfe32fa1Soh_ci self.exec(sql.as_str()) 324dfe32fa1Soh_ci } 325dfe32fa1Soh_ci 326dfe32fa1Soh_ci /// Upgrade database to new version. 327dfe32fa1Soh_ci #[allow(dead_code)] 328dfe32fa1Soh_ci pub fn upgrade(&mut self, user_id: i32, target_ver: u32, callback: UpgradeDbCallback) -> Result<()> { 329dfe32fa1Soh_ci let mut current_ver = self.get_db_version()?; 330dfe32fa1Soh_ci logi!("current database version: {}", current_ver); 331dfe32fa1Soh_ci if current_ver >= target_ver { 332dfe32fa1Soh_ci return Ok(()); 333dfe32fa1Soh_ci } 334dfe32fa1Soh_ci while current_ver < target_ver { 335dfe32fa1Soh_ci match current_ver { 336dfe32fa1Soh_ci DB_UPGRADE_VERSION_V1 => { 337dfe32fa1Soh_ci self.restore_if_exec_fail(|e: &Table| e.upgrade(DB_UPGRADE_VERSION_V2, UPGRADE_COLUMN_INFO_V2))?; 338dfe32fa1Soh_ci current_ver += 1; 339dfe32fa1Soh_ci }, 340dfe32fa1Soh_ci DB_UPGRADE_VERSION_V2 => { 341dfe32fa1Soh_ci self.restore_if_exec_fail(|e: &Table| e.upgrade(DB_UPGRADE_VERSION_V3, UPGRADE_COLUMN_INFO_V3))?; 342dfe32fa1Soh_ci current_ver += 1; 343dfe32fa1Soh_ci }, 344dfe32fa1Soh_ci DB_UPGRADE_VERSION_V3 => { 345dfe32fa1Soh_ci if self.upgrade_key_alias(user_id)? { 346dfe32fa1Soh_ci self.restore_if_exec_fail(|e: &Table| e.upgrade(DB_UPGRADE_VERSION, UPGRADE_COLUMN_INFO))?; 347dfe32fa1Soh_ci current_ver += 1; 348dfe32fa1Soh_ci } else { 349dfe32fa1Soh_ci break; 350dfe32fa1Soh_ci } 351dfe32fa1Soh_ci }, 352dfe32fa1Soh_ci _ => break, 353dfe32fa1Soh_ci } 354dfe32fa1Soh_ci } 355dfe32fa1Soh_ci 356dfe32fa1Soh_ci callback(self, current_ver, target_ver) 357dfe32fa1Soh_ci } 358dfe32fa1Soh_ci 359dfe32fa1Soh_ci fn upgrade_key_alias(&mut self, user_id: i32) -> Result<bool> { 360dfe32fa1Soh_ci let query_results = self.query_data_without_lock( 361dfe32fa1Soh_ci &vec![ 362dfe32fa1Soh_ci column::OWNER_TYPE, 363dfe32fa1Soh_ci column::OWNER, 364dfe32fa1Soh_ci column::AUTH_TYPE, 365dfe32fa1Soh_ci column::ACCESSIBILITY, 366dfe32fa1Soh_ci column::REQUIRE_PASSWORD_SET, 367dfe32fa1Soh_ci ], 368dfe32fa1Soh_ci &DbMap::new(), 369dfe32fa1Soh_ci None, 370dfe32fa1Soh_ci true, 371dfe32fa1Soh_ci )?; 372dfe32fa1Soh_ci 373dfe32fa1Soh_ci let mut upgrade_result = true; 374dfe32fa1Soh_ci for query_result in query_results { 375dfe32fa1Soh_ci let owner_type = query_result.get_enum_attr(&column::OWNER_TYPE)?; 376dfe32fa1Soh_ci let owner_info = query_result.get_bytes_attr(&column::OWNER)?; 377dfe32fa1Soh_ci let calling_info = CallingInfo::new(user_id, owner_type, owner_info.to_vec()); 378dfe32fa1Soh_ci let auth_type = query_result.get_enum_attr(&column::AUTH_TYPE)?; 379dfe32fa1Soh_ci let access_type = query_result.get_enum_attr(&column::ACCESSIBILITY)?; 380dfe32fa1Soh_ci let require_password_set = query_result.get_bool_attr(&column::REQUIRE_PASSWORD_SET)?; 381dfe32fa1Soh_ci // upgrade_result is set to false as long as any call in the loop for renaming key alias returned false. 382dfe32fa1Soh_ci upgrade_result &= rename_key_alias(&calling_info, auth_type, access_type, require_password_set); 383dfe32fa1Soh_ci } 384dfe32fa1Soh_ci 385dfe32fa1Soh_ci Ok(upgrade_result) 386dfe32fa1Soh_ci } 387dfe32fa1Soh_ci 388dfe32fa1Soh_ci /// Delete database file. 389dfe32fa1Soh_ci #[allow(dead_code)] 390dfe32fa1Soh_ci pub(crate) fn delete(user_id: i32, db_name: &str) -> Result<()> { 391dfe32fa1Soh_ci let path = fmt_de_db_path_with_name(user_id, db_name); 392dfe32fa1Soh_ci let backup_path = fmt_backup_path(&path); 393dfe32fa1Soh_ci if let Err(e) = fs::remove_file(path) { 394dfe32fa1Soh_ci return log_throw_error!(ErrCode::FileOperationError, "[FATAL][DB]Delete database failed, err={}", e); 395dfe32fa1Soh_ci } 396dfe32fa1Soh_ci 397dfe32fa1Soh_ci if let Err(e) = fs::remove_file(backup_path) { 398dfe32fa1Soh_ci return log_throw_error!( 399dfe32fa1Soh_ci ErrCode::FileOperationError, 400dfe32fa1Soh_ci "[FATAL][DB]Delete backup database failed, err={}", 401dfe32fa1Soh_ci e 402dfe32fa1Soh_ci ); 403dfe32fa1Soh_ci } 404dfe32fa1Soh_ci Ok(()) 405dfe32fa1Soh_ci } 406dfe32fa1Soh_ci 407dfe32fa1Soh_ci /// Print the error message of database. 408dfe32fa1Soh_ci pub(crate) fn print_db_msg(&self) { 409dfe32fa1Soh_ci let msg = unsafe { SqliteErrMsg(self.handle as _) }; 410dfe32fa1Soh_ci if !msg.is_null() { 411dfe32fa1Soh_ci let s = unsafe { CStr::from_ptr(msg as _) }; 412dfe32fa1Soh_ci if let Ok(rs) = s.to_str() { 413dfe32fa1Soh_ci loge!("[FATAL][DB]Database error message: {}", rs); 414dfe32fa1Soh_ci } 415dfe32fa1Soh_ci } 416dfe32fa1Soh_ci } 417dfe32fa1Soh_ci 418dfe32fa1Soh_ci /// execute sql without prepare 419dfe32fa1Soh_ci pub fn exec(&self, sql: &str) -> Result<()> { 420dfe32fa1Soh_ci let mut sql_s = sql.to_string(); 421dfe32fa1Soh_ci sql_s.push('\0'); 422dfe32fa1Soh_ci let mut msg: *mut u8 = null_mut(); 423dfe32fa1Soh_ci let ret = unsafe { SqliteExec(self.handle as _, sql_s.as_ptr(), &mut msg as _) }; 424dfe32fa1Soh_ci if !msg.is_null() { 425dfe32fa1Soh_ci let s = unsafe { CStr::from_ptr(msg as _) }; 426dfe32fa1Soh_ci if let Ok(rs) = s.to_str() { 427dfe32fa1Soh_ci return log_throw_error!( 428dfe32fa1Soh_ci sqlite_err_handle(ret), 429dfe32fa1Soh_ci "[FATAL]Database execute sql failed. error code={}, error msg={}", 430dfe32fa1Soh_ci ret, 431dfe32fa1Soh_ci rs 432dfe32fa1Soh_ci ); 433dfe32fa1Soh_ci } 434dfe32fa1Soh_ci unsafe { SqliteFree(msg as _) }; 435dfe32fa1Soh_ci } 436dfe32fa1Soh_ci if ret == SQLITE_OK { 437dfe32fa1Soh_ci Ok(()) 438dfe32fa1Soh_ci } else { 439dfe32fa1Soh_ci log_throw_error!(sqlite_err_handle(ret), "[FATAL]Database execute sql failed. error code={}", ret) 440dfe32fa1Soh_ci } 441dfe32fa1Soh_ci } 442dfe32fa1Soh_ci 443dfe32fa1Soh_ci /// execute func in db, if failed and error code is data corrupted then restore 444dfe32fa1Soh_ci pub(crate) fn restore_if_exec_fail<T, F: Fn(&Table) -> Result<T>>(&mut self, func: F) -> Result<T> { 445dfe32fa1Soh_ci let table = Table::new(TABLE_NAME, self); 446dfe32fa1Soh_ci let result = func(&table); 447dfe32fa1Soh_ci match result { 448dfe32fa1Soh_ci Err(ret) if ret.code == ErrCode::DataCorrupted => { 449dfe32fa1Soh_ci self.restore()?; 450dfe32fa1Soh_ci let table = Table::new(TABLE_NAME, self); // Database handle will be changed. 451dfe32fa1Soh_ci func(&table) 452dfe32fa1Soh_ci }, 453dfe32fa1Soh_ci ret => ret, 454dfe32fa1Soh_ci } 455dfe32fa1Soh_ci } 456dfe32fa1Soh_ci 457dfe32fa1Soh_ci /// Insert datas into database. 458dfe32fa1Soh_ci /// The datas is a map of column-data pair. 459dfe32fa1Soh_ci /// If the operation is successful, the number of inserted data is returned. 460dfe32fa1Soh_ci /// 461dfe32fa1Soh_ci /// # Examples 462dfe32fa1Soh_ci /// 463dfe32fa1Soh_ci /// ``` 464dfe32fa1Soh_ci /// use asset_definition::Value; 465dfe32fa1Soh_ci /// use asset_db_operator::{database::Database, types::{column, DbMap}}; 466dfe32fa1Soh_ci /// 467dfe32fa1Soh_ci /// // SQL: insert into table_name(Owner,OwnerType,Alias,value) values('owner',1,'alias','insert_value') 468dfe32fa1Soh_ci /// let datas = DbMap::new(); 469dfe32fa1Soh_ci /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver())); 470dfe32fa1Soh_ci /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32)); 471dfe32fa1Soh_ci /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver())); 472dfe32fa1Soh_ci /// datas.insert("value", Value::Bytes(b"insert_value".to_vec())); 473dfe32fa1Soh_ci /// let user_id = 100; 474dfe32fa1Soh_ci /// let ret = Database::build(user_id)?.insert_datas(&datas); 475dfe32fa1Soh_ci /// ``` 476dfe32fa1Soh_ci /// 477dfe32fa1Soh_ci #[inline(always)] 478dfe32fa1Soh_ci pub fn insert_datas(&mut self, datas: &DbMap) -> Result<i32> { 479dfe32fa1Soh_ci let _lock: std::sync::MutexGuard<'_, i32> = self.db_lock.mtx.lock().unwrap(); 480dfe32fa1Soh_ci let closure = |e: &Table| { 481dfe32fa1Soh_ci let mut query = DbMap::new(); 482dfe32fa1Soh_ci query.insert_attr(column::ALIAS, datas.get_bytes_attr(&column::ALIAS)?.clone()); 483dfe32fa1Soh_ci query.insert_attr(column::OWNER, datas.get_bytes_attr(&column::OWNER)?.clone()); 484dfe32fa1Soh_ci query.insert_attr(column::OWNER_TYPE, datas.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?); 485dfe32fa1Soh_ci if e.is_data_exists(&query, false)? { 486dfe32fa1Soh_ci log_throw_error!(ErrCode::Duplicated, "[FATAL]The data with the specified alias already exists.") 487dfe32fa1Soh_ci } else { 488dfe32fa1Soh_ci e.insert_row(datas) 489dfe32fa1Soh_ci } 490dfe32fa1Soh_ci }; 491dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 492dfe32fa1Soh_ci } 493dfe32fa1Soh_ci 494dfe32fa1Soh_ci /// Delete datas from database. 495dfe32fa1Soh_ci /// The condition is a map of column-data pair. 496dfe32fa1Soh_ci /// If the operation is successful, the number of deleted data is returned. 497dfe32fa1Soh_ci /// 498dfe32fa1Soh_ci /// # Examples 499dfe32fa1Soh_ci /// 500dfe32fa1Soh_ci /// ``` 501dfe32fa1Soh_ci /// use asset_definition::Value; 502dfe32fa1Soh_ci /// use asset_db_operator::{database::Database, types::{column, DbMap}}; 503dfe32fa1Soh_ci /// 504dfe32fa1Soh_ci /// // SQL: delete from table_name where Owner='owner' and OwnerType=1 and Alias='alias' and value='delete_value' 505dfe32fa1Soh_ci /// let datas = DbMap::new(); 506dfe32fa1Soh_ci /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver())); 507dfe32fa1Soh_ci /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32)); 508dfe32fa1Soh_ci /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver())); 509dfe32fa1Soh_ci /// datas.insert("value", Value::Bytes(b"delete_value".to_vec())); 510dfe32fa1Soh_ci /// let user_id = 100; 511dfe32fa1Soh_ci /// let ret = Database::build(user_id)?.delete_datas(&cond, None, false); 512dfe32fa1Soh_ci /// ``` 513dfe32fa1Soh_ci /// 514dfe32fa1Soh_ci /// 515dfe32fa1Soh_ci #[inline(always)] 516dfe32fa1Soh_ci pub fn delete_datas( 517dfe32fa1Soh_ci &mut self, 518dfe32fa1Soh_ci condition: &DbMap, 519dfe32fa1Soh_ci reverse_condition: Option<&DbMap>, 520dfe32fa1Soh_ci is_filter_sync: bool, 521dfe32fa1Soh_ci ) -> Result<i32> { 522dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 523dfe32fa1Soh_ci let closure = |e: &Table| e.delete_row(condition, reverse_condition, is_filter_sync); 524dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 525dfe32fa1Soh_ci } 526dfe32fa1Soh_ci 527dfe32fa1Soh_ci /// Delete datas from database with specific condition. 528dfe32fa1Soh_ci /// If the operation is successful, the number of deleted data is returned. 529dfe32fa1Soh_ci #[inline(always)] 530dfe32fa1Soh_ci pub fn delete_specific_condition_datas(&mut self, specific_cond: &str, condition_value: &[Value]) -> Result<i32> { 531dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 532dfe32fa1Soh_ci let closure = |e: &Table| e.delete_with_specific_cond(specific_cond, condition_value); 533dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 534dfe32fa1Soh_ci } 535dfe32fa1Soh_ci 536dfe32fa1Soh_ci /// Update datas in database. 537dfe32fa1Soh_ci /// The datas is a map of column-data pair. 538dfe32fa1Soh_ci /// If the operation is successful, the number of updated data is returned. 539dfe32fa1Soh_ci /// 540dfe32fa1Soh_ci /// # Examples 541dfe32fa1Soh_ci /// 542dfe32fa1Soh_ci /// ``` 543dfe32fa1Soh_ci /// use asset_definition::Value; 544dfe32fa1Soh_ci /// use asset_db_operator::{database::Database, types::{column, DbMap}}; 545dfe32fa1Soh_ci /// 546dfe32fa1Soh_ci /// // SQL: update table_name set alias='update_value' where Owner='owner' and OwnerType=1 and Alias='alias' 547dfe32fa1Soh_ci /// let cond = DbMap.new(); 548dfe32fa1Soh_ci /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver())); 549dfe32fa1Soh_ci /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32)); 550dfe32fa1Soh_ci /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver())); 551dfe32fa1Soh_ci /// let datas = DbMap::from([("alias", Value::Bytes(b"update_value".to_vec()))]); 552dfe32fa1Soh_ci /// let user_id = 100; 553dfe32fa1Soh_ci /// let ret = Database::build(user_id)?.update_datas(&condition, true, &datas); 554dfe32fa1Soh_ci /// ``` 555dfe32fa1Soh_ci #[inline(always)] 556dfe32fa1Soh_ci pub fn update_datas(&mut self, condition: &DbMap, is_filter_sync: bool, datas: &DbMap) -> Result<i32> { 557dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 558dfe32fa1Soh_ci let closure = |e: &Table| e.update_row(condition, is_filter_sync, datas); 559dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 560dfe32fa1Soh_ci } 561dfe32fa1Soh_ci 562dfe32fa1Soh_ci /// Check whether data exists in the database. 563dfe32fa1Soh_ci /// 564dfe32fa1Soh_ci /// # Examples 565dfe32fa1Soh_ci /// 566dfe32fa1Soh_ci /// ``` 567dfe32fa1Soh_ci /// use asset_definition::Value; 568dfe32fa1Soh_ci /// use asset_db_operator::{database::Database, types::{column, DbMap}}; 569dfe32fa1Soh_ci /// 570dfe32fa1Soh_ci /// // SQL: select count(*) as count from table_name where Owner='owner' and OwnerType=1 and Alias='alias' 571dfe32fa1Soh_ci /// let datas = DbMap::new(); 572dfe32fa1Soh_ci /// datas.insert(column::OWNER, Value::Bytes(b"owner".to_ver())); 573dfe32fa1Soh_ci /// datas.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32)); 574dfe32fa1Soh_ci /// datas.insert(column::ALIAS, Value::Bytes(b"alias".to_ver())); 575dfe32fa1Soh_ci /// let user_id = 100; 576dfe32fa1Soh_ci /// let exist = Database::build(user_id)?.is_data_exists(&datas, false); 577dfe32fa1Soh_ci /// ``` 578dfe32fa1Soh_ci #[inline(always)] 579dfe32fa1Soh_ci pub fn is_data_exists(&mut self, condition: &DbMap, is_filter_sync: bool) -> Result<bool> { 580dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 581dfe32fa1Soh_ci let closure = |e: &Table| e.is_data_exists(condition, is_filter_sync); 582dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 583dfe32fa1Soh_ci } 584dfe32fa1Soh_ci 585dfe32fa1Soh_ci /// Query data that meets specified conditions(can be empty) from the database. 586dfe32fa1Soh_ci /// If the operation is successful, the resultSet is returned. 587dfe32fa1Soh_ci /// 588dfe32fa1Soh_ci /// # Examples 589dfe32fa1Soh_ci /// 590dfe32fa1Soh_ci /// ``` 591dfe32fa1Soh_ci /// use asset_definition::Value; 592dfe32fa1Soh_ci /// use asset_db_operator::{database::Database, types::{column, DbMap}}; 593dfe32fa1Soh_ci /// 594dfe32fa1Soh_ci /// // SQL: select * from table_name where Owner='owner' and OwnerType=1 and Alias='alias' 595dfe32fa1Soh_ci /// let cond = DbMap::new(); 596dfe32fa1Soh_ci /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver())); 597dfe32fa1Soh_ci /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32)); 598dfe32fa1Soh_ci /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver())); 599dfe32fa1Soh_ci /// let user_id = 100; 600dfe32fa1Soh_ci /// let ret = Database::build(user_id)?.query_datas(&vec![], &cond, None, false); 601dfe32fa1Soh_ci /// ``` 602dfe32fa1Soh_ci #[inline(always)] 603dfe32fa1Soh_ci pub fn query_datas( 604dfe32fa1Soh_ci &mut self, 605dfe32fa1Soh_ci columns: &Vec<&'static str>, 606dfe32fa1Soh_ci condition: &DbMap, 607dfe32fa1Soh_ci query_options: Option<&QueryOptions>, 608dfe32fa1Soh_ci is_filter_sync: bool, 609dfe32fa1Soh_ci ) -> Result<Vec<DbMap>> { 610dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 611dfe32fa1Soh_ci let closure = |e: &Table| e.query_row(columns, condition, query_options, is_filter_sync, COLUMN_INFO); 612dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 613dfe32fa1Soh_ci } 614dfe32fa1Soh_ci 615dfe32fa1Soh_ci /// Query data that meets specified conditions(can be empty) from the database. 616dfe32fa1Soh_ci /// If the operation is successful, the resultSet is returned. 617dfe32fa1Soh_ci /// 618dfe32fa1Soh_ci /// # Examples 619dfe32fa1Soh_ci /// 620dfe32fa1Soh_ci /// ``` 621dfe32fa1Soh_ci /// use asset_definition::Value; 622dfe32fa1Soh_ci /// use asset_db_operator::{database::Database, types::{column, DbMap}}; 623dfe32fa1Soh_ci /// 624dfe32fa1Soh_ci /// // SQL: select * from table_name where Owner='owner' and OwnerType=1 and Alias='alias' 625dfe32fa1Soh_ci /// let cond = DbMap::new(); 626dfe32fa1Soh_ci /// cond.insert(column::OWNER, Value::Bytes(b"owner".to_ver())); 627dfe32fa1Soh_ci /// cond.insert(column::OWNER_TYPE, Value::Number(OwnerType::Native as u32)); 628dfe32fa1Soh_ci /// cond.insert(column::ALIAS, Value::Bytes(b"alias".to_ver())); 629dfe32fa1Soh_ci /// let user_id = 100; 630dfe32fa1Soh_ci /// let ret = Database::build(user_id)?.query_data_without_lock(&vec![], &cond, None, false); 631dfe32fa1Soh_ci /// ``` 632dfe32fa1Soh_ci pub fn query_data_without_lock( 633dfe32fa1Soh_ci &mut self, 634dfe32fa1Soh_ci columns: &Vec<&'static str>, 635dfe32fa1Soh_ci condition: &DbMap, 636dfe32fa1Soh_ci query_options: Option<&QueryOptions>, 637dfe32fa1Soh_ci is_filter_sync: bool, 638dfe32fa1Soh_ci ) -> Result<Vec<DbMap>> { 639dfe32fa1Soh_ci let closure = |e: &Table| e.query_row(columns, condition, query_options, is_filter_sync, COLUMN_INFO); 640dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 641dfe32fa1Soh_ci } 642dfe32fa1Soh_ci 643dfe32fa1Soh_ci /// query how many data fit the query condition 644dfe32fa1Soh_ci pub fn query_data_count(&mut self, condition: &DbMap) -> Result<u32> { 645dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 646dfe32fa1Soh_ci let closure = |e: &Table| e.count_datas(condition, false); 647dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 648dfe32fa1Soh_ci } 649dfe32fa1Soh_ci 650dfe32fa1Soh_ci /// Delete old data and insert new data. 651dfe32fa1Soh_ci pub fn replace_datas(&mut self, condition: &DbMap, is_filter_sync: bool, datas: &DbMap) -> Result<()> { 652dfe32fa1Soh_ci let _lock = self.db_lock.mtx.lock().unwrap(); 653dfe32fa1Soh_ci let closure = |e: &Table| e.replace_row(condition, is_filter_sync, datas); 654dfe32fa1Soh_ci self.restore_if_exec_fail(closure) 655dfe32fa1Soh_ci } 656dfe32fa1Soh_ci} 657dfe32fa1Soh_ci 658dfe32fa1Soh_ciimpl Drop for Database { 659dfe32fa1Soh_ci fn drop(&mut self) { 660dfe32fa1Soh_ci self.close_db() 661dfe32fa1Soh_ci } 662dfe32fa1Soh_ci} 663