• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2024 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 "KvAdaptor"
16 
17 #include <cstddef>
18 #include <filesystem>
19 #include <fstream>
20 #include <iostream>
21 
22 #include "kv_delegate.h"
23 
24 #include "datashare_errno.h"
25 #include "directory/directory_manager.h"
26 #include "grd_base/grd_error.h"
27 #include "grd_document/grd_document_api.h"
28 #include "ipc_skeleton.h"
29 #include "log_print.h"
30 #include "log_debug.h"
31 
32 namespace OHOS::DataShare {
33 constexpr int WAIT_TIME = 30;
34 
35 // If using multi-process access, back up dataShare.db.map as well. Check config file to see whether
36 // using multi-process maccess
37 const char* g_backupFiles[] = {
38     "dataShare.db",
39     "dataShare.db.redo",
40     "dataShare.db.safe",
41     "dataShare.db.undo",
42 };
43 constexpr const char* BACKUP_SUFFIX = ".backup";
44 constexpr std::chrono::milliseconds COPY_TIME_OUT_MS = std::chrono::milliseconds(500);
45 
46 // If isBackUp is true, remove db backup files. Otherwise remove source db files.
RemoveDbFile(bool isBackUp) const47 void KvDelegate::RemoveDbFile(bool isBackUp) const
48 {
49     for (auto &fileName: g_backupFiles) {
50         std::string dbPath = path_ + "/" + fileName;
51         if (isBackUp) {
52             dbPath += BACKUP_SUFFIX;
53         }
54         if (std::filesystem::exists(dbPath)) {
55             std::error_code ec;
56             bool success = std::filesystem::remove(dbPath, ec);
57             if (!success) {
58                 ZLOGE("failed to remove file %{public}s, err: %{public}s", fileName, ec.message().c_str());
59             }
60         }
61     }
62 }
63 
CopyFile(bool isBackup)64 bool KvDelegate::CopyFile(bool isBackup)
65 {
66     std::filesystem::copy_options options = std::filesystem::copy_options::overwrite_existing;
67     std::error_code code;
68     bool ret = true;
69     int index = 0;
70     for (auto &fileName : g_backupFiles) {
71         std::string src = path_ + "/" + fileName;
72         std::string dst = src;
73         isBackup ? dst.append(BACKUP_SUFFIX) : src.append(BACKUP_SUFFIX);
74         TimeoutReport timeoutReport({"", "", "", __FUNCTION__, 0});
75         // If src doesn't exist, error will be returned through `std::error_code`
76         bool copyRet = std::filesystem::copy_file(src, dst, options, code);
77         timeoutReport.Report(("file index:" + std::to_string(index)), COPY_TIME_OUT_MS);
78         if (!copyRet) {
79             ZLOGE("failed to copy file %{public}s, isBackup %{public}d, err: %{public}s",
80                 fileName, isBackup, code.message().c_str());
81             ret = false;
82             RemoveDbFile(isBackup);
83             break;
84         }
85         index++;
86     }
87     return ret;
88 }
89 
90 // Restore database data when it is broken somehow. Some failure of insertion / deletion / updates will be considered
91 // as database files damage, and therefore trigger the process of restoration.
Restore()92 void KvDelegate::Restore()
93 {
94     // No need to lock because this inner method will only be called when upper methods lock up
95     CopyFile(false);
96     ZLOGD_MACRO("finish restoring kv");
97 }
98 
99 // Backup database data by copying its key files. This mechanism might be costly, but acceptable when updating
100 // contents of KV database happens not so frequently now.
Backup()101 void KvDelegate::Backup()
102 {
103     // No need to lock because this inner method will only be called when upper methods lock up
104     ZLOGD_MACRO("backup kv");
105     if (hasChange_) {
106         CopyFile(true);
107         hasChange_ = false;
108     }
109     ZLOGD_MACRO("finish backing up kv");
110 }
111 
112 // Set hasChange_ to true. Caller can use this to control when to back up db.
NotifyBackup()113 void OHOS::DataShare::KvDelegate::NotifyBackup()
114 {
115     std::lock_guard<decltype(mutex_)> lock(mutex_);
116     hasChange_ = true;
117 }
118 
119 // The return val indicates whether the database has been restored
RestoreIfNeed(int32_t dbStatus)120 bool KvDelegate::RestoreIfNeed(int32_t dbStatus)
121 {
122     // No need to lock because this inner method will only be called when upper methods lock up
123     if (dbStatus == GRD_INVALID_FILE_FORMAT || dbStatus == GRD_REBUILD_DATABASE) {
124         if (db_ != NULL) {
125             GRD_DBClose(db_, GRD_DB_CLOSE);
126             db_ = nullptr;
127             isInitDone_ = false;
128         }
129         // If db is NULL, it has been closed.
130         Restore();
131         return true;
132     }
133     return false;
134 }
135 
Upsert(const std::string & collectionName,const std::string & filter,const std::string & value)136 std::pair<int32_t, int32_t> KvDelegate::Upsert(const std::string &collectionName, const std::string &filter,
137     const std::string &value)
138 {
139     std::lock_guard<decltype(mutex_)> lock(mutex_);
140     if (!Init()) {
141         ZLOGE("init failed, %{public}s", collectionName.c_str());
142         return std::make_pair(E_ERROR, 0);
143     }
144     int32_t count = GRD_UpsertDoc(db_, collectionName.c_str(), filter.c_str(), value.c_str(), 0);
145     if (count <= 0) {
146         ZLOGE("GRD_UpSertDoc failed,status %{public}d", count);
147         RestoreIfNeed(count);
148         return std::make_pair(count, 0);
149     }
150     Flush();
151     return std::make_pair(E_OK, count);
152 }
153 
Delete(const std::string & collectionName,const std::string & filter)154 std::pair<int32_t, int32_t> KvDelegate::Delete(const std::string &collectionName, const std::string &filter)
155 {
156     std::lock_guard<decltype(mutex_)> lock(mutex_);
157     if (!Init()) {
158         ZLOGE("init failed, %{public}s", collectionName.c_str());
159         return std::make_pair(E_ERROR, 0);
160     }
161     std::vector<std::string> queryResults;
162 
163     int32_t status = GetBatch(collectionName, filter, "{\"id_\": true}", queryResults);
164     if (status != E_OK) {
165         ZLOGE("db GetBatch failed, %{public}s %{public}d", filter.c_str(), status);
166         // `GetBatch` should decide whether to restore before errors are returned, so skip restoration here.
167         return std::make_pair(status, 0);
168     }
169     int32_t deleteCount = 0;
170     for (auto &result : queryResults) {
171         auto count = GRD_DeleteDoc(db_, collectionName.c_str(), result.c_str(), 0);
172         if (count < 0) {
173             ZLOGE("GRD_DeleteDoc failed,status %{public}d %{public}s", count, result.c_str());
174             if (RestoreIfNeed(count)) {
175                 return std::make_pair(count, 0);
176             }
177             continue;
178         }
179         deleteCount += count;
180     }
181     Flush();
182     if (queryResults.size() > 0) {
183         ZLOGI("Delete, %{public}s, count %{public}zu", collectionName.c_str(), queryResults.size());
184     }
185     return std::make_pair(E_OK, deleteCount);
186 }
187 
Init()188 bool KvDelegate::Init()
189 {
190     if (isInitDone_) {
191         if (executors_ != nullptr) {
192             executors_->Reset(taskId_, std::chrono::seconds(WAIT_TIME));
193         }
194         return true;
195     }
196     int status = GRD_DBOpen(
197         (path_ + "/dataShare.db").c_str(), nullptr, GRD_DB_OPEN_CREATE | GRD_DB_OPEN_CHECK_FOR_ABNORMAL, &db_);
198     if (status != GRD_OK || db_ == nullptr) {
199         ZLOGE("GRD_DBOpen failed,status %{public}d", status);
200         RestoreIfNeed(status);
201         return false;
202     }
203     if (executors_ != nullptr) {
204         taskId_ = executors_->Schedule(std::chrono::seconds(WAIT_TIME), [this]() {
205             std::lock_guard<decltype(mutex_)> lock(mutex_);
206             GRD_DBClose(db_, GRD_DB_CLOSE);
207             db_ = nullptr;
208             isInitDone_ = false;
209             taskId_ = ExecutorPool::INVALID_TASK_ID;
210             Backup();
211         });
212     }
213     status = GRD_CreateCollection(db_, TEMPLATE_TABLE, nullptr, 0);
214     if (status != GRD_OK) {
215         // If opeaning db succeeds, it is rare to fail to create tables
216         ZLOGE("GRD_CreateCollection template table failed,status %{public}d", status);
217         RestoreIfNeed(status);
218         return false;
219     }
220 
221     status = GRD_CreateCollection(db_, DATA_TABLE, nullptr, 0);
222     if (status != GRD_OK) {
223         // If opeaning db succeeds, it is rare to fail to create tables
224         ZLOGE("GRD_CreateCollection data table failed,status %{public}d", status);
225         RestoreIfNeed(status);
226         return false;
227     }
228     isInitDone_ = true;
229     return true;
230 }
231 
~KvDelegate()232 KvDelegate::~KvDelegate()
233 {
234     std::lock_guard<decltype(mutex_)> lock(mutex_);
235     if (isInitDone_) {
236         int status = GRD_DBClose(db_, 0);
237         if (status != GRD_OK) {
238             ZLOGE("GRD_DBClose failed,status %{public}d", status);
239         }
240     }
241 }
242 
Upsert(const std::string & collectionName,const KvData & value)243 std::pair<int32_t, int32_t> KvDelegate::Upsert(const std::string &collectionName, const KvData &value)
244 {
245     std::string id = value.GetId();
246     if (value.HasVersion() && value.GetVersion() != 0) {
247         int version = -1;
248         if (GetVersion(collectionName, id, version)) {
249             if (value.GetVersion() <= version) {
250                 ZLOGE("GetVersion failed,%{public}s id %{private}s %{public}d %{public}d", collectionName.c_str(),
251                       id.c_str(), value.GetVersion(), version);
252                 return std::make_pair(E_VERSION_NOT_NEWER, 0);
253             }
254         }
255     }
256     return Upsert(collectionName, id, value.GetValue());
257 }
258 
Get(const std::string & collectionName,const Id & id,std::string & value)259 int32_t KvDelegate::Get(const std::string &collectionName, const Id &id, std::string &value)
260 {
261     std::string filter = DistributedData::Serializable::Marshall(id);
262     if (Get(collectionName, filter, "{}", value) != E_OK) {
263         ZLOGE("Get failed, %{public}s %{public}s", collectionName.c_str(), filter.c_str());
264         return E_ERROR;
265     }
266     return E_OK;
267 }
268 
GetVersion(const std::string & collectionName,const std::string & filter,int & version)269 bool KvDelegate::GetVersion(const std::string &collectionName, const std::string &filter, int &version)
270 {
271     std::string value;
272     if (Get(collectionName, filter, "{}", value) != E_OK) {
273         ZLOGE("Get failed, %{public}s %{public}s", collectionName.c_str(), filter.c_str());
274         return false;
275     }
276     VersionData data(-1);
277     if (!DistributedData::Serializable::Unmarshall(value, data)) {
278         ZLOGE("Unmarshall failed,data %{public}s", value.c_str());
279         return false;
280     }
281     version = data.GetVersion();
282     return true;
283 }
284 
Get(const std::string & collectionName,const std::string & filter,const std::string & projection,std::string & result)285 int32_t KvDelegate::Get(
286     const std::string &collectionName, const std::string &filter, const std::string &projection, std::string &result)
287 {
288     std::lock_guard<decltype(mutex_)> lock(mutex_);
289     if (!Init()) {
290         ZLOGE("init failed, %{public}s", collectionName.c_str());
291         return E_ERROR;
292     }
293     Query query;
294     query.filter = filter.c_str();
295     query.projection = projection.c_str();
296     GRD_ResultSet *resultSet = nullptr;
297     int status = GRD_FindDoc(db_, collectionName.c_str(), query, 0, &resultSet);
298     if (status != GRD_OK || resultSet == nullptr) {
299         ZLOGE("GRD_FindDoc failed,status %{public}d", status);
300         RestoreIfNeed(status);
301         return status;
302     }
303     status = GRD_Next(resultSet);
304     if (status != GRD_OK) {
305         GRD_FreeResultSet(resultSet);
306         ZLOGE("GRD_Next failed,status %{public}d", status);
307         RestoreIfNeed(status);
308         return status;
309     }
310     char *value = nullptr;
311     status = GRD_GetValue(resultSet, &value);
312     if (status != GRD_OK || value == nullptr) {
313         GRD_FreeResultSet(resultSet);
314         ZLOGE("GRD_GetValue failed,status %{public}d", status);
315         RestoreIfNeed(status);
316         return status;
317     }
318     result = value;
319     GRD_FreeValue(value);
320     GRD_FreeResultSet(resultSet);
321     return E_OK;
322 }
323 
Flush()324 void KvDelegate::Flush()
325 {
326     int status = GRD_Flush(db_, GRD_DB_FLUSH_ASYNC);
327     if (status != GRD_OK) {
328         ZLOGE("GRD_Flush failed,status %{public}d", status);
329         RestoreIfNeed(status);
330     }
331 }
332 
GetBatch(const std::string & collectionName,const std::string & filter,const std::string & projection,std::vector<std::string> & result)333 int32_t KvDelegate::GetBatch(const std::string &collectionName, const std::string &filter,
334     const std::string &projection, std::vector<std::string> &result)
335 {
336     std::lock_guard<decltype(mutex_)> lock(mutex_);
337     if (!Init()) {
338         ZLOGE("init failed, %{public}s", collectionName.c_str());
339         return E_ERROR;
340     }
341     Query query;
342     query.filter = filter.c_str();
343     query.projection = projection.c_str();
344     GRD_ResultSet *resultSet;
345     int status = GRD_FindDoc(db_, collectionName.c_str(), query, GRD_DOC_ID_DISPLAY, &resultSet);
346     if (status != GRD_OK || resultSet == nullptr) {
347         ZLOGE("GRD_FindDoc failed,status %{public}d", status);
348         RestoreIfNeed(status);
349         return status;
350     }
351     char *value = nullptr;
352     while (GRD_Next(resultSet) == GRD_OK) {
353         status = GRD_GetValue(resultSet, &value);
354         if (status != GRD_OK || value == nullptr) {
355             GRD_FreeResultSet(resultSet);
356             ZLOGE("GRD_GetValue failed,status %{public}d", status);
357             RestoreIfNeed(status);
358             return status;
359         }
360         result.emplace_back(value);
361         GRD_FreeValue(value);
362     }
363     GRD_FreeResultSet(resultSet);
364     return E_OK;
365 }
366 
KvDelegate(const std::string & path,const std::shared_ptr<ExecutorPool> & executors)367 KvDelegate::KvDelegate(const std::string &path, const std::shared_ptr<ExecutorPool> &executors)
368     : path_(path), executors_(executors)
369 {
370 }
371 } // namespace OHOS::DataShare