1/*
2 * Copyright (c) 2021-2022 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 "database_disaster_recovery.h"
17
18#include <mutex>
19
20#include "calllog_database.h"
21#include "contacts_database.h"
22#include "file_utils.h"
23#include "profile_database.h"
24
25namespace OHOS {
26namespace Contacts {
27namespace {
28std::mutex g_mtx;
29}
30
31const std::string DataBaseDisasterRecovery::BACKUP_LINK_SYMBOL = "_";
32const std::string DataBaseDisasterRecovery::BACKUP_SUFFIX = ".db";
33const std::string DataBaseDisasterRecovery::DB_OK = "ok";
34std::shared_ptr<DataBaseDisasterRecovery> DataBaseDisasterRecovery::instance_ = nullptr;
35std::map<std::string, std::shared_ptr<OHOS::NativeRdb::RdbStore>> DataBaseDisasterRecovery::redbStoreMap;
36
37std::shared_ptr<DataBaseDisasterRecovery> DataBaseDisasterRecovery::GetInstance()
38{
39    if (instance_ == nullptr) {
40        instance_.reset(new DataBaseDisasterRecovery());
41    }
42    return instance_;
43}
44
45DataBaseDisasterRecovery::DataBaseDisasterRecovery()
46{
47}
48
49DataBaseDisasterRecovery::~DataBaseDisasterRecovery()
50{
51}
52
53int DataBaseDisasterRecovery::SQLiteCheckDb()
54{
55    g_mtx.lock();
56    HILOG_INFO("DataBaseDisasterRecovery SQLliteCheck start.");
57    redbStoreMap.clear();
58    if (redbStoreMap.empty()) {
59        HILOG_ERROR("DataBaseDisasterRecovery redbStoreMap init. nullptr!");
60        std::shared_ptr<ProfileDatabase> profile = ProfileDatabase::GetInstance();
61        std::shared_ptr<ContactsDataBase> contacts = ContactsDataBase::GetInstance();
62        redbStoreMap.insert(std::make_pair(PROFILE_DATABASE_NAME, profile->store_));
63        redbStoreMap.insert(std::make_pair(CONTACT_DATABASE_NAME, contacts->contactStore_));
64    }
65    if (redbStoreMap.empty()) {
66        HILOG_ERROR("DataBaseDisasterRecovery SQLliteCheck redbStoreMap is empty!");
67        g_mtx.unlock();
68        return RDB_OBJECT_EMPTY;
69    }
70    for (auto &kv : redbStoreMap) {
71        std::shared_ptr<OHOS::NativeRdb::RdbStore> store_ = kv.second;
72        int ret = SQLiteCheckDb(store_, kv.first);
73        if (ret != OHOS::NativeRdb::E_OK) {
74            HILOG_ERROR("DataBaseDisasterRecovery SQLiteCheckDb ERROR.");
75            g_mtx.unlock();
76            return ret;
77        }
78    }
79    g_mtx.unlock();
80    HILOG_INFO("DataBaseDisasterRecovery SQLliteCheck end.");
81    return RDB_EXECUTE_OK;
82}
83
84int DataBaseDisasterRecovery::SQLiteCheckDb(
85    std::shared_ptr<OHOS::NativeRdb::RdbStore> rdbStore, std::string dataBaseName)
86{
87    // default switch
88    std::shared_ptr<OHOS::NativeRdb::RdbStore> &store_ = rdbStore;
89    if (store_ == nullptr) {
90        HILOG_ERROR("DataBaseDisasterRecovery SQLliteCheck %{public}s store_ is nullptr", dataBaseName.c_str());
91        return RDB_OBJECT_EMPTY;
92    }
93    std::string result;
94    store_->ExecuteAndGetString(result, "PRAGMA quick_check(0)");
95    HILOG_INFO("DataBaseDisasterRecovery check %{public}s result is %{public}s", dataBaseName.c_str(), result.c_str());
96    if (result == DB_OK) {
97        return RDB_EXECUTE_OK;
98    }
99    return RDB_EXECUTE_FAIL;
100}
101
102int DataBaseDisasterRecovery::BackDatabase()
103{
104    // Recovery
105    g_mtx.lock();
106    HILOG_INFO("entry DataBaseDisasterRecovery");
107    FileUtils fileUtils;
108    fileUtils.Mkdir(ContactsPath::RDB_BACKUP_PATH);
109    redbStoreMap.clear();
110    if (redbStoreMap.empty()) {
111        HILOG_ERROR("DataBaseDisasterRecovery redbStoreMap init. nullptr");
112        std::shared_ptr<ProfileDatabase> profile = ProfileDatabase::GetInstance();
113        std::shared_ptr<ContactsDataBase> contacts = ContactsDataBase::GetInstance();
114        redbStoreMap.insert(std::make_pair(PROFILE_DATABASE_NAME, profile->store_));
115        redbStoreMap.insert(std::make_pair(CONTACT_DATABASE_NAME, contacts->contactStore_));
116    }
117    if (redbStoreMap.empty()) {
118        HILOG_ERROR("DataBaseDisasterRecovery SQLliteCheck redbStoreMap is empty");
119        g_mtx.unlock();
120        return RDB_OBJECT_EMPTY;
121    }
122    for (auto &kv : redbStoreMap) {
123        std::shared_ptr<OHOS::NativeRdb::RdbStore> store_ = kv.second;
124        int version;
125        store_->GetVersion(version);
126        HILOG_INFO("backup version is %{public}d", version);
127        int ret = BackDatabase(kv.first);
128        HILOG_INFO("BackDatabase %{public}s status is %{public}d", kv.first.c_str(), ret);
129    }
130    g_mtx.unlock();
131    return RDB_EXECUTE_OK;
132}
133
134int DataBaseDisasterRecovery::BackDatabase(std::string dataBaseName)
135{
136    g_mtx.lock();
137    auto iter = redbStoreMap.find(dataBaseName);
138    HILOG_INFO("DataBaseDisasterRecovery BackDatabase redbStoreMap size is %{public}zu", redbStoreMap.size());
139    if (iter != redbStoreMap.end()) {
140        std::shared_ptr<OHOS::NativeRdb::RdbStore> store_ = iter->second;
141        if (store_ == nullptr) {
142            HILOG_ERROR("DataBaseDisasterRecovery BackDatabase %{public}s store_ is nullptr", dataBaseName.c_str());
143            g_mtx.unlock();
144            return RDB_OBJECT_EMPTY;
145        }
146        std::string dbPath = ContactsPath::RDB_BACKUP_PATH + dataBaseName + BACKUP_SUFFIX;
147        int64_t outRowId = 0;
148        OHOS::NativeRdb::ValuesBucket values;
149        values.PutString(DatabaseBackupColumns::BACKUP_PATH, dbPath);
150        int ret = store_->Insert(outRowId, ContactTableName::DATABASE_BACKUP_TASK, values);
151        if (ret != OHOS::NativeRdb::E_OK) {
152            HILOG_ERROR("DataBaseDisasterRecovery Insert failed, status is %{public}d.", ret);
153            g_mtx.unlock();
154            return RDB_EXECUTE_FAIL;
155        }
156        OHOS::NativeRdb::RdbHelper::DeleteRdbStore(dbPath);
157        ret = store_->Backup(dbPath, std::vector<uint8_t>());
158        if (ret != OHOS::NativeRdb::E_OK) {
159            HILOG_ERROR("DataBaseDisasterRecovery Backup failed, status is %{public}d.", ret);
160            g_mtx.unlock();
161            return RDB_EXECUTE_FAIL;
162        }
163    }
164    g_mtx.unlock();
165    return RDB_EXECUTE_OK;
166}
167
168std::string DataBaseDisasterRecovery::GetBackUpDatabase(const std::shared_ptr<OHOS::NativeRdb::RdbStore> &store_)
169{
170    std::string sql = "select backup_path from ";
171    sql.append(ContactTableName::DATABASE_BACKUP_TASK).append(" order by backup_time desc limit 0,1");
172    auto result = store_->QuerySql(sql, std::vector<std::string>());
173    std::string currValue;
174    if (result->GoToFirstRow() == OHOS::NativeRdb::E_OK) {
175        int currValueIndex;
176        result->GetColumnIndex(DatabaseBackupColumns::BACKUP_PATH, currValueIndex);
177        result->GetString(currValueIndex, currValue);
178        result->GoToNextRow();
179    }
180    result->Close();
181    return currValue;
182}
183
184int DataBaseDisasterRecovery::RecoveryDatabase(std::string dataBaseName)
185{
186    if (dataBaseName == PROFILE_DATABASE_NAME) {
187        std::string backupPath = ContactsPath::RDB_BACKUP_PATH + dataBaseName + BACKUP_SUFFIX;
188        ProfileDatabase::DestroyInstanceAndRestore(backupPath);
189    } else if (dataBaseName == CONTACT_DATABASE_NAME) {
190        std::string backupPath = ContactsPath::RDB_BACKUP_PATH + dataBaseName + BACKUP_SUFFIX;
191        ContactsDataBase::DestroyInstanceAndRestore(backupPath);
192    }
193    return RDB_EXECUTE_OK;
194}
195} // namespace Contacts
196} // namespace OHOS
197