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 implements database statements and provides precompiled query capabilities.
17dfe32fa1Soh_ci
18dfe32fa1Soh_ciuse core::ffi::c_void;
19dfe32fa1Soh_ciuse std::ffi::CStr;
20dfe32fa1Soh_ci
21dfe32fa1Soh_ciuse asset_definition::{log_throw_error, ErrCode, Result, Value};
22dfe32fa1Soh_ciuse asset_log::loge;
23dfe32fa1Soh_ci
24dfe32fa1Soh_ciuse crate::{
25dfe32fa1Soh_ci    database::Database,
26dfe32fa1Soh_ci    types::{sqlite_err_handle, SQLITE_DONE, SQLITE_OK, SQLITE_ROW},
27dfe32fa1Soh_ci};
28dfe32fa1Soh_ci
29dfe32fa1Soh_citype BindCallback = extern "C" fn(p: *mut c_void);
30dfe32fa1Soh_ciextern "C" {
31dfe32fa1Soh_ci    fn SqliteFinalize(stmt: *mut c_void) -> i32;
32dfe32fa1Soh_ci    fn SqlitePrepareV2(db: *mut c_void, z_sql: *const u8, pp_stmt: *mut *mut c_void, pz_tail: *mut *mut u8) -> i32;
33dfe32fa1Soh_ci    fn SqliteBindBlob(stmt: *mut c_void, index: i32, blob: *const u8, n: i32, callback: Option<BindCallback>) -> i32;
34dfe32fa1Soh_ci    fn SqliteBindInt64(stmt: *mut c_void, index: i32, value: i64) -> i32;
35dfe32fa1Soh_ci    fn SqliteStep(stmt: *mut c_void) -> i32;
36dfe32fa1Soh_ci    fn SqliteColumnName(stmt: *mut c_void, n: i32) -> *const u8;
37dfe32fa1Soh_ci    fn SqliteDataCount(stmt: *mut c_void) -> i32;
38dfe32fa1Soh_ci    fn SqliteColumnBlob(stmt: *mut c_void, i_col: i32) -> *const u8;
39dfe32fa1Soh_ci    fn SqliteColumnInt64(stmt: *mut c_void, i_col: i32) -> i64;
40dfe32fa1Soh_ci    fn SqliteColumnBytes(stmt: *mut c_void, i_col: i32) -> i32;
41dfe32fa1Soh_ci    fn SqliteColumnType(stmt: *mut c_void, i_col: i32) -> i32;
42dfe32fa1Soh_ci    fn SqliteReset(stmt: *mut c_void) -> i32;
43dfe32fa1Soh_ci}
44dfe32fa1Soh_ci
45dfe32fa1Soh_ciconst SQLITE_INTEGER: i32 = 1;
46dfe32fa1Soh_ciconst SQLITE_BLOB: i32 = 4;
47dfe32fa1Soh_ciconst SQLITE_NULL: i32 = 5;
48dfe32fa1Soh_ci
49dfe32fa1Soh_ci#[repr(C)]
50dfe32fa1Soh_cipub(crate) struct Statement<'b> {
51dfe32fa1Soh_ci    pub(crate) sql: String,
52dfe32fa1Soh_ci    db: &'b Database,
53dfe32fa1Soh_ci    handle: usize, // Poiner to statement.
54dfe32fa1Soh_ci}
55dfe32fa1Soh_ci
56dfe32fa1Soh_ciimpl<'b> Statement<'b> {
57dfe32fa1Soh_ci    /// Prepare a sql, you can use '?' for datas and bind datas later.
58dfe32fa1Soh_ci    pub(crate) fn prepare(sql: &str, db: &'b Database) -> Result<Statement<'b>> {
59dfe32fa1Soh_ci        let mut tail = 0usize;
60dfe32fa1Soh_ci        let mut sql_s = sql.to_string();
61dfe32fa1Soh_ci        sql_s.push('\0');
62dfe32fa1Soh_ci        let mut stmt = Statement { sql: sql_s, handle: 0, db };
63dfe32fa1Soh_ci        let ret = unsafe {
64dfe32fa1Soh_ci            SqlitePrepareV2(
65dfe32fa1Soh_ci                db.handle as _,
66dfe32fa1Soh_ci                stmt.sql.as_ptr(),
67dfe32fa1Soh_ci                &mut stmt.handle as *mut usize as _,
68dfe32fa1Soh_ci                &mut tail as *mut usize as _,
69dfe32fa1Soh_ci            )
70dfe32fa1Soh_ci        };
71dfe32fa1Soh_ci        if ret == 0 {
72dfe32fa1Soh_ci            Ok(stmt)
73dfe32fa1Soh_ci        } else {
74dfe32fa1Soh_ci            db.print_db_msg();
75dfe32fa1Soh_ci            log_throw_error!(sqlite_err_handle(ret), "Prepare statement failed, err={}", ret)
76dfe32fa1Soh_ci        }
77dfe32fa1Soh_ci    }
78dfe32fa1Soh_ci
79dfe32fa1Soh_ci    /// Executing the precompiled sql. if succ
80dfe32fa1Soh_ci    /// If the execution is successful, return SQLITE_DONE for update, insert, delete and return SQLITE_ROW for select.
81dfe32fa1Soh_ci    pub(crate) fn step(&self) -> Result<i32> {
82dfe32fa1Soh_ci        let ret = unsafe { SqliteStep(self.handle as _) };
83dfe32fa1Soh_ci        if ret != SQLITE_ROW && ret != SQLITE_DONE {
84dfe32fa1Soh_ci            self.db.print_db_msg();
85dfe32fa1Soh_ci            log_throw_error!(sqlite_err_handle(ret), "Step statement failed, err={}", ret)
86dfe32fa1Soh_ci        } else {
87dfe32fa1Soh_ci            Ok(ret)
88dfe32fa1Soh_ci        }
89dfe32fa1Soh_ci    }
90dfe32fa1Soh_ci
91dfe32fa1Soh_ci    /// Reset statement before bind data for insert statement.
92dfe32fa1Soh_ci    #[allow(dead_code)]
93dfe32fa1Soh_ci    pub(crate) fn reset(&self) -> Result<()> {
94dfe32fa1Soh_ci        let ret = unsafe { SqliteReset(self.handle as _) };
95dfe32fa1Soh_ci        if ret != SQLITE_OK {
96dfe32fa1Soh_ci            self.db.print_db_msg();
97dfe32fa1Soh_ci            log_throw_error!(sqlite_err_handle(ret), "Reset statement failed, err={}", ret)
98dfe32fa1Soh_ci        } else {
99dfe32fa1Soh_ci            Ok(())
100dfe32fa1Soh_ci        }
101dfe32fa1Soh_ci    }
102dfe32fa1Soh_ci
103dfe32fa1Soh_ci    /// Bind data to prepared statement. The index is start from 1.
104dfe32fa1Soh_ci    pub(crate) fn bind_data(&self, index: i32, data: &Value) -> Result<()> {
105dfe32fa1Soh_ci        let ret = match data {
106dfe32fa1Soh_ci            Value::Bytes(b) => unsafe { SqliteBindBlob(self.handle as _, index, b.as_ptr(), b.len() as _, None) },
107dfe32fa1Soh_ci            Value::Number(i) => unsafe { SqliteBindInt64(self.handle as _, index, *i as _) },
108dfe32fa1Soh_ci            Value::Bool(b) => unsafe { SqliteBindInt64(self.handle as _, index, *b as _) },
109dfe32fa1Soh_ci        };
110dfe32fa1Soh_ci        if ret != SQLITE_OK {
111dfe32fa1Soh_ci            self.db.print_db_msg();
112dfe32fa1Soh_ci            log_throw_error!(sqlite_err_handle(ret), "Bind data failed, index={}, err={}", index, ret)
113dfe32fa1Soh_ci        } else {
114dfe32fa1Soh_ci            Ok(())
115dfe32fa1Soh_ci        }
116dfe32fa1Soh_ci    }
117dfe32fa1Soh_ci
118dfe32fa1Soh_ci    /// Query the column name.
119dfe32fa1Soh_ci    pub(crate) fn query_column_name(&self, n: i32) -> Result<&str> {
120dfe32fa1Soh_ci        let s = unsafe { SqliteColumnName(self.handle as _, n) };
121dfe32fa1Soh_ci        if !s.is_null() {
122dfe32fa1Soh_ci            let name = unsafe { CStr::from_ptr(s as _) };
123dfe32fa1Soh_ci            if let Ok(rn) = name.to_str() {
124dfe32fa1Soh_ci                return Ok(rn);
125dfe32fa1Soh_ci            }
126dfe32fa1Soh_ci        }
127dfe32fa1Soh_ci        log_throw_error!(ErrCode::DatabaseError, "[FATAL][DB]Get asset column name failed.")
128dfe32fa1Soh_ci    }
129dfe32fa1Soh_ci
130dfe32fa1Soh_ci    /// Get the count of columns in the query result.
131dfe32fa1Soh_ci    pub(crate) fn data_count(&self) -> i32 {
132dfe32fa1Soh_ci        unsafe { SqliteDataCount(self.handle as _) }
133dfe32fa1Soh_ci    }
134dfe32fa1Soh_ci
135dfe32fa1Soh_ci    /// Query column and return a value of the Value type.
136dfe32fa1Soh_ci    pub(crate) fn query_column_auto_type(&self, i: i32) -> Result<Option<Value>> {
137dfe32fa1Soh_ci        let tp = self.column_type(i);
138dfe32fa1Soh_ci        let data = match tp {
139dfe32fa1Soh_ci            SQLITE_INTEGER => Some(Value::Number(self.query_column_int(i))),
140dfe32fa1Soh_ci            SQLITE_BLOB => {
141dfe32fa1Soh_ci                let blob = self.query_column_blob(i);
142dfe32fa1Soh_ci                if blob.is_empty() {
143dfe32fa1Soh_ci                    None
144dfe32fa1Soh_ci                } else {
145dfe32fa1Soh_ci                    Some(Value::Bytes(blob.to_vec()))
146dfe32fa1Soh_ci                }
147dfe32fa1Soh_ci            },
148dfe32fa1Soh_ci            SQLITE_NULL => None,
149dfe32fa1Soh_ci            t => return log_throw_error!(ErrCode::DatabaseError, "Unexpect column type: {}.", t),
150dfe32fa1Soh_ci        };
151dfe32fa1Soh_ci        Ok(data)
152dfe32fa1Soh_ci    }
153dfe32fa1Soh_ci
154dfe32fa1Soh_ci    /// Query column datas in result set of blob type
155dfe32fa1Soh_ci    /// The index is start from 0.
156dfe32fa1Soh_ci    pub(crate) fn query_column_blob(&self, index: i32) -> &[u8] {
157dfe32fa1Soh_ci        let blob = unsafe { SqliteColumnBlob(self.handle as _, index) };
158dfe32fa1Soh_ci        let len = self.column_bytes(index);
159dfe32fa1Soh_ci        unsafe { core::slice::from_raw_parts(blob, len as _) }
160dfe32fa1Soh_ci    }
161dfe32fa1Soh_ci
162dfe32fa1Soh_ci    /// Query column datas in result set of int type.
163dfe32fa1Soh_ci    /// The index is start with 0.
164dfe32fa1Soh_ci    pub(crate) fn query_column_int(&self, index: i32) -> u32 {
165dfe32fa1Soh_ci        unsafe { SqliteColumnInt64(self.handle as _, index) as u32 }
166dfe32fa1Soh_ci    }
167dfe32fa1Soh_ci
168dfe32fa1Soh_ci    /// Get the bytes of data, you should first call query_column_text or query_column_blob,
169dfe32fa1Soh_ci    pub(crate) fn column_bytes(&self, index: i32) -> i32 {
170dfe32fa1Soh_ci        unsafe { SqliteColumnBytes(self.handle as _, index) }
171dfe32fa1Soh_ci    }
172dfe32fa1Soh_ci
173dfe32fa1Soh_ci    /// Get the type of column.
174dfe32fa1Soh_ci    pub(crate) fn column_type(&self, index: i32) -> i32 {
175dfe32fa1Soh_ci        unsafe { SqliteColumnType(self.handle as _, index) }
176dfe32fa1Soh_ci    }
177dfe32fa1Soh_ci}
178dfe32fa1Soh_ci
179dfe32fa1Soh_ciimpl<'b> Drop for Statement<'b> {
180dfe32fa1Soh_ci    fn drop(&mut self) {
181dfe32fa1Soh_ci        if self.handle != 0 {
182dfe32fa1Soh_ci            let ret = unsafe { SqliteFinalize(self.handle as _) };
183dfe32fa1Soh_ci            if ret != SQLITE_OK {
184dfe32fa1Soh_ci                loge!("sqlite3 finalize fail ret {}", ret);
185dfe32fa1Soh_ci            }
186dfe32fa1Soh_ci        }
187dfe32fa1Soh_ci    }
188dfe32fa1Soh_ci}
189