• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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