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