• 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 "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 unsigned int OFFSET = 32;
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 = { 1, &serializer, info->isCountAllRows, info->startPos,
113         info->requiredPos, SeriAddRow, SeriReset, SeriFinish, SeriPutString, SeriPutLong, SeriPutDouble, SeriPutBlob,
114         SeriPutNull, SeriPutOther
115     };
116     auto db = sqlite3_db_handle(stmt);
117     int err = sqlite3_db_config(db, SQLITE_DBCONFIG_SET_SHAREDBLOCK, stmt, &sqliteBlock);
118     if (err != SQLITE_OK) {
119         LOG_ERROR("set sqlite shared block methods error. err=%{public}d, errno=%{public}d", err, errno);
120         return SQLiteError::ErrNo(err);
121     }
122     int retryCount = 0;
123     while (true) {
124         err = sqlite3_step(stmt);
125         if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
126             LOG_WARN("Database locked, retrying err=%{public}d, errno=%{public}d", err, errno);
127             if (retryCount <= RETRY_TIME) {
128                 usleep(SLEEP_TIME);
129                 retryCount++;
130                 continue;
131             }
132         }
133         break;
134     }
135     info->totalRows = serializer.GetTotalRows();
136     info->startPos = serializer.GetStartPos();
137     info->addedRows = serializer.GetAddedRows();
138 
139     err = sqlite3_db_config(db, SQLITE_DBCONFIG_SET_SHAREDBLOCK, stmt, nullptr);
140     if (err != SQLITE_OK) {
141         LOG_ERROR("clear sqlite shared block methods error. err=%{public}d, errno=%{public}d", err, errno);
142     }
143     return SQLiteError::ErrNo(err);
144 }
145 
FillSharedBlock(SharedBlockInfo * info,sqlite3_stmt * stmt)146 int FillSharedBlock(SharedBlockInfo *info, sqlite3_stmt *stmt)
147 {
148     int retryCount = 0;
149     info->totalRows = info->addedRows = 0;
150     bool isFull = false;
151     bool hasException = false;
152     while (!hasException && (!isFull || info->isCountAllRows)) {
153         int err = sqlite3_step(stmt);
154         if (err == SQLITE_ROW) {
155             retryCount = 0;
156             info->totalRows += 1;
157             if (info->startPos >= info->totalRows || isFull) {
158                 continue;
159             }
160             FillRow(info, stmt);
161             isFull = info->isFull;
162             hasException = info->hasException;
163         } else if (err == SQLITE_DONE) {
164             LOG_WARN("Processed all rows.");
165             break;
166         } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
167             LOG_WARN("Database locked, retrying");
168             if (retryCount > RETRY_TIME) {
169                 LOG_ERROR("Bailing on database busy retry.");
170                 hasException = true;
171                 return E_DATABASE_BUSY;
172             } else {
173                 usleep(SLEEP_TIME);
174                 retryCount++;
175             }
176         } else {
177             hasException = true;
178             return SQLiteError::ErrNo(err);
179         }
180     }
181     return E_OK;
182 }
183 
FillRow(SharedBlockInfo * info,sqlite3_stmt * stmt)184 void FillRow(SharedBlockInfo *info, sqlite3_stmt *stmt)
185 {
186     FillOneRowResult fillOneRowResult =
187         FillOneRow(info->sharedBlock, stmt, info->columnNum, info->startPos, info->addedRows);
188     if (fillOneRowResult == SHARED_BLOCK_IS_FULL && info->addedRows &&
189         info->startPos + info->addedRows <= info->requiredPos) {
190         info->sharedBlock->Clear();
191         info->sharedBlock->SetColumnNum(info->columnNum);
192         info->startPos += info->addedRows;
193         info->addedRows = 0;
194         fillOneRowResult =
195             FillOneRow(info->sharedBlock, stmt, info->columnNum, info->startPos, info->addedRows);
196     }
197 
198     if (fillOneRowResult == FILL_ONE_ROW_SUCESS) {
199         info->addedRows += 1;
200     } else if (fillOneRowResult == SHARED_BLOCK_IS_FULL) {
201         info->isFull = true;
202     } else {
203         info->hasException = true;
204     }
205 }
206 
FillOneRow(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int numColumns,int startPos,int addedRows)207 FillOneRowResult FillOneRow(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int numColumns,
208     int startPos, int addedRows)
209 {
210     int status = sharedBlock->AllocRow();
211     if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
212         LOG_ERROR("Failed allocating fieldDir at startPos %{public}d row %{public}d, error=%{public}d", startPos,
213             addedRows, status);
214         return SHARED_BLOCK_IS_FULL;
215     }
216 
217     FillOneRowResult result = FILL_ONE_ROW_SUCESS;
218     for (int i = 0; i < numColumns; i++) {
219         int type = sqlite3_column_type(statement, i);
220         if (type == SQLITE_TEXT) {
221             // TEXT data
222             result = FillOneRowOfString(sharedBlock, statement, startPos, addedRows, i);
223         } else if (type == SQLITE_INTEGER) {
224             // INTEGER data
225             result = FillOneRowOfLong(sharedBlock, statement, startPos, addedRows, i);
226         } else if (type == SQLITE_FLOAT) {
227             // FLOAT data
228             result = FillOneRowOfFloat(sharedBlock, statement, startPos, addedRows, i);
229         } else if (type == SQLITE_BLOB) {
230             // BLOB data
231             result = FillOneRowOfBlob(sharedBlock, statement, startPos, addedRows, i);
232         } else if (type == SQLITE_NULL) {
233             // NULL field
234             result = FillOneRowOfNull(sharedBlock, statement, startPos, addedRows, i);
235         } else {
236             // Unknown data
237             LOG_ERROR("Unknown column type when filling database shared block.");
238             result = FILL_ONE_ROW_FAIL;
239             break;
240         }
241 
242         if (result == SHARED_BLOCK_IS_FULL) {
243             break;
244         }
245     }
246 
247     // Free the last row if if was not successfully copied.
248     if (result != FILL_ONE_ROW_SUCESS) {
249         sharedBlock->FreeLastRow();
250     }
251     return result;
252 }
253 
254 
FillOneRowOfString(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)255 FillOneRowResult FillOneRowOfString(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
256     int addedRows, int pos)
257 {
258     const char *text = reinterpret_cast<const char *>(sqlite3_column_text(statement, pos));
259     if (text == nullptr) {
260         LOG_ERROR("Text is null.");
261         return SHARED_BLOCK_IS_FULL;
262     }
263 
264     auto sizeIncludingNull = sqlite3_column_bytes(statement, pos) + 1;
265     int status = sharedBlock->PutString(addedRows, pos, text, sizeIncludingNull);
266     if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
267         LOG_ERROR("Failed allocating %{public}d bytes for text at %{public}d,%{public}d, error=%{public}d",
268             sizeIncludingNull, startPos + addedRows, pos, status);
269         return SHARED_BLOCK_IS_FULL;
270     }
271 
272     return FILL_ONE_ROW_SUCESS;
273 }
274 
FillOneRowOfLong(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)275 FillOneRowResult FillOneRowOfLong(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
276     int addedRows, int pos)
277 {
278     int64_t value = sqlite3_column_int64(statement, pos);
279     int status = sharedBlock->PutLong(addedRows, pos, value);
280     if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
281         LOG_ERROR("Failed allocating space for a long in column %{public}d, error=%{public}d", pos, status);
282         return SHARED_BLOCK_IS_FULL;
283     }
284 
285     return FILL_ONE_ROW_SUCESS;
286 }
287 
FillOneRowOfFloat(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)288 FillOneRowResult FillOneRowOfFloat(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
289     int addedRows, int pos)
290 {
291     double value = sqlite3_column_double(statement, pos);
292     int status = sharedBlock->PutDouble(addedRows, pos, value);
293     if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
294         LOG_ERROR("Failed allocating space for a double in column %{public}d, error=%{public}d", pos, status);
295         return SHARED_BLOCK_IS_FULL;
296     }
297 
298     return FILL_ONE_ROW_SUCESS;
299 }
300 
FillOneRowOfBlob(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)301 FillOneRowResult FillOneRowOfBlob(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
302     int addedRows, int pos)
303 {
304     auto action = &AppDataFwk::SharedBlock::PutBlob;
305     auto *declType = sqlite3_column_decltype(statement, pos);
306     if (declType != nullptr) {
307         std::string type(declType);
308         std::transform(type.begin(), type.end(), type.begin(), [](auto ch) { return std::toupper(ch); });
309         action = (type == ValueObject::DeclType<ValueObject::Asset>())         ? &AppDataFwk::SharedBlock::PutAsset
310                  : (type == ValueObject::DeclType<ValueObject::Assets>())      ? &AppDataFwk::SharedBlock::PutAssets
311                  : (type == ValueObject::DeclType<ValueObject::FloatVector>()) ? &AppDataFwk::SharedBlock::PutFloats
312                  : (type == ValueObject::DeclType<ValueObject::BigInt>())      ? &AppDataFwk::SharedBlock::PutBigInt
313                                                                                : &AppDataFwk::SharedBlock::PutBlob;
314     }
315 
316     const void *blob = sqlite3_column_blob(statement, pos);
317     auto size = sqlite3_column_bytes(statement, pos);
318     int status = (sharedBlock->*action)(addedRows, pos, blob, size);
319     if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
320         LOG_ERROR("Failed allocating %{public}d bytes for blob at %{public}d,%{public}d, error=%{public}d", size,
321             startPos + addedRows, pos, status);
322         return SHARED_BLOCK_IS_FULL;
323     }
324 
325     return FILL_ONE_ROW_SUCESS;
326 }
327 
FillOneRowOfNull(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)328 FillOneRowResult FillOneRowOfNull(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
329     int addedRows, int pos)
330 {
331     int status = sharedBlock->PutNull(addedRows, pos);
332     if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
333         LOG_ERROR("Failed allocating space for a null in column %{public}d, error=%{public}d", pos, status);
334         return SHARED_BLOCK_IS_FULL;
335     }
336     return FILL_ONE_ROW_SUCESS;
337 }
338 
ResetStatement(SharedBlockInfo * info,sqlite3_stmt * stmt)339 bool ResetStatement(SharedBlockInfo *info, sqlite3_stmt *stmt)
340 {
341     sqlite3_reset(stmt);
342     if (info->startPos > info->totalRows) {
343         LOG_ERROR("startPos %{public}d > actual rows %{public}d", info->startPos, info->totalRows);
344     }
345 
346     if (info->totalRows > 0 && info->addedRows == 0) {
347         return false;
348     }
349     return true;
350 }
351 
GetCombinedData(int startPos,int totalRows)352 int64_t GetCombinedData(int startPos, int totalRows)
353 {
354     if (startPos > totalRows) {
355         LOG_ERROR("startPos %{public}d > actual rows %{public}d", startPos, totalRows);
356     }
357 
358     auto high = static_cast<uint64_t>(static_cast<uint32_t>(startPos));
359     auto low = static_cast<uint64_t>(static_cast<uint32_t>(totalRows));
360     return static_cast<int64_t>((high << OFFSET) | low);
361 }
362 } // namespace NativeRdb
363 } // namespace OHOS