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 "db_common.h"
17
18 #include <atomic>
19 #include <charconv>
20 #include <climits>
21 #include <cstdio>
22 #ifndef _WIN32
23 #include <dlfcn.h>
24 #endif
25 #include <mutex>
26 #include <queue>
27
28 #include "cloud/cloud_db_constant.h"
29 #include "cloud/cloud_db_types.h"
30 #include "db_errno.h"
31 #include "param_check_utils.h"
32 #include "platform_specific.h"
33 #include "query_sync_object.h"
34 #include "hash.h"
35 #include "runtime_context.h"
36 #include "version.h"
37
38 namespace DistributedDB {
39 namespace {
RemoveFiles(const std::list<OS::FileAttr> & fileList,OS::FileType type)40 void RemoveFiles(const std::list<OS::FileAttr> &fileList, OS::FileType type)
41 {
42 for (const auto &item : fileList) {
43 if (item.fileType != type) {
44 continue;
45 }
46 int errCode = OS::RemoveFile(item.fileName);
47 if (errCode != E_OK) {
48 LOGE("Remove file failed:%d", errno);
49 }
50 }
51 }
52
RemoveDirectories(const std::list<OS::FileAttr> & fileList,OS::FileType type)53 void RemoveDirectories(const std::list<OS::FileAttr> &fileList, OS::FileType type)
54 {
55 for (auto item = fileList.rbegin(); item != fileList.rend(); ++item) {
56 if (item->fileType != type) {
57 continue;
58 }
59 int errCode = OS::RemoveDBDirectory(item->fileName);
60 if (errCode != 0) {
61 LOGE("Remove directory failed:%d", errno);
62 }
63 }
64 }
65 const std::string CAP_HEX_CHAR_MAP = "0123456789ABCDEF";
66 }
67
68 static std::atomic_bool g_isGrdLoaded = false;
69
CreateDirectory(const std::string & directory)70 int DBCommon::CreateDirectory(const std::string &directory)
71 {
72 bool isExisted = OS::CheckPathExistence(directory);
73 if (!isExisted) {
74 int errCode = OS::MakeDBDirectory(directory);
75 if (errCode != E_OK) {
76 return errCode;
77 }
78 }
79 return E_OK;
80 }
81
VectorToString(const std::vector<uint8_t> & src,std::string & dst)82 void DBCommon::VectorToString(const std::vector<uint8_t> &src, std::string &dst)
83 {
84 dst.clear();
85 dst.assign(src.begin(), src.end());
86 }
87
VectorToHexString(const std::vector<uint8_t> & inVec,const std::string & separator)88 std::string DBCommon::VectorToHexString(const std::vector<uint8_t> &inVec, const std::string &separator)
89 {
90 std::string outString;
91 for (auto &entry : inVec) {
92 outString.push_back(CAP_HEX_CHAR_MAP[entry >> 4]); // high 4 bits to one hex.
93 outString.push_back(CAP_HEX_CHAR_MAP[entry & 0x0F]); // low 4 bits to one hex.
94 outString += separator;
95 }
96 outString.erase(outString.size() - separator.size(), separator.size()); // remove needless separator at last
97 return outString;
98 }
99
PrintHexVector(const std::vector<uint8_t> & data,int line,const std::string & tag)100 void DBCommon::PrintHexVector(const std::vector<uint8_t> &data, int line, const std::string &tag)
101 {
102 const size_t maxDataLength = 1024;
103 const int byteHexNum = 2;
104 size_t dataLength = data.size();
105
106 if (data.size() > maxDataLength) {
107 dataLength = maxDataLength;
108 }
109
110 char *buff = new (std::nothrow) char[dataLength * byteHexNum + 1]; // dual and add one for the end;
111 if (buff == nullptr) {
112 return;
113 }
114
115 for (std::vector<uint8_t>::size_type i = 0; i < dataLength; ++i) {
116 buff[byteHexNum * i] = CAP_HEX_CHAR_MAP[data[i] >> 4]; // high 4 bits to one hex.
117 buff[byteHexNum * i + 1] = CAP_HEX_CHAR_MAP[data[i] & 0x0F]; // low 4 bits to one hex.
118 }
119 buff[dataLength * byteHexNum] = '\0';
120
121 if (line == 0) {
122 LOGD("[%s] size:%zu -- %s", tag.c_str(), data.size(), buff);
123 } else {
124 LOGD("[%s][%d] size:%zu -- %s", tag.c_str(), line, data.size(), buff);
125 }
126
127 delete []buff;
128 return;
129 }
130
CreateStoreDirectory(const std::string & directory,const std::string & identifierName,const std::string & subDir,bool isCreate)131 int DBCommon::CreateStoreDirectory(const std::string &directory, const std::string &identifierName,
132 const std::string &subDir, bool isCreate)
133 {
134 std::string newDir = directory;
135 if (newDir.back() != '/') {
136 newDir += "/";
137 }
138
139 newDir += identifierName;
140 if (!isCreate) {
141 if (!OS::CheckPathExistence(newDir)) {
142 LOGE("Required path does not exist and won't create.");
143 return -E_INVALID_ARGS;
144 }
145 return E_OK;
146 }
147
148 if (directory.empty()) {
149 return -E_INVALID_ARGS;
150 }
151
152 int errCode = DBCommon::CreateDirectory(newDir);
153 if (errCode != E_OK) {
154 return errCode;
155 }
156
157 newDir += ("/" + subDir);
158 return DBCommon::CreateDirectory(newDir);
159 }
160
CopyFile(const std::string & srcFile,const std::string & dstFile)161 int DBCommon::CopyFile(const std::string &srcFile, const std::string &dstFile)
162 {
163 const int copyBlockSize = 4096;
164 std::vector<uint8_t> tmpBlock(copyBlockSize, 0);
165 int errCode;
166 FILE *fileIn = fopen(srcFile.c_str(), "rb");
167 if (fileIn == nullptr) {
168 LOGE("[Common:CpFile] open the source file error:%d", errno);
169 return -E_INVALID_FILE;
170 }
171 FILE *fileOut = fopen(dstFile.c_str(), "wb");
172 if (fileOut == nullptr) {
173 LOGE("[Common:CpFile] open the target file error:%d", errno);
174 errCode = -E_INVALID_FILE;
175 goto END;
176 }
177 for (;;) {
178 size_t readSize = fread(static_cast<void *>(tmpBlock.data()), 1, copyBlockSize, fileIn);
179 if (readSize < copyBlockSize) {
180 // not end and have error.
181 if (feof(fileIn) != 0 && ferror(fileIn) != 0) {
182 LOGE("Copy the file error:%d", errno);
183 errCode = -E_SYSTEM_API_FAIL;
184 break;
185 }
186 }
187
188 if (readSize != 0) {
189 size_t writeSize = fwrite(static_cast<void *>(tmpBlock.data()), 1, readSize, fileOut);
190 if (ferror(fileOut) != 0 || writeSize != readSize) {
191 LOGE("Write the data while copy:%d", errno);
192 errCode = -E_SYSTEM_API_FAIL;
193 break;
194 }
195 }
196
197 if (feof(fileIn) != 0) {
198 errCode = E_OK;
199 break;
200 }
201 }
202
203 END:
204 if (fileIn != nullptr) {
205 (void)fclose(fileIn);
206 }
207 if (fileOut != nullptr) {
208 (void)fclose(fileOut);
209 }
210 return errCode;
211 }
212
RemoveAllFilesOfDirectory(const std::string & dir,bool isNeedRemoveDir)213 int DBCommon::RemoveAllFilesOfDirectory(const std::string &dir, bool isNeedRemoveDir)
214 {
215 std::list<OS::FileAttr> fileList;
216 bool isExisted = OS::CheckPathExistence(dir);
217 if (!isExisted) {
218 return E_OK;
219 }
220 int errCode = OS::GetFileAttrFromPath(dir, fileList, true);
221 if (errCode != E_OK) {
222 return errCode;
223 }
224
225 RemoveFiles(fileList, OS::FileType::FILE);
226 RemoveDirectories(fileList, OS::FileType::PATH);
227 if (isNeedRemoveDir) {
228 // Pay attention to the order of deleting the directory
229 if (OS::CheckPathExistence(dir) && OS::RemoveDBDirectory(dir) != 0) {
230 LOGI("Remove the directory error:%d", errno);
231 errCode = -E_SYSTEM_API_FAIL;
232 }
233 }
234
235 return errCode;
236 }
237
GenerateIdentifierId(const std::string & storeId,const std::string & appId,const std::string & userId,const std::string & subUser,int32_t instanceId)238 std::string DBCommon::GenerateIdentifierId(const std::string &storeId,
239 const std::string &appId, const std::string &userId, const std::string &subUser, int32_t instanceId)
240 {
241 std::string id = userId + "-" + appId + "-" + storeId;
242 if (instanceId != 0) {
243 id += "-" + std::to_string(instanceId);
244 }
245 if (!subUser.empty()) {
246 id += "-" + subUser;
247 }
248 return id;
249 }
250
GenerateDualTupleIdentifierId(const std::string & storeId,const std::string & appId)251 std::string DBCommon::GenerateDualTupleIdentifierId(const std::string &storeId, const std::string &appId)
252 {
253 return appId + "-" + storeId;
254 }
255
SetDatabaseIds(KvDBProperties & properties,const DbIdParam & dbIdParam)256 void DBCommon::SetDatabaseIds(KvDBProperties &properties, const DbIdParam &dbIdParam)
257 {
258 properties.SetIdentifier(dbIdParam.userId, dbIdParam.appId, dbIdParam.storeId,
259 dbIdParam.subUser, dbIdParam.instanceId);
260 std::string oriStoreDir;
261 // IDENTIFIER_DIR no need cal with instanceId and subUser
262 std::string identifier = GenerateIdentifierId(dbIdParam.storeId, dbIdParam.appId, dbIdParam.userId);
263 if (properties.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false)) {
264 oriStoreDir = dbIdParam.storeId;
265 } else {
266 oriStoreDir = identifier;
267 }
268 std::string hashIdentifier = TransferHashString(identifier);
269 std::string hashDir = TransferHashString(oriStoreDir);
270 std::string hexHashDir = TransferStringToHex(hashDir);
271 properties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, hexHashDir);
272 }
273
StringMasking(const std::string & oriStr,size_t remain)274 std::string DBCommon::StringMasking(const std::string &oriStr, size_t remain)
275 {
276 #ifndef DB_DEBUG_ENV
277 if (oriStr.size() > remain) {
278 return oriStr.substr(0, remain);
279 }
280 #endif
281 return oriStr;
282 }
283
GetDistributedTableName(const std::string & device,const std::string & tableName)284 std::string DBCommon::GetDistributedTableName(const std::string &device, const std::string &tableName)
285 {
286 if (!RuntimeContext::GetInstance()->ExistTranslateDevIdCallback()) {
287 return GetDistributedTableNameWithHash(device, tableName);
288 }
289 return CalDistributedTableName(device, tableName);
290 }
291
GetDistributedTableName(const std::string & device,const std::string & tableName,const StoreInfo & info)292 std::string DBCommon::GetDistributedTableName(const std::string &device, const std::string &tableName,
293 const StoreInfo &info)
294 {
295 std::string newDeviceId;
296 if (RuntimeContext::GetInstance()->TranslateDeviceId(device, info, newDeviceId) != E_OK) {
297 return GetDistributedTableNameWithHash(device, tableName);
298 }
299 return CalDistributedTableName(newDeviceId, tableName);
300 }
301
GetDistributedTableNameWithHash(const std::string & device,const std::string & tableName)302 std::string DBCommon::GetDistributedTableNameWithHash(const std::string &device, const std::string &tableName)
303 {
304 std::string deviceHashHex = DBCommon::TransferStringToHex(DBCommon::TransferHashString(device));
305 return CalDistributedTableName(deviceHashHex, tableName);
306 }
307
CalDistributedTableName(const std::string & device,const std::string & tableName)308 std::string DBCommon::CalDistributedTableName(const std::string &device, const std::string &tableName)
309 {
310 return DBConstant::RELATIONAL_PREFIX + tableName + "_" + device;
311 }
312
GetDeviceFromName(const std::string & deviceTableName,std::string & deviceHash,std::string & tableName)313 void DBCommon::GetDeviceFromName(const std::string &deviceTableName, std::string &deviceHash, std::string &tableName)
314 {
315 std::size_t found = deviceTableName.rfind('_');
316 if (found != std::string::npos && found + 1 < deviceTableName.length() &&
317 found > DBConstant::RELATIONAL_PREFIX_SIZE) {
318 deviceHash = deviceTableName.substr(found + 1);
319 tableName = deviceTableName.substr(DBConstant::RELATIONAL_PREFIX_SIZE,
320 found - DBConstant::RELATIONAL_PREFIX_SIZE);
321 }
322 }
323
IsSameCipher(CipherType srcType,CipherType inputType)324 bool DBCommon::IsSameCipher(CipherType srcType, CipherType inputType)
325 {
326 // At present, the default type is AES-256-GCM.
327 // So when src is default and input is AES-256-GCM,
328 // or when src is AES-256-GCM and input is default,
329 // we think they are the same type.
330 if (srcType == inputType ||
331 ((srcType == CipherType::DEFAULT || srcType == CipherType::AES_256_GCM) &&
332 (inputType == CipherType::DEFAULT || inputType == CipherType::AES_256_GCM))) {
333 return true;
334 }
335 return false;
336 }
337
CheckQueryWithoutMultiTable(const Query & query)338 bool DBCommon::CheckQueryWithoutMultiTable(const Query &query)
339 {
340 if (!QuerySyncObject::GetQuerySyncObject(query).empty()) {
341 LOGE("check query object from table failed!");
342 return false;
343 }
344 return true;
345 }
346
347 /* this function us topology sorting algorithm to detect whether a ring exists in the dependency
348 * the algorithm main procedure as below:
349 * 1. select a point which in-degree is 0 in the graph and record it;
350 * 2. delete the point and all edges starting from this point;
351 * 3. repeat step 1 and 2, until the graph is empty or there is no point with a zero degree
352 * */
IsCircularDependency(int size,const std::vector<std::vector<int>> & dependency)353 bool DBCommon::IsCircularDependency(int size, const std::vector<std::vector<int>> &dependency)
354 {
355 std::vector<int> inDegree(size, 0); // save in-degree of every point
356 std::vector<std::vector<int>> adjacencyList(size);
357 for (size_t i = 0; i < dependency.size(); i++) {
358 adjacencyList[dependency[i][0]].push_back(dependency[i][1]); // update adjacencyList
359 inDegree[dependency[i][1]]++;
360 }
361 std::queue<int> que;
362 for (size_t i = 0; i < inDegree.size(); i++) {
363 if (inDegree[i] == 0) {
364 que.push(i); // push all point which in-degree = 0
365 }
366 }
367
368 int zeroDegreeCnt = static_cast<int>(que.size());
369 while (!que.empty()) {
370 int index = que.front();
371 que.pop();
372 for (size_t i = 0; i < adjacencyList[index].size(); ++i) {
373 int j = adjacencyList[index][i]; // adjacencyList[index] save the point which is connected to index
374 inDegree[j]--;
375 if (inDegree[j] == 0) {
376 zeroDegreeCnt++;
377 que.push(j);
378 }
379 }
380 }
381 return zeroDegreeCnt != size;
382 }
383
SerializeWaterMark(Timestamp localMark,const std::string & cloudMark,Value & blobMeta)384 int DBCommon::SerializeWaterMark(Timestamp localMark, const std::string &cloudMark, Value &blobMeta)
385 {
386 uint64_t length = Parcel::GetUInt64Len() + Parcel::GetStringLen(cloudMark);
387 blobMeta.resize(length);
388 Parcel parcel(blobMeta.data(), blobMeta.size());
389 parcel.WriteUInt64(localMark);
390 parcel.WriteString(cloudMark);
391 if (parcel.IsError()) {
392 LOGE("[DBCommon] Parcel error while serializing cloud meta data.");
393 return -E_PARSE_FAIL;
394 }
395 return E_OK;
396 }
397
GetPrefixTableName(const TableName & tableName)398 Key DBCommon::GetPrefixTableName(const TableName &tableName)
399 {
400 TableName newName = CloudDbConstant::CLOUD_META_TABLE_PREFIX + tableName;
401 Key prefixedTableName(newName.begin(), newName.end());
402 return prefixedTableName;
403 }
404
InsertNodesByScore(const std::map<std::string,std::map<std::string,bool>> & graph,const std::vector<std::string> & generateNodes,const std::map<std::string,int> & scoreGraph,std::list<std::string> & insertTarget)405 void DBCommon::InsertNodesByScore(const std::map<std::string, std::map<std::string, bool>> &graph,
406 const std::vector<std::string> &generateNodes, const std::map<std::string, int> &scoreGraph,
407 std::list<std::string> &insertTarget)
408 {
409 auto copyGraph = graph;
410 // insert all nodes into res
411 for (const auto &generateNode : generateNodes) {
412 auto iterator = insertTarget.begin();
413 for (; iterator != insertTarget.end(); iterator++) {
414 // don't compare two no reachable node
415 if (!copyGraph[*iterator][generateNode] && !copyGraph[generateNode][*iterator]) {
416 continue;
417 }
418 if (scoreGraph.find(*iterator) == scoreGraph.end() || scoreGraph.find(generateNode) == scoreGraph.end()) {
419 // should not happen
420 LOGW("[DBCommon] not find score in graph");
421 continue;
422 }
423 if (scoreGraph.at(*iterator) <= scoreGraph.at(generateNode)) {
424 break;
425 }
426 }
427 insertTarget.insert(iterator, generateNode);
428 }
429 }
430
GenerateNodesByNodeWeight(const std::vector<std::string> & nodes,const std::map<std::string,std::map<std::string,bool>> & graph,const std::map<std::string,int> & nodeWeight)431 std::list<std::string> DBCommon::GenerateNodesByNodeWeight(const std::vector<std::string> &nodes,
432 const std::map<std::string, std::map<std::string, bool>> &graph,
433 const std::map<std::string, int> &nodeWeight)
434 {
435 std::list<std::string> res;
436 std::set<std::string> paramNodes;
437 std::set<std::string> visitNodes;
438 for (const auto &node : nodes) {
439 res.push_back(node);
440 paramNodes.insert(node);
441 visitNodes.insert(node);
442 }
443 // find all node which can be reached by param nodes
444 for (const auto &source : paramNodes) {
445 if (graph.find(source) == graph.end()) {
446 continue;
447 }
448 for (const auto &[target, reach] : graph.at(source)) {
449 if (reach) {
450 visitNodes.insert(target);
451 }
452 }
453 }
454 std::vector<std::string> generateNodes;
455 for (const auto &node : visitNodes) {
456 // ignore the node which is param
457 if (paramNodes.find(node) == paramNodes.end()) {
458 generateNodes.push_back(node);
459 }
460 }
461 InsertNodesByScore(graph, generateNodes, nodeWeight, res);
462 return res;
463 }
464
HasPrimaryKey(const std::vector<Field> & fields)465 bool DBCommon::HasPrimaryKey(const std::vector<Field> &fields)
466 {
467 for (const auto &field : fields) {
468 if (field.primary) {
469 return true;
470 }
471 }
472 return false;
473 }
474
IsRecordError(const VBucket & record)475 bool DBCommon::IsRecordError(const VBucket &record)
476 {
477 // check record err should deal or skip, false is no error or error is considered, true is error not considered
478 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
479 return false;
480 }
481 if (record.at(CloudDbConstant::ERROR_FIELD).index() != TYPE_INDEX<int64_t>) {
482 return false;
483 }
484 auto status = std::get<int64_t>(record.at(CloudDbConstant::ERROR_FIELD));
485 return status != static_cast<int64_t>(DBStatus::CLOUD_RECORD_EXIST_CONFLICT) &&
486 status != static_cast<int64_t>(DBStatus::CLOUD_RECORD_ALREADY_EXISTED) &&
487 status != static_cast<int64_t>(DBStatus::CLOUD_RECORD_NOT_FOUND) &&
488 status != static_cast<int64_t>(DBStatus::LOCAL_ASSET_NOT_FOUND);
489 }
490
IsIntTypeRecordError(const VBucket & record)491 bool DBCommon::IsIntTypeRecordError(const VBucket &record)
492 {
493 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
494 return false;
495 }
496 return record.at(CloudDbConstant::ERROR_FIELD).index() == TYPE_INDEX<int64_t>;
497 }
498
IsRecordIgnored(const VBucket & record)499 bool DBCommon::IsRecordIgnored(const VBucket &record)
500 {
501 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
502 return false;
503 }
504 if (record.at(CloudDbConstant::ERROR_FIELD).index() != TYPE_INDEX<int64_t>) {
505 return false;
506 }
507 auto status = std::get<int64_t>(record.at(CloudDbConstant::ERROR_FIELD));
508 return status == static_cast<int64_t>(DBStatus::CLOUD_RECORD_EXIST_CONFLICT) ||
509 status == static_cast<int64_t>(DBStatus::CLOUD_VERSION_CONFLICT);
510 }
511
IsRecordFailed(const VBucket & record,DBStatus status)512 bool DBCommon::IsRecordFailed(const VBucket &record, DBStatus status)
513 {
514 if (status == OK) {
515 return false;
516 }
517 return DBCommon::IsRecordError(record) || !DBCommon::IsRecordSuccess(record);
518 }
519
IsRecordVersionConflict(const VBucket & record)520 bool DBCommon::IsRecordVersionConflict(const VBucket &record)
521 {
522 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
523 return false;
524 }
525 if (record.at(CloudDbConstant::ERROR_FIELD).index() != TYPE_INDEX<int64_t>) {
526 return false;
527 }
528 auto status = std::get<int64_t>(record.at(CloudDbConstant::ERROR_FIELD));
529 return status == static_cast<int64_t>(DBStatus::CLOUD_VERSION_CONFLICT);
530 }
531
IsRecordAssetsMissing(const VBucket & record)532 bool DBCommon::IsRecordAssetsMissing(const VBucket &record)
533 {
534 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
535 return false;
536 }
537 if (record.at(CloudDbConstant::ERROR_FIELD).index() != TYPE_INDEX<int64_t>) {
538 return false;
539 }
540 auto status = std::get<int64_t>(record.at(CloudDbConstant::ERROR_FIELD));
541 return status == static_cast<int64_t>(DBStatus::LOCAL_ASSET_NOT_FOUND);
542 }
543
IsRecordDelete(const VBucket & record)544 bool DBCommon::IsRecordDelete(const VBucket &record)
545 {
546 if (record.find(CloudDbConstant::DELETE_FIELD) == record.end()) {
547 return false;
548 }
549 if (record.at(CloudDbConstant::DELETE_FIELD).index() != TYPE_INDEX<bool>) {
550 return false;
551 }
552 return std::get<bool>(record.at(CloudDbConstant::DELETE_FIELD));
553 }
554
IsCloudRecordNotFound(const VBucket & record)555 bool DBCommon::IsCloudRecordNotFound(const VBucket &record)
556 {
557 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
558 return false;
559 }
560 if (record.at(CloudDbConstant::ERROR_FIELD).index() != TYPE_INDEX<int64_t>) {
561 return false;
562 }
563 auto status = std::get<int64_t>(record.at(CloudDbConstant::ERROR_FIELD));
564 return status == static_cast<int64_t>(DBStatus::CLOUD_RECORD_NOT_FOUND);
565 }
566
IsCloudRecordAlreadyExisted(const VBucket & record)567 bool DBCommon::IsCloudRecordAlreadyExisted(const VBucket &record)
568 {
569 if (record.find(CloudDbConstant::ERROR_FIELD) == record.end()) {
570 return false;
571 }
572 if (record.at(CloudDbConstant::ERROR_FIELD).index() != TYPE_INDEX<int64_t>) {
573 return false;
574 }
575 auto status = std::get<int64_t>(record.at(CloudDbConstant::ERROR_FIELD));
576 return status == static_cast<int64_t>(DBStatus::CLOUD_RECORD_ALREADY_EXISTED);
577 }
578
IsNeedCompensatedForUpload(const VBucket & uploadExtend,const CloudWaterType & type)579 bool DBCommon::IsNeedCompensatedForUpload(const VBucket &uploadExtend, const CloudWaterType &type)
580 {
581 return (DBCommon::IsCloudRecordAlreadyExisted(uploadExtend) && type == CloudWaterType::INSERT) ||
582 (DBCommon::IsCloudRecordNotFound(uploadExtend) && type == CloudWaterType::UPDATE);
583 }
584
IsRecordIgnoredForReliability(const VBucket & uploadExtend,const CloudWaterType & type)585 bool DBCommon::IsRecordIgnoredForReliability(const VBucket &uploadExtend, const CloudWaterType &type)
586 {
587 return (DBCommon::IsCloudRecordAlreadyExisted(uploadExtend) && type == CloudWaterType::INSERT) ||
588 (DBCommon::IsCloudRecordNotFound(uploadExtend) &&
589 (type == CloudWaterType::UPDATE || type == CloudWaterType::DELETE));
590 }
591
IsRecordSuccess(const VBucket & record)592 bool DBCommon::IsRecordSuccess(const VBucket &record)
593 {
594 return record.find(CloudDbConstant::ERROR_FIELD) == record.end();
595 }
596
GenerateHashLabel(const DBInfo & dbInfo)597 std::string DBCommon::GenerateHashLabel(const DBInfo &dbInfo)
598 {
599 if (dbInfo.syncDualTupleMode) {
600 return DBCommon::TransferHashString(dbInfo.appId + "-" + dbInfo.storeId);
601 }
602 return DBCommon::TransferHashString(dbInfo.userId + "-" + dbInfo.appId + "-" + dbInfo.storeId);
603 }
604
EraseBit(uint64_t origin,uint64_t eraseBit)605 uint64_t DBCommon::EraseBit(uint64_t origin, uint64_t eraseBit)
606 {
607 return origin & (~eraseBit);
608 }
609
LoadGrdLib(void)610 void *DBCommon::LoadGrdLib(void)
611 {
612 #ifndef _WIN32
613 auto handle = dlopen("libarkdata_db_core.z.so", RTLD_LAZY);
614 if (handle == nullptr) {
615 LOGW("[DBCommon] unable to load grd lib, errno: %d, %s", errno, dlerror());
616 return nullptr;
617 }
618 return handle;
619 #else
620 return nullptr;
621 #endif
622 }
623
UnLoadGrdLib(void * handle)624 void DBCommon::UnLoadGrdLib(void *handle)
625 {
626 #ifndef _WIN32
627 if (handle == nullptr) {
628 return;
629 }
630 dlclose(handle);
631 #endif
632 }
633
IsGrdLibLoaded(void)634 bool DBCommon::IsGrdLibLoaded(void)
635 {
636 return g_isGrdLoaded;
637 }
638
CheckCloudSyncConfigValid(const CloudSyncConfig & config)639 bool DBCommon::CheckCloudSyncConfigValid(const CloudSyncConfig &config)
640 {
641 if (config.maxUploadCount < CloudDbConstant::MIN_UPLOAD_BATCH_COUNT ||
642 config.maxUploadCount > CloudDbConstant::MAX_UPLOAD_BATCH_COUNT) {
643 LOGE("[DBCommon] invalid upload count %" PRId32, config.maxUploadCount);
644 return false;
645 }
646 if (config.maxUploadSize < CloudDbConstant::MIN_UPLOAD_SIZE ||
647 config.maxUploadSize > CloudDbConstant::MAX_UPLOAD_SIZE) {
648 LOGE("[DBCommon] invalid upload size %" PRId32, config.maxUploadSize);
649 return false;
650 }
651 if (config.maxRetryConflictTimes < CloudDbConstant::MIN_RETRY_CONFLICT_COUNTS) {
652 LOGE("[DBCommon] invalid retry conflict count %" PRId32, config.maxRetryConflictTimes);
653 return false;
654 }
655 return true;
656 }
657
ConvertToUInt64(const std::string & str,uint64_t & value)658 bool DBCommon::ConvertToUInt64(const std::string &str, uint64_t &value)
659 {
660 auto [ptr, errCode] = std::from_chars(str.data(), str.data() + str.size(), value);
661 return errCode == std::errc{} && ptr == str.data() + str.size();
662 }
663
CmpModifyTime(const std::string & preModifyTimeStr,const std::string & curModifyTimeStr)664 bool CmpModifyTime(const std::string &preModifyTimeStr, const std::string &curModifyTimeStr)
665 {
666 uint64_t curModifyTime = 0;
667 uint64_t preModifyTime = 0;
668 if (preModifyTimeStr.empty() || !DBCommon::ConvertToUInt64(preModifyTimeStr, preModifyTime)) {
669 return true;
670 }
671 if (curModifyTimeStr.empty() || !DBCommon::ConvertToUInt64(curModifyTimeStr, curModifyTime)) {
672 return false;
673 }
674 return curModifyTime >= preModifyTime;
675 }
676
RemoveDuplicateAssetsData(std::vector<Asset> & assets)677 void DBCommon::RemoveDuplicateAssetsData(std::vector<Asset> &assets)
678 {
679 std::unordered_map<std::string, size_t> indexMap;
680 size_t vectorSize = assets.size();
681 std::vector<size_t> arr(vectorSize, 0);
682 for (std::vector<DistributedDB::Asset>::size_type i = 0; i < assets.size(); ++i) {
683 DistributedDB::Asset asset = assets.at(i);
684 auto it = indexMap.find(asset.name);
685 if (it == indexMap.end()) {
686 indexMap[asset.name] = i;
687 continue;
688 }
689 size_t prevIndex = it->second;
690 Asset &prevAsset = assets.at(prevIndex);
691 if (prevAsset.assetId.empty() && !asset.assetId.empty()) {
692 arr[prevIndex] = 1;
693 indexMap[asset.name] = i;
694 continue;
695 }
696 if (!prevAsset.assetId.empty() && asset.assetId.empty()) {
697 arr[i] = 1;
698 indexMap[asset.name] = prevIndex;
699 continue;
700 }
701 if (CmpModifyTime(prevAsset.modifyTime, asset.modifyTime)) {
702 arr[prevIndex] = 1;
703 indexMap[asset.name] = i;
704 continue;
705 }
706 arr[i] = 1;
707 indexMap[asset.name] = prevIndex;
708 }
709 indexMap.clear();
710 size_t arrIndex = 0;
711 for (auto it = assets.begin(); it != assets.end();) {
712 if (arr[arrIndex] == 1) {
713 it = assets.erase(it);
714 } else {
715 it++;
716 }
717 arrIndex++;
718 }
719 }
720
TransformToCaseInsensitive(const std::vector<std::string> & origin)721 std::set<std::string, CaseInsensitiveComparator> DBCommon::TransformToCaseInsensitive(
722 const std::vector<std::string> &origin)
723 {
724 std::set<std::string, CaseInsensitiveComparator> res;
725 for (const auto &item : origin) {
726 res.insert(item);
727 }
728 return res;
729 }
730
GetStoreIdentifier(const StoreInfo & info,const std::string & subUser,bool syncDualTupleMode,bool allowStoreIdWithDot)731 std::string DBCommon::GetStoreIdentifier(const StoreInfo &info, const std::string &subUser, bool syncDualTupleMode,
732 bool allowStoreIdWithDot)
733 {
734 if (!ParamCheckUtils::CheckStoreParameter(info, syncDualTupleMode, subUser, allowStoreIdWithDot)) {
735 return "";
736 }
737 if (syncDualTupleMode) {
738 return DBCommon::TransferHashString(info.appId + "-" + info.storeId);
739 }
740 if (subUser.empty()) {
741 return DBCommon::TransferHashString(info.userId + "-" + info.appId + "-" + info.storeId);
742 }
743 return DBCommon::TransferHashString(info.userId + "-" + info.appId + "-" + info.storeId + "-" + subUser);
744 }
745
TransfDbVersionToSoftwareVersion(uint16_t dbVersion)746 uint32_t DBCommon::TransfDbVersionToSoftwareVersion(uint16_t dbVersion)
747 {
748 return SOFTWARE_VERSION_EARLIEST + dbVersion;
749 }
750 } // namespace DistributedDB
751