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
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 "value_object.h"
25
26 namespace OHOS {
27 namespace NativeRdb {
28 using namespace OHOS::Rdb;
29
30 const int ERROR_STATUS = -1;
31 const unsigned int SLEEP_TIME = 1000;
32 // move to the highest 32 bits of 64 bits number
33 const int RETRY_TIME = 50;
34 const unsigned int OFFSET = 32;
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
ClearSharedBlock(AppDataFwk::SharedBlock * sharedBlock)90 int ClearSharedBlock(AppDataFwk::SharedBlock *sharedBlock)
91 {
92 int status = sharedBlock->Clear();
93 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
94 return ERROR_STATUS;
95 }
96 return status;
97 }
98
SharedBlockSetColumnNum(AppDataFwk::SharedBlock * sharedBlock,int columnNum)99 int SharedBlockSetColumnNum(AppDataFwk::SharedBlock *sharedBlock, int columnNum)
100 {
101 int status = sharedBlock->SetColumnNum(columnNum);
102 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
103 return ERROR_STATUS;
104 }
105 return status;
106 }
107
FillSharedBlockOpt(SharedBlockInfo * info)108 void FillSharedBlockOpt(SharedBlockInfo *info)
109 {
110 SharedBlockSerializerInfo serializer(info->sharedBlock, info->statement, info->numColumns, info->startPos);
111 Sqlite3SharedBlockMethods sqliteSharedBlock = {
112 1, &serializer, info->isCountAllRows, info->startPos, info->requiredPos, SeriAddRow, SeriReset,
113 SeriFinish, SeriPutString, SeriPutLong, SeriPutDouble, SeriPutBlob, SeriPutNull, SeriPutOther
114 };
115
116 int rc =
117 sqlite3_db_config(info->connection->db, SQLITE_DBCONFIG_SET_SHAREDBLOCK, info->statement, &sqliteSharedBlock);
118 if (rc != SQLITE_OK) {
119 LOG_ERROR("set sqlite shared block methods error. rc=%{private}d", rc);
120 return;
121 }
122 int retryCount = 0;
123 while (true) {
124 int err = sqlite3_step(info->statement);
125 if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
126 LOG_WARN("Database locked, retrying");
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 rc = sqlite3_db_config(info->connection->db, SQLITE_DBCONFIG_SET_SHAREDBLOCK, info->statement, 0);
140 if (rc != SQLITE_OK) {
141 LOG_ERROR("clear sqlite shared block methods error. rc=%{private}d", rc);
142 return;
143 }
144 }
145
FillSharedBlock(SharedBlockInfo * info)146 void FillSharedBlock(SharedBlockInfo *info)
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(info->statement);
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);
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 } else {
172 usleep(SLEEP_TIME);
173 retryCount++;
174 }
175 } else {
176 hasException = true;
177 }
178 }
179 }
180
FillRow(SharedBlockInfo * info)181 void FillRow(SharedBlockInfo *info)
182 {
183 FillOneRowResult fillOneRowResult =
184 FillOneRow(info->sharedBlock, info->statement, info->columnNum, info->startPos, info->addedRows);
185 if (fillOneRowResult == SHARED_BLOCK_IS_FULL && info->addedRows &&
186 info->startPos + info->addedRows <= info->requiredPos) {
187 info->sharedBlock->Clear();
188 info->sharedBlock->SetColumnNum(info->columnNum);
189 info->startPos += info->addedRows;
190 info->addedRows = 0;
191 fillOneRowResult =
192 FillOneRow(info->sharedBlock, info->statement, info->columnNum, info->startPos, info->addedRows);
193 }
194
195 if (fillOneRowResult == FILL_ONE_ROW_SUCESS) {
196 info->addedRows += 1;
197 } else if (fillOneRowResult == SHARED_BLOCK_IS_FULL) {
198 info->isFull = true;
199 } else {
200 info->hasException = true;
201 }
202 }
203
FillOneRow(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int numColumns,int startPos,int addedRows)204 FillOneRowResult FillOneRow(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int numColumns,
205 int startPos, int addedRows)
206 {
207 int status = sharedBlock->AllocRow();
208 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
209 LOG_ERROR("Failed allocating fieldDir at startPos %{public}d row %{public}d, error=%{public}d", startPos,
210 addedRows, status);
211 return SHARED_BLOCK_IS_FULL;
212 }
213
214 FillOneRowResult result = FILL_ONE_ROW_SUCESS;
215 for (int i = 0; i < numColumns; i++) {
216 int type = sqlite3_column_type(statement, i);
217 if (type == SQLITE_TEXT) {
218 // TEXT data
219 result = FillOneRowOfString(sharedBlock, statement, startPos, addedRows, i);
220 } else if (type == SQLITE_INTEGER) {
221 // INTEGER data
222 result = FillOneRowOfLong(sharedBlock, statement, startPos, addedRows, i);
223 } else if (type == SQLITE_FLOAT) {
224 // FLOAT data
225 result = FillOneRowOfFloat(sharedBlock, statement, startPos, addedRows, i);
226 } else if (type == SQLITE_BLOB) {
227 // BLOB data
228 result = FillOneRowOfBlob(sharedBlock, statement, startPos, addedRows, i);
229 } else if (type == SQLITE_NULL) {
230 // NULL field
231 result = FillOneRowOfNull(sharedBlock, statement, startPos, addedRows, i);
232 } else {
233 // Unknown data
234 LOG_ERROR("Unknown column type when filling database shared block.");
235 result = FILL_ONE_ROW_FAIL;
236 break;
237 }
238
239 if (result == SHARED_BLOCK_IS_FULL) {
240 break;
241 }
242 }
243
244 // Free the last row if if was not successfully copied.
245 if (result != FILL_ONE_ROW_SUCESS) {
246 sharedBlock->FreeLastRow();
247 }
248 return result;
249 }
250
251
FillOneRowOfString(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)252 FillOneRowResult FillOneRowOfString(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
253 int addedRows, int pos)
254 {
255 const char *text = reinterpret_cast<const char *>(sqlite3_column_text(statement, pos));
256 if (text == nullptr) {
257 LOG_ERROR("Text is null.");
258 return SHARED_BLOCK_IS_FULL;
259 }
260
261 auto sizeIncludingNull = sqlite3_column_bytes(statement, pos) + 1;
262 int status = sharedBlock->PutString(addedRows, pos, text, sizeIncludingNull);
263 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
264 LOG_ERROR("Failed allocating %{public}d bytes for text at %{public}d,%{public}d, error=%{public}d",
265 sizeIncludingNull, startPos + addedRows, pos, status);
266 return SHARED_BLOCK_IS_FULL;
267 }
268
269 return FILL_ONE_ROW_SUCESS;
270 }
271
FillOneRowOfLong(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)272 FillOneRowResult FillOneRowOfLong(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
273 int addedRows, int pos)
274 {
275 int64_t value = sqlite3_column_int64(statement, pos);
276 int status = sharedBlock->PutLong(addedRows, pos, value);
277 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
278 LOG_ERROR("Failed allocating space for a long in column %{public}d, error=%{public}d", pos, status);
279 return SHARED_BLOCK_IS_FULL;
280 }
281
282 return FILL_ONE_ROW_SUCESS;
283 }
284
FillOneRowOfFloat(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)285 FillOneRowResult FillOneRowOfFloat(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
286 int addedRows, int pos)
287 {
288 double value = sqlite3_column_double(statement, pos);
289 int status = sharedBlock->PutDouble(addedRows, pos, value);
290 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
291 LOG_ERROR("Failed allocating space for a double in column %{public}d, error=%{public}d", pos, status);
292 return SHARED_BLOCK_IS_FULL;
293 }
294
295 return FILL_ONE_ROW_SUCESS;
296 }
297
FillOneRowOfBlob(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)298 FillOneRowResult FillOneRowOfBlob(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
299 int addedRows, int pos)
300 {
301 auto action = &AppDataFwk::SharedBlock::PutBlob;
302 auto *declType = sqlite3_column_decltype(statement, pos);
303 if (declType != nullptr) {
304 std::string type(declType);
305 std::transform(type.begin(), type.end(), type.begin(), [](auto ch) { return std::toupper(ch); });
306 action = (type == ValueObject::DeclType<ValueObject::Asset>()) ? &AppDataFwk::SharedBlock::PutAsset
307 : (type == ValueObject::DeclType<ValueObject::Assets>()) ? &AppDataFwk::SharedBlock::PutAssets
308 : &AppDataFwk::SharedBlock::PutBlob;
309 }
310
311 const void *blob = sqlite3_column_blob(statement, pos);
312 auto size = sqlite3_column_bytes(statement, pos);
313 int status = (sharedBlock->*action)(addedRows, pos, blob, size);
314 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
315 LOG_ERROR("Failed allocating %{public}d bytes for blob at %{public}d,%{public}d, error=%{public}d", size,
316 startPos + addedRows, pos, status);
317 return SHARED_BLOCK_IS_FULL;
318 }
319
320 return FILL_ONE_ROW_SUCESS;
321 }
322
FillOneRowOfNull(AppDataFwk::SharedBlock * sharedBlock,sqlite3_stmt * statement,int startPos,int addedRows,int pos)323 FillOneRowResult FillOneRowOfNull(AppDataFwk::SharedBlock *sharedBlock, sqlite3_stmt *statement, int startPos,
324 int addedRows, int pos)
325 {
326 int status = sharedBlock->PutNull(addedRows, pos);
327 if (status != AppDataFwk::SharedBlock::SHARED_BLOCK_OK) {
328 LOG_ERROR("Failed allocating space for a null in column %{public}d, error=%{public}d", pos, status);
329 return SHARED_BLOCK_IS_FULL;
330 }
331 return FILL_ONE_ROW_SUCESS;
332 }
333
ResetStatement(SharedBlockInfo * sharedBlockInfo)334 bool ResetStatement(SharedBlockInfo *sharedBlockInfo)
335 {
336 sqlite3_reset(sharedBlockInfo->statement);
337
338 if (sharedBlockInfo->startPos > sharedBlockInfo->totalRows) {
339 LOG_ERROR("startPos %{public}d > actual rows %{public}d", sharedBlockInfo->startPos,
340 sharedBlockInfo->totalRows);
341 }
342
343 if (sharedBlockInfo->totalRows > 0 && sharedBlockInfo->addedRows == 0) {
344 return false;
345 }
346 return true;
347 }
348
GetCombinedData(int startPos,int totalRows)349 int64_t GetCombinedData(int startPos, int totalRows)
350 {
351 if (startPos > totalRows) {
352 LOG_ERROR("startPos %{public}d > actual rows %{public}d", startPos, totalRows);
353 }
354
355 auto high = static_cast<uint64_t>(static_cast<uint32_t>(startPos));
356 auto low = static_cast<uint64_t>(static_cast<uint32_t>(totalRows));
357 return static_cast<int64_t>((high << OFFSET) | low);
358 }
359 } // namespace NativeRdb
360 } // namespace OHOS