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