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