1 /*
2 * Copyright (c) 2021-2025 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 #define LOG_TAG "ShareBlock"
16 #include "share_block.h"
17
18 #include <unistd.h>
19
20 #include <algorithm>
21
22 #include "logger.h"
23 #include "shared_block.h"
24 #include "shared_block_serializer_info.h"
25 #include "sqlite_errno.h"
26 #include "sqlite_utils.h"
27 #include "string_utils.h"
28 #include "value_object.h"
29
30 namespace OHOS {
31 namespace NativeRdb {
32 using namespace OHOS::Rdb;
33
34 const int ERROR_STATUS = -1;
35 const unsigned int SLEEP_TIME = 1000;
36 // move to the highest 32 bits of 64 bits number
37 const int RETRY_TIME = 50;
38 const int PRINT_RETRY_TIMES = 10;
39
SeriAddRow(void * pCtx,int addedRows)40 int SeriAddRow(void *pCtx, int addedRows)
41 {
42 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
43 return serializer->AddRow(addedRows);
44 }
45
SeriReset(void * pCtx,int startPos)46 int SeriReset(void *pCtx, int startPos)
47 {
48 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
49 return serializer->Reset(startPos);
50 }
51
SeriFinish(void * pCtx,int addedRows,int totalRows)52 int SeriFinish(void *pCtx, int addedRows, int totalRows)
53 {
54 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
55 return serializer->Finish(addedRows, totalRows);
56 }
57
SeriPutString(void * pCtx,int addedRows,int column,const char * text,int size)58 int SeriPutString(void *pCtx, int addedRows, int column, const char *text, int size)
59 {
60 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
61 return serializer->PutString(addedRows, column, text, size);
62 }
63
SeriPutLong(void * pCtx,int addedRows,int column,sqlite3_int64 value)64 int SeriPutLong(void *pCtx, int addedRows, int column, sqlite3_int64 value)
65 {
66 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
67 return serializer->PutLong(addedRows, column, value);
68 }
69
SeriPutDouble(void * pCtx,int addedRows,int column,double value)70 int SeriPutDouble(void *pCtx, int addedRows, int column, double value)
71 {
72 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
73 return serializer->PutDouble(addedRows, column, value);
74 }
75
SeriPutBlob(void * pCtx,int addedRows,int column,const void * blob,int len)76 int SeriPutBlob(void *pCtx, int addedRows, int column, const void *blob, int len)
77 {
78 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
79 return serializer->PutBlob(addedRows, column, blob, len);
80 }
81
SeriPutNull(void * pCtx,int addedRows,int column)82 int SeriPutNull(void *pCtx, int addedRows, int column)
83 {
84 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
85 return serializer->PutNull(addedRows, column);
86 }
87
SeriPutOther(void * pCtx,int addedRows,int column)88 int SeriPutOther(void *pCtx, int addedRows, int column)
89 {
90 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
91 return serializer->PutOther(addedRows, column);
92 }
93
SharedBlockSetColumnNum(AppDataFwk::SharedBlock * sharedBlock,int columnNum)94 int SharedBlockSetColumnNum(AppDataFwk::SharedBlock *sharedBlock, int columnNum)
95 {
96 int status = sharedBlock->SetColumnNum(columnNum);
97 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
98 return ERROR_STATUS;
99 }
100 return status;
101 }
102
FillSharedBlockOpt(SharedBlockInfo * info,sqlite3_stmt * stmt)103 int FillSharedBlockOpt(SharedBlockInfo *info, sqlite3_stmt *stmt)
104 {
105 SharedBlockSerializerInfo serializer(info->sharedBlock, stmt, info->columnNum, info->startPos);
106 Sqlite3SharedBlockMethods sqliteBlock =
107 (info->sharedBlock != nullptr)
108 ? Sqlite3SharedBlockMethods{ 1, &serializer, info->isCountAllRows, info->startPos, info->requiredPos,
109 SeriAddRow, SeriReset, SeriFinish, SeriPutString, SeriPutLong, SeriPutDouble, SeriPutBlob,
110 SeriPutNull, SeriPutOther }
111 : Sqlite3SharedBlockMethods{ 1, &serializer, true, 0, 0, DefAddRow, DefReset, DefFinish, DefPutString,
112 DefPutLong, DefPutDouble, DefPutBlob, DefPutNull, DefPutOther };
113
114 auto db = sqlite3_db_handle(stmt);
115 int cfgErr = sqlite3_db_config(db, SQLITE_DBCONFIG_SET_SHAREDBLOCK, stmt, &sqliteBlock);
116 if (cfgErr != SQLITE_OK) {
117 LOG_ERROR("set sqlite shared block methods error. err=%{public}d, errno=%{public}d", cfgErr, errno);
118 return SQLiteError::ErrNo(cfgErr);
119 }
120 int retryCount = 0;
121 int errCode = SQLITE_OK;
122 while (true) {
123 errCode = sqlite3_step(stmt);
124 if (errCode == SQLITE_LOCKED || errCode == SQLITE_BUSY) {
125 LOG_WARN("Database locked, retrying errCode=%{public}d, errno=%{public}d", errCode, errno);
126 if (retryCount <= RETRY_TIME) {
127 usleep(SLEEP_TIME);
128 retryCount++;
129 continue;
130 }
131 }
132 break;
133 }
134 info->totalRows = serializer.GetTotalRows();
135 info->startPos = serializer.GetStartPos();
136 info->addedRows = serializer.GetAddedRows();
137 info->isFull = serializer.IsFull();
138
139 if (errCode == SQLITE_DONE || errCode == SQLITE_ROW) {
140 errCode = SQLITE_OK;
141 }
142
143 cfgErr = sqlite3_db_config(db, SQLITE_DBCONFIG_SET_SHAREDBLOCK, stmt, nullptr);
144 if (cfgErr != SQLITE_OK || (errCode != SQLITE_OK) || retryCount > PRINT_RETRY_TIMES) {
145 LOG_ERROR("failed, cfgErr=%{public}d errCode=%{public}d retry=%{public}d", cfgErr, errCode, retryCount);
146 }
147 return SQLiteError::ErrNo(errCode);
148 }
149
FillSharedBlock(SharedBlockInfo * info,sqlite3_stmt * stmt)150 int FillSharedBlock(SharedBlockInfo *info, sqlite3_stmt *stmt)
151 {
152 int retryCount = 0;
153 info->totalRows = info->addedRows = 0;
154 bool isFull = false;
155 bool hasException = false;
156 auto fillRow = info->sharedBlock == nullptr ? DefFillRow : FillRow;
157 while (!hasException && (!isFull || info->isCountAllRows)) {
158 int err = sqlite3_step(stmt);
159 if (err == SQLITE_ROW) {
160 retryCount = 0;
161 info->totalRows += 1;
162 if (info->startPos >= info->totalRows || isFull) {
163 continue;
164 }
165 fillRow(info, stmt);
166 isFull = info->isFull;
167 hasException = info->hasException;
168 } else if (err == SQLITE_DONE) {
169 LOG_WARN("Processed all rows.");
170 break;
171 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
172 LOG_WARN("Database locked, retrying");
173 if (retryCount > RETRY_TIME) {
174 LOG_ERROR("Bailing on database busy retry.");
175 hasException = true;
176 return E_DATABASE_BUSY;
177 } else {
178 usleep(SLEEP_TIME);
179 retryCount++;
180 }
181 } else {
182 hasException = true;
183 return SQLiteError::ErrNo(err);
184 }
185 }
186 return E_OK;
187 }
188
FillRow(SharedBlockInfo * info,sqlite3_stmt * stmt)189 void FillRow(SharedBlockInfo *info, sqlite3_stmt *stmt)
190 {
191 FillOneRowResult fillOneRowResult =
192 FillOneRow(info->sharedBlock, stmt, info->columnNum, info->startPos, info->addedRows);
193 if (fillOneRowResult == SHARED_BLOCK_IS_FULL && info->addedRows &&
194 info->startPos + info->addedRows <= info->requiredPos) {
195 info->sharedBlock->Clear();
196 info->sharedBlock->SetColumnNum(info->columnNum);
197 info->startPos += info->addedRows;
198 info->addedRows = 0;
199 fillOneRowResult = FillOneRow(info->sharedBlock, stmt, info->columnNum, info->startPos, info->addedRows);
200 }
201
202 if (fillOneRowResult == FILL_ONE_ROW_SUCESS) {
203 info->addedRows += 1;
204 } else if (fillOneRowResult == SHARED_BLOCK_IS_FULL) {
205 info->isFull = true;
206 } else {
207 info->hasException = true;
208 }
209 }
210
FillOneRow(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int numColumns,int startPos,int addedRows)211 FillOneRowResult FillOneRow(
212 AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int numColumns, int startPos, int addedRows)
213 {
214 int status = sharedBlock->AllocRow();
215 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
216 LOG_ERROR("Failed allocating fieldDir at startPos %{public}d row %{public}d, error=%{public}d", startPos,
217 addedRows, status);
218 return SHARED_BLOCK_IS_FULL;
219 }
220
221 FillOneRowResult result = FILL_ONE_ROW_SUCESS;
222 for (int i = 0; i < numColumns; i++) {
223 int type = sqlite3_column_type(statement, i);
224 if (type == SQLITE_TEXT) {
225 // TEXT data
226 result = FillOneRowOfString(sharedBlock, statement, startPos, addedRows, i);
227 } else if (type == SQLITE_INTEGER) {
228 // INTEGER data
229 result = FillOneRowOfLong(sharedBlock, statement, startPos, addedRows, i);
230 } else if (type == SQLITE_FLOAT) {
231 // FLOAT data
232 result = FillOneRowOfFloat(sharedBlock, statement, startPos, addedRows, i);
233 } else if (type == SQLITE_BLOB) {
234 // BLOB data
235 result = FillOneRowOfBlob(sharedBlock, statement, startPos, addedRows, i);
236 } else if (type == SQLITE_NULL) {
237 // NULL field
238 result = FillOneRowOfNull(sharedBlock, statement, startPos, addedRows, i);
239 } else {
240 // Unknown data
241 LOG_ERROR("Unknown column type when filling database shared block.");
242 result = FILL_ONE_ROW_FAIL;
243 break;
244 }
245
246 if (result == SHARED_BLOCK_IS_FULL) {
247 break;
248 }
249 }
250
251 // Free the last row if if was not successfully copied.
252 if (result != FILL_ONE_ROW_SUCESS) {
253 sharedBlock->FreeLastRow();
254 }
255 return result;
256 }
257
FillOneRowOfString(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)258 FillOneRowResult FillOneRowOfString(
259 AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos, int addedRows, int pos)
260 {
261 const char *text = reinterpret_cast<const char *>(sqlite3_column_text(statement, pos));
262 if (text == nullptr) {
263 LOG_ERROR("Text is null.");
264 return SHARED_BLOCK_IS_FULL;
265 }
266
267 auto sizeIncludingNull = sqlite3_column_bytes(statement, pos) + 1;
268 int status = sharedBlock->PutString(addedRows, pos, text, sizeIncludingNull);
269 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
270 LOG_ERROR("Failed allocating %{public}d bytes for text at %{public}d,%{public}d, error=%{public}d",
271 sizeIncludingNull, startPos + addedRows, pos, status);
272 return SHARED_BLOCK_IS_FULL;
273 }
274
275 return FILL_ONE_ROW_SUCESS;
276 }
277
FillOneRowOfLong(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)278 FillOneRowResult FillOneRowOfLong(
279 AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos, int addedRows, int pos)
280 {
281 int64_t value = sqlite3_column_int64(statement, pos);
282 int status = sharedBlock->PutLong(addedRows, pos, value);
283 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
284 LOG_ERROR("Failed allocating space for a long in column %{public}d, error=%{public}d", pos, status);
285 return SHARED_BLOCK_IS_FULL;
286 }
287
288 return FILL_ONE_ROW_SUCESS;
289 }
290
FillOneRowOfFloat(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)291 FillOneRowResult FillOneRowOfFloat(
292 AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos, int addedRows, int pos)
293 {
294 double value = sqlite3_column_double(statement, pos);
295 int status = sharedBlock->PutDouble(addedRows, pos, value);
296 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
297 LOG_ERROR("Failed allocating space for a double in column %{public}d, error=%{public}d", pos, status);
298 return SHARED_BLOCK_IS_FULL;
299 }
300
301 return FILL_ONE_ROW_SUCESS;
302 }
303
FillOneRowOfBlob(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)304 FillOneRowResult FillOneRowOfBlob(
305 AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos, int addedRows, int pos)
306 {
307 auto action = &AppDataFwk::SharedBlock::PutBlob;
308 auto *declType = sqlite3_column_decltype(statement, pos);
309 if (declType != nullptr) {
310 auto type = StringUtils::TruncateAfterFirstParen(SqliteUtils::StrToUpper(declType));
311 action = (type == ValueObject::DeclType<ValueObject::Asset>()) ? &AppDataFwk::SharedBlock::PutAsset
312 : (type == ValueObject::DeclType<ValueObject::Assets>()) ? &AppDataFwk::SharedBlock::PutAssets
313 : (type == ValueObject::DeclType<ValueObject::FloatVector>()) ? &AppDataFwk::SharedBlock::PutFloats
314 : (type == ValueObject::DeclType<ValueObject::BigInt>()) ? &AppDataFwk::SharedBlock::PutBigInt
315 : &AppDataFwk::SharedBlock::PutBlob;
316 }
317
318 const void *blob = sqlite3_column_blob(statement, pos);
319 auto size = sqlite3_column_bytes(statement, pos);
320 int status = (sharedBlock->*action)(addedRows, pos, blob, size);
321 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
322 LOG_ERROR("Failed allocating %{public}d bytes for blob at %{public}d,%{public}d, error=%{public}d", size,
323 startPos + addedRows, pos, status);
324 return SHARED_BLOCK_IS_FULL;
325 }
326
327 return FILL_ONE_ROW_SUCESS;
328 }
329
FillOneRowOfNull(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)330 FillOneRowResult FillOneRowOfNull(
331 AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos, int addedRows, int pos)
332 {
333 int status = sharedBlock->PutNull(addedRows, pos);
334 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
335 LOG_ERROR("Failed allocating space for a null in column %{public}d, error=%{public}d", pos, status);
336 return SHARED_BLOCK_IS_FULL;
337 }
338 return FILL_ONE_ROW_SUCESS;
339 }
340
ResetStatement(SharedBlockInfo * info,sqlite3_stmt * stmt)341 bool ResetStatement(SharedBlockInfo *info, sqlite3_stmt *stmt)
342 {
343 sqlite3_reset(stmt);
344 if (info->startPos > info->totalRows) {
345 LOG_ERROR("startPos %{public}d > actual rows %{public}d", info->startPos, info->totalRows);
346 }
347
348 if ((info->isFull && info->totalRows > 0 && info->addedRows == 0) && info->sharedBlock != nullptr) {
349 LOG_WARN("over 2MB[%{public}d, %{public}d]", info->totalRows, info->addedRows);
350 return false;
351 }
352 return true;
353 }
354
DefAddRow(void * pCtx,int addedRows)355 int DefAddRow(void *pCtx, int addedRows)
356 {
357 return SQLITE_FULL;
358 }
359
DefReset(void * pCtx,int startPos)360 int DefReset(void *pCtx, int startPos)
361 {
362 return SQLITE_OK;
363 }
364
DefFinish(void * pCtx,int addedRows,int totalRows)365 int DefFinish(void *pCtx, int addedRows, int totalRows)
366 {
367 auto *serializer = static_cast<SharedBlockSerializerInfo *>(pCtx);
368 return serializer->Finish(addedRows, totalRows);
369 }
370
DefPutString(void * pCtx,int addedRows,int column,const char * text,int size)371 int DefPutString(void *pCtx, int addedRows, int column, const char *text, int size)
372 {
373 return SQLITE_FULL;
374 }
375
DefPutLong(void * pCtx,int addedRows,int column,sqlite3_int64 value)376 int DefPutLong(void *pCtx, int addedRows, int column, sqlite3_int64 value)
377 {
378 return SQLITE_FULL;
379 }
380
DefPutDouble(void * pCtx,int addedRows,int column,double value)381 int DefPutDouble(void *pCtx, int addedRows, int column, double value)
382 {
383 return SQLITE_FULL;
384 }
385
DefPutBlob(void * pCtx,int addedRows,int column,const void * blob,int len)386 int DefPutBlob(void *pCtx, int addedRows, int column, const void *blob, int len)
387 {
388 return SQLITE_FULL;
389 }
390
DefPutNull(void * pCtx,int addedRows,int column)391 int DefPutNull(void *pCtx, int addedRows, int column)
392 {
393 return SQLITE_FULL;
394 }
395
DefPutOther(void * pCtx,int addedRows,int column)396 int DefPutOther(void *pCtx, int addedRows, int column)
397 {
398 return SQLITE_FULL;
399 }
400
DefFillRow(SharedBlockInfo * info,sqlite3_stmt * stmt)401 void DefFillRow(SharedBlockInfo *info, sqlite3_stmt *stmt)
402 {
403 info->isFull = true;
404 }
405 } // namespace NativeRdb
406 } // namespace OHOS