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