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 #include "rd_sqlite_utils.h"
16 
17 #include <mutex>
18 
19 #include "doc_errno.h"
20 #include "rd_log_print.h"
21 
22 namespace DocumentDB {
23 const int MAX_BLOB_READ_SIZE = 5 * 1024 * 1024; // 5M limit
24 const int BUSY_TIMEOUT_MS = 3000; // 3000ms for sqlite busy timeout.
25 const std::string BEGIN_SQL = "BEGIN TRANSACTION";
26 const std::string BEGIN_IMMEDIATE_SQL = "BEGIN IMMEDIATE TRANSACTION";
27 const std::string COMMIT_SQL = "COMMIT TRANSACTION";
28 const std::string ROLLBACK_SQL = "ROLLBACK TRANSACTION";
29 
30 namespace {
MapSqliteError(int errCode)31 int MapSqliteError(int errCode)
32 {
33     switch (errCode) {
34         case SQLITE_OK:
35             return E_OK;
36         case SQLITE_PERM:
37         case SQLITE_CANTOPEN:
38             return -E_INVALID_ARGS;
39         case SQLITE_READONLY:
40             return -E_FILE_OPERATION;
41         case SQLITE_NOTADB:
42             return -E_INVALID_FILE_FORMAT;
43         case SQLITE_BUSY:
44             return -E_RESOURCE_BUSY;
45         default:
46             return -E_ERROR;
47     }
48 }
49 
50 std::mutex g_logConfigMutex;
51 bool g_configLog = false;
52 } // namespace
53 
SqliteLogCallback(void * data,int err,const char * msg)54 void RDSQLiteUtils::SqliteLogCallback(void *data, int err, const char *msg)
55 {
56     GLOGD("[SQLite] err=%d sys=%d %s", err, errno, sqlite3_errstr(err));
57 }
58 
CreateDataBase(const std::string & path,int flag,sqlite3 * & db)59 int RDSQLiteUtils::CreateDataBase(const std::string &path, int flag, sqlite3 *&db)
60 {
61     {
62         std::lock_guard<std::mutex> lock(g_logConfigMutex);
63         if (!g_configLog) {
64             sqlite3_config(SQLITE_CONFIG_LOG, &SqliteLogCallback, nullptr);
65             g_configLog = true;
66         }
67     }
68 
69     int errCode = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
70     if (errCode != SQLITE_OK) {
71         GLOGE("Open database failed. %d", errCode);
72         if (db != nullptr) {
73             (void)sqlite3_close_v2(db);
74             db = nullptr;
75         }
76         return MapSqliteError(errCode);
77     }
78 
79     errCode = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
80     if (errCode != SQLITE_OK) {
81         GLOGE("Set busy timeout failed:%d", errCode);
82     }
83     return MapSqliteError(errCode);
84 }
85 
GetStatement(sqlite3 * db,const std::string & sql,sqlite3_stmt * & statement)86 int RDSQLiteUtils::GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&statement)
87 {
88     if (db == nullptr) {
89         GLOGE("Invalid db for get statement");
90         return -E_INVALID_ARGS;
91     }
92 
93     // Prepare the new statement only when the input parameter is not null
94     if (statement != nullptr) {
95         return E_OK;
96     }
97     int errCode = sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, nullptr);
98     if (errCode != SQLITE_OK) {
99         GLOGE("Prepare SQLite statement failed:%d", errCode);
100         (void)RDSQLiteUtils::ResetStatement(statement, true);
101         return MapSqliteError(errCode);
102     }
103 
104     if (statement == nullptr) {
105         return -E_ERROR;
106     }
107 
108     return E_OK;
109 }
110 
StepWithRetry(sqlite3_stmt * statement)111 int RDSQLiteUtils::StepWithRetry(sqlite3_stmt *statement)
112 {
113     if (statement == nullptr) {
114         return -E_INVALID_ARGS;
115     }
116 
117     int errCode = sqlite3_step(statement);
118     if (errCode != SQLITE_DONE && errCode != SQLITE_ROW) {
119         GLOGE("[RDSQLiteUtils] Step error:%d, sys:%d", errCode, errno);
120     }
121 
122     return errCode;
123 }
124 
ResetStatement(sqlite3_stmt * & statement,bool finalize)125 int RDSQLiteUtils::ResetStatement(sqlite3_stmt *&statement, bool finalize)
126 {
127     if (statement == nullptr) {
128         return -E_INVALID_ARGS;
129     }
130 
131     int errCode = E_OK;
132     if (!finalize) {
133         errCode = sqlite3_reset(statement);
134         if (errCode != SQLITE_OK) {
135             GLOGE("[RDSQLiteUtils] reset statement error:%d, sys:%d", errCode, errno);
136             goto FINALIZE;
137         }
138 
139         (void)sqlite3_clear_bindings(statement);
140         return errCode;
141     }
142 
143 FINALIZE:
144     int finalizeResult = sqlite3_finalize(statement);
145     if (finalizeResult != SQLITE_OK) {
146         GLOGE("[RDSQLiteUtils] finalize statement error:%d, sys:%d", finalizeResult, errno);
147     }
148     statement = nullptr;
149     return (errCode == SQLITE_OK ? finalizeResult : errCode);
150 }
151 
BindBlobToStatement(sqlite3_stmt * statement,int index,const std::vector<uint8_t> & value)152 int RDSQLiteUtils::BindBlobToStatement(sqlite3_stmt *statement, int index, const std::vector<uint8_t> &value)
153 {
154     if (statement == nullptr) {
155         return -E_INVALID_ARGS;
156     }
157 
158     int errCode;
159     if (value.empty()) {
160         errCode = sqlite3_bind_zeroblob(statement, index, -1); // -1 for zero-length blob.
161     } else {
162         errCode = sqlite3_bind_blob(statement, index, static_cast<const void *>(value.data()), value.size(),
163             SQLITE_TRANSIENT);
164     }
165     return errCode;
166 }
167 
GetColumnBlobValue(sqlite3_stmt * statement,int index,std::vector<uint8_t> & value)168 int RDSQLiteUtils::GetColumnBlobValue(sqlite3_stmt *statement, int index, std::vector<uint8_t> &value)
169 {
170     if (statement == nullptr) {
171         return -E_INVALID_ARGS;
172     }
173 
174     int keySize = sqlite3_column_bytes(statement, index);
175     if (keySize < 0 || keySize > MAX_BLOB_READ_SIZE) {
176         GLOGW("[RDSQLiteUtils][Column blob] size over limit:%d", keySize);
177         value.resize(MAX_BLOB_READ_SIZE + 1); // Reset value size to invalid
178         return E_OK; // Return OK for continue get data, but value is invalid
179     }
180 
181     auto keyRead = static_cast<const uint8_t *>(sqlite3_column_blob(statement, index));
182     if (keySize == 0 || keyRead == nullptr) {
183         value.resize(0);
184     } else {
185         value.resize(keySize);
186         value.assign(keyRead, keyRead + keySize);
187     }
188 
189     return E_OK;
190 }
191 
BindTextToStatement(sqlite3_stmt * statement,int index,const std::string & value)192 int RDSQLiteUtils::BindTextToStatement(sqlite3_stmt *statement, int index, const std::string &value)
193 {
194     if (statement == nullptr) {
195         return -E_INVALID_ARGS;
196     }
197 
198     int errCode = sqlite3_bind_text(statement, index, value.c_str(), value.length(), SQLITE_TRANSIENT);
199     if (errCode != SQLITE_OK) {
200         GLOGE("[SQLiteUtil][Bind text]Failed to bind the value:%d", errCode);
201         return errCode;
202     }
203 
204     return E_OK;
205 }
206 
BeginTransaction(sqlite3 * db,TransactType type)207 int RDSQLiteUtils::BeginTransaction(sqlite3 *db, TransactType type)
208 {
209     if (type == TransactType::IMMEDIATE) {
210         return ExecSql(db, BEGIN_IMMEDIATE_SQL);
211     }
212 
213     return ExecSql(db, BEGIN_SQL);
214 }
215 
CommitTransaction(sqlite3 * db)216 int RDSQLiteUtils::CommitTransaction(sqlite3 *db)
217 {
218     return ExecSql(db, COMMIT_SQL);
219 }
220 
RollbackTransaction(sqlite3 * db)221 int RDSQLiteUtils::RollbackTransaction(sqlite3 *db)
222 {
223     return ExecSql(db, ROLLBACK_SQL);
224 }
225 
ExecSql(sqlite3 * db,const std::string & sql)226 int RDSQLiteUtils::ExecSql(sqlite3 *db, const std::string &sql)
227 {
228     if (db == nullptr || sql.empty()) {
229         return -E_INVALID_ARGS;
230     }
231 
232     char *errMsg = nullptr;
233     int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg);
234     if (errCode != SQLITE_OK && errMsg != nullptr) {
235         GLOGE("Execute sql failed. %d err: %s", errCode, errMsg);
236     }
237 
238     sqlite3_free(errMsg);
239     return MapSqliteError(errCode);
240 }
241 
ExecSql(sqlite3 * db,const std::string & sql,const std::function<int (sqlite3_stmt *)> & bindCallback,const std::function<int (sqlite3_stmt *,bool &)> & resultCallback)242 int RDSQLiteUtils::ExecSql(sqlite3 *db, const std::string &sql, const std::function<int(sqlite3_stmt *)> &bindCallback,
243     const std::function<int(sqlite3_stmt *, bool &)> &resultCallback)
244 {
245     if (db == nullptr || sql.empty()) {
246         return -E_INVALID_ARGS;
247     }
248     bool bindFinish = true;
249     sqlite3_stmt *stmt = nullptr;
250     bool isMatchOneData = false;
251     int errCode = RDSQLiteUtils::GetStatement(db, sql, stmt);
252     if (errCode != E_OK) {
253         goto END;
254     }
255     do {
256         if (bindCallback) {
257             errCode = bindCallback(stmt);
258             if (errCode != E_OK && errCode != -E_UNFINISHED) {
259                 goto END;
260             }
261             bindFinish = (errCode != -E_UNFINISHED); // continue bind if unfinished
262         }
263 
264         while (true) {
265             errCode = RDSQLiteUtils::StepWithRetry(stmt);
266             if (errCode == SQLITE_DONE) {
267                 break;
268             } else if (errCode != SQLITE_ROW) {
269                 goto END; // Step return error
270             }
271             if (resultCallback != nullptr) { // find one data, stop stepping.
272                 errCode = resultCallback(stmt, isMatchOneData);
273             }
274             if (resultCallback != nullptr && ((errCode != E_OK) || isMatchOneData)) {
275                 goto END;
276             }
277         }
278         errCode = RDSQLiteUtils::ResetStatement(stmt, false);
279     } while (!bindFinish);
280 
281 END:
282     (void)RDSQLiteUtils::ResetStatement(stmt, true);
283     return MapSqliteError(errCode);
284 }
285 } // namespace DocumentDB
286