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