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 #ifndef OMIT_MULTI_VER
17 #include "multi_ver_natural_store_commit_storage.h"
18
19 #include <stack>
20
21 #include "db_errno.h"
22 #include "db_constant.h"
23 #include "log_print.h"
24 #include "multi_ver_commit.h"
25 #include "ikvdb_factory.h"
26 #include "parcel.h"
27 #include "db_common.h"
28 #include "sqlite_local_kvdb.h"
29 #include "kvdb_utils.h"
30
31 namespace DistributedDB {
32 using std::string;
33 using std::vector;
34 using std::list;
35 using std::map;
36 using std::make_pair;
37 using std::stack;
38
39 namespace {
40 const size_t MAX_COMMIT_ST_LENGTH = 4096;
41 const Version VERSION_MAX = 0xFFFFFFFFFFFFFFFF;
42 const string MULTI_VER_COMMIT_DB_NAME = "commit_logs.db";
43 }
44
45 const string MultiVerNaturalStoreCommitStorage::HEADER_KEY = "header commit";
46
MultiVerNaturalStoreCommitStorage()47 MultiVerNaturalStoreCommitStorage::MultiVerNaturalStoreCommitStorage()
48 : commitStorageDatabase_(nullptr),
49 commitStorageDBConnection_(nullptr)
50 {}
51
~MultiVerNaturalStoreCommitStorage()52 MultiVerNaturalStoreCommitStorage::~MultiVerNaturalStoreCommitStorage()
53 {
54 Close();
55 }
56
CheckVersion(const Property & property,bool & isDbExist) const57 int MultiVerNaturalStoreCommitStorage::CheckVersion(const Property &property, bool &isDbExist) const
58 {
59 int dbVer = 0;
60 int errCode = GetVersion(property, dbVer, isDbExist);
61 if (errCode != E_OK) {
62 LOGE("[CommitStorage][CheckVer] GetVersion failed, errCode=%d.", errCode);
63 return errCode;
64 }
65 if (!isDbExist) {
66 return E_OK;
67 }
68 LOGD("[CommitStorage][CheckVer] DbVersion=%d, CurVersion=%d.", dbVer, MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT);
69 if (dbVer > MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT) {
70 LOGE("[CommitStorage][CheckVer] Version Not Support!");
71 return -E_VERSION_NOT_SUPPORT;
72 }
73 return E_OK;
74 }
75
GetVersion(const IKvDBCommitStorage::Property & property,int & version,bool & isDbExisted)76 int MultiVerNaturalStoreCommitStorage::GetVersion(const IKvDBCommitStorage::Property &property,
77 int &version, bool &isDbExisted)
78 {
79 SQLiteLocalKvDB *localKvdb = new (std::nothrow) SQLiteLocalKvDB();
80 if (localKvdb == nullptr) {
81 return -E_INVALID_DB;
82 }
83
84 KvDBProperties dbProperties;
85 dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate);
86 dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
87 dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
88 dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
89 dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
90 dbProperties.SetPassword(property.cipherType, property.passwd);
91
92 int errCode = localKvdb->GetVersion(dbProperties, version, isDbExisted);
93 RefObject::DecObjRef(localKvdb);
94 localKvdb = nullptr;
95 return errCode;
96 }
97
Open(const IKvDBCommitStorage::Property & property)98 int MultiVerNaturalStoreCommitStorage::Open(const IKvDBCommitStorage::Property &property)
99 {
100 if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) {
101 return E_OK;
102 }
103 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
104 if (factory == nullptr) {
105 LOGE("Failed to open IKvDB! Get factory failed.");
106 return -E_INVALID_DB;
107 }
108 int errCode = E_OK;
109 commitStorageDatabase_ = factory->CreateCommitStorageDB(errCode);
110 if (commitStorageDatabase_ == nullptr) {
111 LOGE("Failed to create commit storage database:%d", errCode);
112 return -E_INVALID_DB;
113 }
114
115 KvDBProperties dbProperties;
116 dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, property.isNeedCreate);
117 dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
118 dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
119 dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
120 dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
121 dbProperties.SetPassword(property.cipherType, property.passwd);
122
123 errCode = commitStorageDatabase_->Open(dbProperties);
124 if (errCode != E_OK) {
125 LOGE("Failed to open commit storage database! err:%d", errCode);
126 RefObject::KillAndDecObjRef(commitStorageDatabase_);
127 commitStorageDatabase_ = nullptr;
128 return errCode;
129 }
130 commitStorageDBConnection_ = commitStorageDatabase_->GetDBConnection(errCode);
131 if (commitStorageDBConnection_ == nullptr) {
132 LOGE("Failed to get connection for commit storage! err:%d", errCode);
133 RefObject::KillAndDecObjRef(commitStorageDatabase_);
134 commitStorageDatabase_ = nullptr;
135 return errCode;
136 }
137 // Need to refactor in the future
138 errCode = static_cast<SQLiteLocalKvDB *>(commitStorageDatabase_)->SetVersion(dbProperties,
139 MULTI_VER_COMMIT_STORAGE_VERSION_CURRENT);
140 if (errCode != E_OK) {
141 LOGE("[CommitStorage][Open] SetVersion fail, errCode=%d.", errCode);
142 Close();
143 return errCode;
144 }
145 return E_OK;
146 }
147
Close()148 void MultiVerNaturalStoreCommitStorage::Close()
149 {
150 if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) {
151 commitStorageDBConnection_->Close();
152 commitStorageDBConnection_ = nullptr;
153 }
154 if (commitStorageDatabase_ != nullptr) {
155 IKvDB::DecObjRef(commitStorageDatabase_);
156 commitStorageDatabase_ = nullptr;
157 }
158 }
159
Remove(const IKvDBCommitStorage::Property & property)160 int MultiVerNaturalStoreCommitStorage::Remove(const IKvDBCommitStorage::Property &property)
161 {
162 if (commitStorageDatabase_ != nullptr && commitStorageDBConnection_ != nullptr) {
163 commitStorageDBConnection_->Close();
164 commitStorageDBConnection_ = nullptr;
165 RefObject::DecObjRef(commitStorageDatabase_);
166 commitStorageDatabase_ = nullptr;
167 }
168
169 std::string dataDir = property.path + ("/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/");
170 int errCode = KvDBUtils::RemoveKvDB(dataDir, DBConstant::MULTI_VER_COMMIT_STORE);
171 if (errCode != E_OK) {
172 LOGE("Failed to remove commit storage database! err:%d", errCode);
173 return errCode;
174 }
175 return E_OK;
176 }
177
AllocCommit(int & errCode) const178 IKvDBCommit *MultiVerNaturalStoreCommitStorage::AllocCommit(int &errCode) const
179 {
180 auto commit = new (std::nothrow) MultiVerCommit();
181 if (commit != nullptr) {
182 errCode = E_OK;
183 } else {
184 errCode = -E_OUT_OF_MEMORY;
185 LOGE("Failed to alloc commit! Bad alloc.");
186 }
187 return commit;
188 }
189
GetCommit(const CommitID & commitId,int & errCode) const190 IKvDBCommit *MultiVerNaturalStoreCommitStorage::GetCommit(const CommitID &commitId, int &errCode) const
191 {
192 if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
193 LOGE("Failed to get commit! Commit storage do not open.");
194 errCode = -E_INVALID_DB;
195 return nullptr;
196 }
197 Key key;
198 TransferCommitIDToKey(commitId, key);
199 IOption option;
200 Value value;
201 errCode = commitStorageDBConnection_->Get(option, key, value);
202 if (errCode != E_OK) {
203 if (errCode != -E_NOT_FOUND) {
204 LOGE("Failed to get the commit:%d", errCode);
205 }
206 return nullptr;
207 }
208
209 IKvDBCommit *commit = AllocCommit(errCode);
210 if (commit == nullptr) {
211 return nullptr;
212 }
213
214 errCode = TransferValueToCommit(value, *commit);
215 if (errCode != E_OK) {
216 delete commit;
217 commit = nullptr;
218 }
219 return commit;
220 }
221
StartVacuum()222 int MultiVerNaturalStoreCommitStorage::StartVacuum()
223 {
224 if (commitStorageDBConnection_ == nullptr) {
225 LOGE("commitStorage Connection not existed!");
226 return -E_INVALID_CONNECTION;
227 }
228 return commitStorageDBConnection_->StartTransaction();
229 }
230
CancelVacuum()231 int MultiVerNaturalStoreCommitStorage::CancelVacuum()
232 {
233 if (commitStorageDBConnection_ == nullptr) {
234 LOGE("commitStorage Connection not existed!");
235 return -E_INVALID_CONNECTION;
236 }
237 return commitStorageDBConnection_->RollBack();
238 }
239
FinishVacuum()240 int MultiVerNaturalStoreCommitStorage::FinishVacuum()
241 {
242 if (commitStorageDBConnection_ == nullptr) {
243 LOGE("commitStorage Connection not existed!");
244 return -E_INVALID_CONNECTION;
245 }
246 return commitStorageDBConnection_->Commit();
247 }
248
GetAllCommitsInTree(std::list<MultiVerCommitNode> & commits) const249 int MultiVerNaturalStoreCommitStorage::GetAllCommitsInTree(std::list<MultiVerCommitNode> &commits) const
250 {
251 std::map<CommitID, IKvDBCommit *> commitsTable;
252 CommitID headerId;
253 int errCode = GetAllCommits(commitsTable, headerId);
254 if (errCode != E_OK || commitsTable.empty()) { // error or no commit.
255 return errCode;
256 }
257
258 std::stack<CommitID> commitStack;
259 commitStack.push(headerId);
260 while (!commitStack.empty()) {
261 auto currentCommitIter = commitsTable.find(commitStack.top());
262 if (currentCommitIter == commitsTable.end()) {
263 // not found the node in the commit tree.
264 commits.clear();
265 errCode = -E_UNEXPECTED_DATA;
266 break;
267 }
268
269 commitStack.pop();
270 if (currentCommitIter->second == nullptr) {
271 // if the node has been released or traveled.
272 continue;
273 }
274
275 AddParentsToStack(currentCommitIter->second, commitsTable, commitStack);
276 MultiVerCommitNode commitNode;
277 // Get the current commit info
278 commitNode.commitId = currentCommitIter->first;
279 commitNode.deviceInfo = currentCommitIter->second->GetDeviceInfo();
280 commitNode.isLocal = (currentCommitIter->second->GetLocalFlag() ?
281 MultiVerCommitNode::LOCAL_FLAG : MultiVerCommitNode::NON_LOCAL_FLAG);
282 commitNode.leftParent = currentCommitIter->second->GetLeftParentId();
283 commitNode.rightParent = currentCommitIter->second->GetRightParentId();
284 commitNode.timestamp = currentCommitIter->second->GetTimestamp();
285 commitNode.version = currentCommitIter->second->GetCommitVersion();
286 commits.push_back(commitNode);
287
288 ReleaseCommit(currentCommitIter->second);
289 currentCommitIter->second = nullptr; // has been traveled, set to nullptr.
290 }
291
292 commits.sort([] (const MultiVerCommitNode &thisNode, const MultiVerCommitNode &thatNode) {
293 return (thisNode.version > thatNode.version);
294 });
295 ReleaseUnusedCommits(commitsTable);
296
297 return errCode;
298 }
299
AddCommit(const IKvDBCommit & commitEntry,bool isHeader)300 int MultiVerNaturalStoreCommitStorage::AddCommit(const IKvDBCommit &commitEntry, bool isHeader)
301 {
302 int errCode = CheckAddedCommit(commitEntry);
303 if (errCode != E_OK) {
304 return errCode;
305 }
306
307 Key key;
308 TransferCommitIDToKey(commitEntry.GetCommitId(), key);
309 Value value;
310 errCode = TransferCommitToValue(commitEntry, value);
311 if (errCode != E_OK) {
312 return errCode;
313 }
314 IOption option;
315 errCode = commitStorageDBConnection_->StartTransaction();
316 if (errCode != E_OK) {
317 return errCode;
318 }
319
320 errCode = commitStorageDBConnection_->Put(option, key, value);
321 if (errCode != E_OK) {
322 goto END;
323 }
324
325 if (isHeader) {
326 errCode = SetHeaderInner(commitEntry.GetCommitId());
327 }
328 END:
329 if (errCode != E_OK) {
330 commitStorageDBConnection_->RollBack();
331 } else {
332 errCode = commitStorageDBConnection_->Commit();
333 }
334
335 return errCode;
336 }
337
RemoveCommit(const CommitID & commitId)338 int MultiVerNaturalStoreCommitStorage::RemoveCommit(const CommitID &commitId)
339 {
340 if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
341 LOGE("Failed to get commit! Commit storage do not open.");
342 return -E_INVALID_DB;
343 }
344 int errCode = commitStorageDBConnection_->StartTransaction();
345 if (errCode != E_OK) {
346 LOGE("Failed to remove commit when start transaction! err:%d", errCode);
347 return errCode;
348 }
349 Key key;
350 IOption option;
351 CommitID header = GetHeader(errCode);
352 if (header == commitId) {
353 IKvDBCommit *commit = GetCommit(commitId, errCode);
354 if (commit == nullptr) {
355 LOGE("Failed to remove commit when get header commit! err:%d", errCode);
356 goto ERROR;
357 }
358 errCode = SetHeader(commit->GetLeftParentId());
359 ReleaseCommit(commit);
360 commit = nullptr;
361 if (errCode != E_OK) {
362 LOGE("Failed to remove commit when set header commit! err:%d", errCode);
363 goto ERROR;
364 }
365 } else {
366 LOGE("Failed to remove commit! The commit is not the header.");
367 errCode = -E_UNEXPECTED_DATA;
368 goto ERROR;
369 }
370 TransferCommitIDToKey(commitId, key);
371 errCode = commitStorageDBConnection_->Delete(option, key);
372 if (errCode != E_OK) {
373 LOGI("Failed to remove commit when remove commit! err:%d", errCode);
374 goto ERROR;
375 }
376 errCode = commitStorageDBConnection_->Commit();
377 if (errCode != E_OK) {
378 LOGE("Failed to remove commit when commit! err:%d", errCode);
379 goto ERROR;
380 }
381 return E_OK;
382 ERROR:
383 (void)commitStorageDBConnection_->RollBack();
384 return errCode;
385 }
386
ReleaseCommit(const IKvDBCommit * commit) const387 void MultiVerNaturalStoreCommitStorage::ReleaseCommit(const IKvDBCommit *commit) const
388 {
389 if (commit != nullptr) {
390 delete commit;
391 commit = nullptr;
392 }
393 }
394
SetHeader(const CommitID & commitId)395 int MultiVerNaturalStoreCommitStorage::SetHeader(const CommitID &commitId)
396 {
397 if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
398 LOGE("Failed to get commit! Commit storage do not open.");
399 return -E_INVALID_DB;
400 }
401
402 if (commitId.size() != 0) {
403 int errCode = E_OK;
404 if (!CommitExist(commitId, errCode)) {
405 LOGE("Failed to set header! The commit does not exist.");
406 return errCode;
407 }
408 }
409
410 return SetHeaderInner(commitId);
411 }
412
GetHeader(int & errCode) const413 CommitID MultiVerNaturalStoreCommitStorage::GetHeader(int &errCode) const
414 {
415 CommitID headerCommitID;
416 if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
417 LOGE("Failed to get commit for uninitialized store");
418 errCode = -E_INVALID_DB;
419 return headerCommitID;
420 }
421 Key key;
422 TransferStringToKey(HEADER_KEY, key);
423 IOption option;
424 Value value;
425 errCode = commitStorageDBConnection_->Get(option, key, value);
426 if (errCode != E_OK) {
427 if (errCode == -E_NOT_FOUND) { // not find the header, means no header.
428 LOGI("Not find the header.");
429 errCode = E_OK;
430 } else {
431 LOGE("Get the commit header failed:%d", errCode);
432 return headerCommitID;
433 }
434 }
435 TransferValueToCommitID(value, headerCommitID);
436 return headerCommitID;
437 }
438
CommitExist(const CommitID & commitId,int & errCode) const439 bool MultiVerNaturalStoreCommitStorage::CommitExist(const CommitID &commitId, int &errCode) const
440 {
441 IKvDBCommit *commit = GetCommit(commitId, errCode);
442 if (commit == nullptr) {
443 return false;
444 } else {
445 ReleaseCommit(commit);
446 commit = nullptr;
447 return true;
448 }
449 }
450
ReleaseUnusedCommits(std::map<CommitID,IKvDBCommit * > & commits) const451 void MultiVerNaturalStoreCommitStorage::ReleaseUnusedCommits(
452 std::map<CommitID, IKvDBCommit *> &commits) const
453 {
454 // need release the unmerged commits
455 for (auto &item : commits) {
456 if (item.second != nullptr) {
457 ReleaseCommit(item.second);
458 item.second = nullptr;
459 }
460 }
461 commits.clear();
462 }
463
ReleaseLatestCommits(std::map<DeviceID,IKvDBCommit * > & latestCommits) const464 void MultiVerNaturalStoreCommitStorage::ReleaseLatestCommits(
465 std::map<DeviceID, IKvDBCommit *> &latestCommits) const
466 {
467 // need release the commits for exception.
468 for (auto &item : latestCommits) {
469 if (item.second != nullptr) {
470 ReleaseCommit(item.second);
471 item.second = nullptr;
472 }
473 }
474 latestCommits.clear();
475 }
476
ReleaseCommitList(list<IKvDBCommit * > & commits) const477 void MultiVerNaturalStoreCommitStorage::ReleaseCommitList(list<IKvDBCommit *> &commits) const
478 {
479 for (auto &item : commits) {
480 if (item != nullptr) {
481 ReleaseCommit(item);
482 item = nullptr;
483 }
484 }
485 commits.clear();
486 }
487
GetLatestCommits(std::map<DeviceID,IKvDBCommit * > & latestCommits) const488 int MultiVerNaturalStoreCommitStorage::GetLatestCommits(std::map<DeviceID, IKvDBCommit *> &latestCommits) const
489 {
490 latestCommits.clear();
491 map<CommitID, IKvDBCommit *> commits;
492 CommitID headerId;
493 int errCode = GetAllCommits(commits, headerId);
494 if (errCode != E_OK || commits.empty()) { // error or no commit.
495 return errCode;
496 }
497
498 std::stack<CommitID> commitStack;
499 commitStack.push(headerId);
500 while (!commitStack.empty()) {
501 CommitID frontId = commitStack.top();
502 auto currentCommitIter = commits.find(frontId);
503 if (currentCommitIter == commits.end()) {
504 // not found the node in the commit tree.
505 LOGE("Not found the commit for the latest commits!");
506 ReleaseLatestCommits(latestCommits);
507 errCode = -E_UNEXPECTED_DATA;
508 break;
509 }
510
511 commitStack.pop();
512 if (currentCommitIter->second == nullptr) {
513 // if the node has been released or traveled.
514 continue;
515 }
516
517 AddParentsToStack(currentCommitIter->second, commits, commitStack);
518
519 // Get the current commit info
520 DeviceID deviceInfo = currentCommitIter->second->GetDeviceInfo();
521 auto latestCommit = latestCommits.find(deviceInfo);
522 if (latestCommit == latestCommits.end()) {
523 // not found any node of the device in the commit tree.
524 latestCommits.insert(make_pair(deviceInfo, currentCommitIter->second));
525 } else if (CompareCommit(latestCommit->second, currentCommitIter->second)) {
526 // if the current commit version is bigger than the stored.
527 ReleaseCommit(latestCommit->second);
528 latestCommit->second = currentCommitIter->second;
529 } else {
530 ReleaseCommit(currentCommitIter->second);
531 }
532 currentCommitIter->second = nullptr; // has been traveled, set to nullptr.
533 }
534
535 ReleaseUnusedCommits(commits);
536 return errCode;
537 }
538
GetLocalVersionThredForLatestCommits(const map<CommitID,IKvDBCommit * > & allCommits,const map<DeviceID,CommitID> & latestCommits,map<DeviceID,Version> & latestCommitVersions)539 void MultiVerNaturalStoreCommitStorage::GetLocalVersionThredForLatestCommits(
540 const map<CommitID, IKvDBCommit *> &allCommits, const map<DeviceID, CommitID> &latestCommits,
541 map<DeviceID, Version> &latestCommitVersions)
542 {
543 for (const auto &latestCommit : latestCommits) {
544 auto commitIter = allCommits.find(latestCommit.second);
545 if (commitIter != allCommits.end()) {
546 // found in the local store, just set the threshold.
547 latestCommitVersions.insert(make_pair(latestCommit.first, commitIter->second->GetCommitVersion()));
548 } else {
549 // not found in the local store, means that newer than local.
550 latestCommitVersions.insert(make_pair(latestCommit.first, VERSION_MAX));
551 }
552 }
553 }
554
AddParentsToStack(const IKvDBCommit * commit,const std::map<CommitID,IKvDBCommit * > & allCommits,std::stack<CommitID> & commitStack)555 void MultiVerNaturalStoreCommitStorage::AddParentsToStack(const IKvDBCommit *commit,
556 const std::map<CommitID, IKvDBCommit *> &allCommits, std::stack<CommitID> &commitStack)
557 {
558 if (commit == nullptr) {
559 return;
560 }
561
562 auto leftParentId = commit->GetLeftParentId();
563 auto rightParentId = commit->GetRightParentId();
564 if (!rightParentId.empty()) {
565 auto iter = allCommits.find(rightParentId);
566 if (iter != allCommits.end() && iter->second != nullptr) {
567 // if the right parent has not been traveled, just push into the stack.
568 commitStack.push(rightParentId);
569 }
570 }
571 if (!leftParentId.empty()) {
572 auto iter = allCommits.find(leftParentId);
573 if (iter != allCommits.end() && iter->second != nullptr) {
574 // if the left parent has not been traveled, just push into the stack.
575 commitStack.push(leftParentId);
576 }
577 }
578 }
579
GetCommitTree(const map<DeviceID,CommitID> & latestCommits,list<IKvDBCommit * > & commits) const580 int MultiVerNaturalStoreCommitStorage::GetCommitTree(const map<DeviceID, CommitID> &latestCommits,
581 list<IKvDBCommit *> &commits) const
582 {
583 commits.clear();
584 CommitID header;
585 map<CommitID, IKvDBCommit *> allCommits;
586 int errCode = GetAllCommits(allCommits, header);
587 // error or no commit.
588 if (errCode != E_OK || allCommits.empty()) {
589 return errCode;
590 }
591 map<DeviceID, Version> latestCommitVersions;
592 GetLocalVersionThredForLatestCommits(allCommits, latestCommits, latestCommitVersions);
593 std::stack<CommitID> commitStack;
594 commitStack.push(header);
595 while (!commitStack.empty()) {
596 CommitID frontId = commitStack.top();
597 auto currentCommitIter = allCommits.find(frontId);
598 if (currentCommitIter == allCommits.end()) {
599 // not found the node in the commit tree.
600 LOGE("Not found the commit in the local tree!");
601 ReleaseCommitList(commits);
602 errCode = -E_UNEXPECTED_DATA;
603 break;
604 }
605 commitStack.pop();
606 if (currentCommitIter->second == nullptr) {
607 // if the commit has been traveled.
608 continue;
609 }
610 AddParentsToStack(currentCommitIter->second, allCommits, commitStack);
611 // Get the current commit info
612 DeviceID deviceInfo = currentCommitIter->second->GetDeviceInfo();
613 auto latestCommit = latestCommitVersions.find(deviceInfo);
614 if (latestCommit == latestCommitVersions.end() ||
615 latestCommit->second < currentCommitIter->second->GetCommitVersion()) {
616 // not found in the latest commits of the other device,
617 // or the current commit version is bigger than the threshold.
618 commits.push_back(currentCommitIter->second);
619 } else {
620 // means that the commit existed in the other device.
621 ReleaseCommit(currentCommitIter->second);
622 }
623 currentCommitIter->second = nullptr;
624 }
625
626 ReleaseUnusedCommits(allCommits);
627 RefreshCommitTree(commits); // for version ascend.
628 return errCode;
629 }
630
RunRekeyLogic(CipherType type,const CipherPassword & passwd)631 int MultiVerNaturalStoreCommitStorage::RunRekeyLogic(CipherType type, const CipherPassword &passwd)
632 {
633 int errCode = static_cast<SQLiteLocalKvDB *>(commitStorageDatabase_)->RunRekeyLogic(type, passwd);
634 if (errCode != E_OK) {
635 LOGE("commit logs rekey failed:%d", errCode);
636 }
637 return errCode;
638 }
639
RunExportLogic(CipherType type,const CipherPassword & passwd,const std::string & dbDir)640 int MultiVerNaturalStoreCommitStorage::RunExportLogic(CipherType type, const CipherPassword &passwd,
641 const std::string &dbDir)
642 {
643 // execute export
644 std::string newDbName = dbDir + "/" + MULTI_VER_COMMIT_DB_NAME;
645 int errCode = static_cast<SQLiteLocalKvDB *>(commitStorageDatabase_)->RunExportLogic(type, passwd, newDbName);
646 if (errCode != E_OK) {
647 LOGE("commit logs export failed:%d", errCode);
648 }
649 return errCode;
650 }
651
TransferCommitIDToKey(const CommitID & commitID,Key & key)652 void MultiVerNaturalStoreCommitStorage::TransferCommitIDToKey(const CommitID &commitID, Key &key)
653 {
654 key = commitID;
655 }
656
TransferCommitToValue(const IKvDBCommit & commit,Value & value)657 int MultiVerNaturalStoreCommitStorage::TransferCommitToValue(const IKvDBCommit &commit, Value &value)
658 {
659 // 3 uint64_t members.
660 uint32_t totalLength = Parcel::GetUInt64Len() * 3 + Parcel::GetVectorCharLen(commit.GetCommitId()) +
661 Parcel::GetVectorCharLen(commit.GetLeftParentId()) + Parcel::GetVectorCharLen(commit.GetRightParentId()) +
662 Parcel::GetStringLen(commit.GetDeviceInfo());
663 if (totalLength > MAX_COMMIT_ST_LENGTH) {
664 LOGE("The commit length is over the max threshold");
665 return -E_UNEXPECTED_DATA;
666 }
667
668 value.resize(totalLength);
669 Parcel parcel(const_cast<uint8_t *>(value.data()), totalLength);
670
671 int errCode = parcel.WriteUInt64(commit.GetTimestamp());
672 if (errCode != E_OK) {
673 return errCode;
674 }
675
676 uint64_t localFlag = static_cast<uint32_t>((commit.GetLocalFlag() == true) ? 1 : 0);
677 errCode = parcel.WriteUInt64(localFlag);
678 if (errCode != E_OK) {
679 return errCode;
680 }
681
682 errCode = parcel.WriteUInt64(commit.GetCommitVersion());
683 if (errCode != E_OK) {
684 return errCode;
685 }
686
687 errCode = parcel.WriteVectorChar(commit.GetCommitId());
688 if (errCode != E_OK) {
689 return errCode;
690 }
691
692 errCode = parcel.WriteVectorChar(commit.GetLeftParentId());
693 if (errCode != E_OK) {
694 return errCode;
695 }
696
697 errCode = parcel.WriteVectorChar(commit.GetRightParentId());
698 if (errCode != E_OK) {
699 return errCode;
700 }
701
702 return parcel.WriteString(commit.GetDeviceInfo());
703 }
704
TransferValueToCommit(const Value & value,IKvDBCommit & commit)705 int MultiVerNaturalStoreCommitStorage::TransferValueToCommit(const Value &value, IKvDBCommit &commit)
706 {
707 size_t valueLength = value.size();
708 if (valueLength == 0 || valueLength >= MAX_COMMIT_ST_LENGTH) {
709 LOGE("Failed to transfer value to commit struct! invalid value length:%zu.", valueLength);
710 return -E_UNEXPECTED_DATA;
711 }
712
713 Timestamp timestamp = 0;
714 uint64_t localFlag = 1;
715 Version versionInfo;
716
717 CommitID commitID;
718 CommitID leftParentID;
719 CommitID rightParentID;
720 DeviceID deviceInfo;
721
722 Parcel parcel(const_cast<uint8_t *>(value.data()), valueLength);
723 parcel.ReadUInt64(timestamp);
724 parcel.ReadUInt64(localFlag);
725 parcel.ReadUInt64(versionInfo);
726 parcel.ReadVectorChar(commitID);
727 parcel.ReadVectorChar(leftParentID);
728 parcel.ReadVectorChar(rightParentID);
729 parcel.ReadString(deviceInfo);
730 if (parcel.IsError()) {
731 return -E_PARSE_FAIL;
732 }
733
734 // set commit value
735 commit.SetCommitVersion(versionInfo);
736 commit.SetCommitId(commitID);
737 commit.SetLeftParentId(leftParentID);
738 commit.SetRightParentId(rightParentID);
739 commit.SetTimestamp(timestamp);
740 commit.SetLocalFlag((localFlag == 1) ? true : false);
741 commit.SetDeviceInfo(deviceInfo);
742 return E_OK;
743 }
744
TransferStringToKey(const string & str,Key & key)745 void MultiVerNaturalStoreCommitStorage::TransferStringToKey(const string &str, Key &key)
746 {
747 key.assign(str.begin(), str.end());
748 }
749
TransferCommitIDToValue(const CommitID & commitID,Value & value)750 void MultiVerNaturalStoreCommitStorage::TransferCommitIDToValue(const CommitID &commitID, Value &value)
751 {
752 value = commitID;
753 }
754
TransferValueToCommitID(const Value & value,CommitID & commitID)755 void MultiVerNaturalStoreCommitStorage::TransferValueToCommitID(const Value &value, CommitID &commitID)
756 {
757 commitID = value;
758 }
759
CompareCommit(const IKvDBCommit * first,const IKvDBCommit * second)760 bool MultiVerNaturalStoreCommitStorage::CompareCommit(const IKvDBCommit *first,
761 const IKvDBCommit *second)
762 {
763 if (first == nullptr || second == nullptr) {
764 return false;
765 }
766 return first->GetCommitVersion() < second->GetCommitVersion();
767 }
768
GetAllCommits(map<CommitID,IKvDBCommit * > & commits,CommitID & headerId) const769 int MultiVerNaturalStoreCommitStorage::GetAllCommits(map<CommitID, IKvDBCommit *> &commits,
770 CommitID &headerId) const
771 {
772 if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
773 LOGE("Failed to get all commits for uninitialized store");
774 return -E_INVALID_DB;
775 }
776 IOption option;
777 Key keyPrefix;
778 vector<Entry> entries;
779 int errCode = commitStorageDBConnection_->GetEntries(option, keyPrefix, entries);
780 if (errCode != E_OK) {
781 if (errCode == -E_NOT_FOUND) {
782 errCode = E_OK;
783 } else {
784 LOGE("Failed to get commit entries from DB:%d", errCode);
785 }
786
787 return errCode;
788 }
789
790 Key header;
791 TransferStringToKey(HEADER_KEY, header);
792
793 for (const auto &entry : entries) {
794 if (entry.key == header) {
795 headerId = entry.value; // get the header.
796 continue;
797 }
798 IKvDBCommit *commit = new (std::nothrow) MultiVerCommit();
799 if (commit == nullptr) {
800 ReleaseUnusedCommits(commits);
801 LOGE("Failed to alloc commit! Bad alloc.");
802 return -E_OUT_OF_MEMORY;
803 }
804 errCode = TransferValueToCommit(entry.value, *commit);
805 if (errCode != E_OK) {
806 delete commit;
807 commit = nullptr;
808 ReleaseUnusedCommits(commits);
809 return errCode;
810 }
811 commits.insert(make_pair(commit->GetCommitId(), commit));
812 }
813 return E_OK;
814 }
815
SetHeaderInner(const CommitID & commitId)816 int MultiVerNaturalStoreCommitStorage::SetHeaderInner(const CommitID &commitId)
817 {
818 Key key;
819 Value value;
820 TransferStringToKey(HEADER_KEY, key);
821 TransferCommitIDToValue(commitId, value);
822 IOption option;
823 int errCode = commitStorageDBConnection_->Put(option, key, value);
824 if (errCode != E_OK) {
825 LOGE("Failed to set header! err:%d", errCode);
826 }
827 return errCode;
828 }
829
CheckAddedCommit(const IKvDBCommit & commitEntry) const830 int MultiVerNaturalStoreCommitStorage::CheckAddedCommit(const IKvDBCommit &commitEntry) const
831 {
832 if (commitStorageDatabase_ == nullptr || commitStorageDBConnection_ == nullptr) {
833 LOGE("Failed to get commit! Commit storage do not open.");
834 return -E_INVALID_DB;
835 }
836 // Parameter check
837 if (!((static_cast<const MultiVerCommit&>(commitEntry)).CheckCommit())) {
838 LOGE("Failed to add commit! Commit is invalid.");
839 return -E_UNEXPECTED_DATA;
840 }
841 int errCode = E_OK;
842 if (commitEntry.GetLeftParentId().size() != 0) {
843 if (!CommitExist(commitEntry.GetLeftParentId(), errCode)) {
844 LOGE("Failed to add commit! The left parent commit does not exist.");
845 return errCode;
846 }
847 }
848 if (commitEntry.GetRightParentId().size() != 0) {
849 if (!CommitExist(commitEntry.GetRightParentId(), errCode)) {
850 LOGE("Failed to add commit! The right parent commit does not exist.");
851 return errCode;
852 }
853 }
854
855 return E_OK;
856 }
857
RefreshCommitTree(std::list<IKvDBCommit * > & commits)858 void MultiVerNaturalStoreCommitStorage::RefreshCommitTree(std::list<IKvDBCommit *> &commits)
859 {
860 if (commits.empty()) {
861 return;
862 }
863 commits.sort(CompareCommit);
864 }
865
GetMaxCommitVersion(int & errCode) const866 Version MultiVerNaturalStoreCommitStorage::GetMaxCommitVersion(int &errCode) const
867 {
868 std::map<CommitID, IKvDBCommit *> commits;
869 CommitID headerId;
870 errCode = GetAllCommits(commits, headerId);
871 if (errCode != E_OK || commits.empty()) { // means no commit or error.
872 return 0;
873 }
874
875 Version maxVersion = 0;
876 for (const auto &item : commits) {
877 if (item.second != nullptr) {
878 Version itemVersion = item.second->GetCommitVersion();
879 maxVersion = (maxVersion < itemVersion) ? itemVersion : maxVersion;
880 }
881 }
882 ReleaseUnusedCommits(commits);
883 errCode = E_OK;
884 return maxVersion;
885 }
886
BackupCurrentDatabase(const Property & property,const std::string & dir)887 int MultiVerNaturalStoreCommitStorage::BackupCurrentDatabase(const Property &property, const std::string &dir)
888 {
889 KvDBProperties dbProperties;
890 dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true);
891 dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
892 dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
893 dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
894 dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
895 dbProperties.SetPassword(property.cipherType, property.passwd);
896 int errCode = SQLiteLocalKvDB::BackupCurrentDatabase(dbProperties, dir);
897 return errCode;
898 }
899
ImportDatabase(const Property & property,const std::string & dir,const CipherPassword & passwd)900 int MultiVerNaturalStoreCommitStorage::ImportDatabase(const Property &property, const std::string &dir,
901 const CipherPassword &passwd)
902 {
903 KvDBProperties dbProperties;
904 dbProperties.SetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true);
905 dbProperties.SetStringProp(KvDBProperties::DATA_DIR, property.path);
906 dbProperties.SetStringProp(KvDBProperties::FILE_NAME, DBConstant::MULTI_VER_COMMIT_STORE);
907 dbProperties.SetStringProp(KvDBProperties::IDENTIFIER_DIR, property.identifierName);
908 dbProperties.SetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::MULTI_VER_TYPE_SQLITE);
909 dbProperties.SetPassword(property.cipherType, property.passwd);
910 int errCode = SQLiteLocalKvDB::ImportDatabase(dbProperties, dir, passwd);
911 return errCode;
912 }
913 } // namespace DistributedDB
914 #endif
915