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