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