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