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