1 /*
2  * Copyright (C) 2023 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 "medialibrary_kvstore.h"
17 
18 #include <algorithm>
19 
20 #include "medialibrary_errno.h"
21 #include "medialibrary_tracer.h"
22 #include "media_log.h"
23 
24 using namespace OHOS::DistributedKv;
25 namespace OHOS::Media {
26 const OHOS::DistributedKv::AppId KVSTORE_APPID = {"com.ohos.medialibrary.medialibrarydata"};
27 const OHOS::DistributedKv::StoreId KVSTORE_MONTH_STOREID = {"medialibrary_month_astc_data"};
28 const OHOS::DistributedKv::StoreId KVSTORE_YEAR_STOREID = {"medialibrary_year_astc_data"};
29 const size_t KVSTORE_MAX_NUMBER_BATCH_INSERT = 100;
30 
31 // Different storeId used to distinguish different database
32 const OHOS::DistributedKv::StoreId KVSTORE_MONTH_STOREID_OLD_VERSION = {"medialibrary_month_astc"};
33 const OHOS::DistributedKv::StoreId KVSTORE_YEAR_STOREID_OLD_VERSION = {"medialibrary_year_astc"};
34 
~MediaLibraryKvStore()35 MediaLibraryKvStore::~MediaLibraryKvStore()
36 {
37     Close();
38 }
39 
Init(const KvStoreRoleType & roleType,const KvStoreValueType & valueType,const std::string & baseDir)40 int32_t MediaLibraryKvStore::Init(
41     const KvStoreRoleType &roleType, const KvStoreValueType &valueType, const std::string &baseDir)
42 {
43     MediaLibraryTracer tracer;
44     tracer.Start("MediaLibraryKvStore::InitKvStore");
45     Options options;
46     if (!GetKvStoreOption(options, roleType, baseDir)) {
47         MEDIA_ERR_LOG("failed to GetKvStoreOption");
48         return E_ERR;
49     }
50 
51     MEDIA_INFO_LOG("InitKvStore baseDir %{public}s", options.group.groupDir.c_str());
52     Status status;
53     if (valueType == KvStoreValueType::MONTH_ASTC) {
54         status = dataManager_.GetSingleKvStore(options, KVSTORE_APPID, KVSTORE_MONTH_STOREID, kvStorePtr_);
55     } else if (valueType == KvStoreValueType::YEAR_ASTC) {
56         status = dataManager_.GetSingleKvStore(options, KVSTORE_APPID, KVSTORE_YEAR_STOREID, kvStorePtr_);
57     } else if (valueType == KvStoreValueType::MONTH_ASTC_OLD_VERSION) {
58         status = dataManager_.GetSingleKvStore(options, KVSTORE_APPID, KVSTORE_MONTH_STOREID_OLD_VERSION, kvStorePtr_);
59     } else if (valueType == KvStoreValueType::YEAR_ASTC_OLD_VERSION) {
60         status = dataManager_.GetSingleKvStore(options, KVSTORE_APPID, KVSTORE_YEAR_STOREID_OLD_VERSION, kvStorePtr_);
61     } else {
62         MEDIA_ERR_LOG("invalid value type");
63         return E_ERR;
64     }
65 
66     if (status != Status::SUCCESS || kvStorePtr_ == nullptr) {
67         MEDIA_ERR_LOG("init KvStore failed, status %{public}d", status);
68         return static_cast<int32_t>(status);
69     }
70     return E_OK;
71 }
72 
Insert(const std::string & key,const std::vector<uint8_t> & value)73 int32_t MediaLibraryKvStore::Insert(const std::string &key, const std::vector<uint8_t> &value)
74 {
75     if (kvStorePtr_ == nullptr) {
76         MEDIA_ERR_LOG("kvStorePtr_ is nullptr");
77         return E_HAS_DB_ERROR;
78     }
79 
80     MediaLibraryTracer tracer;
81     tracer.Start("MediaLibraryKvStore::Insert");
82     Key k(key);
83     Value v(value);
84     Status status = kvStorePtr_->Put(k, v);
85     if (status != Status::SUCCESS) {
86         MEDIA_ERR_LOG("insert failed, status %{public}d", status);
87     }
88     return static_cast<int32_t>(status);
89 }
90 
GetCount(const std::string & key,int32_t & count)91 int32_t MediaLibraryKvStore::GetCount(const std::string& key, int32_t& count)
92 {
93     if (kvStorePtr_ == nullptr) {
94         MEDIA_ERR_LOG("kvStorePtr_ is nullptr");
95         return E_HAS_DB_ERROR;
96     }
97 
98     MediaLibraryTracer tracer;
99     tracer.Start("MediaLibraryKvStore::GetCount");
100     DataQuery dataQuery;
101     dataQuery.Between(key, key);
102     std::shared_ptr<KvStoreResultSet> output;
103     Status status = kvStorePtr_->GetResultSet(dataQuery, output);
104     if (status != Status::SUCCESS) {
105         MEDIA_ERR_LOG("delete failed, status %{public}d", status);
106         count = 0;
107     } else {
108         count = output->GetCount();
109     }
110     status = kvStorePtr_->CloseResultSet(output);
111     return static_cast<int32_t>(status);
112 }
113 
Delete(const std::string & key)114 int32_t MediaLibraryKvStore::Delete(const std::string &key)
115 {
116     if (kvStorePtr_ == nullptr) {
117         MEDIA_ERR_LOG("kvStorePtr_ is nullptr");
118         return E_HAS_DB_ERROR;
119     }
120 
121     MediaLibraryTracer tracer;
122     tracer.Start("MediaLibraryKvStore::Delete");
123     Key k(key);
124     Status status = kvStorePtr_->Delete(k);
125     if (status != Status::SUCCESS) {
126         MEDIA_ERR_LOG("delete failed, status %{public}d", status);
127     }
128     return static_cast<int32_t>(status);
129 }
130 
Query(const std::string & key,std::vector<uint8_t> & value)131 int32_t MediaLibraryKvStore::Query(const std::string &key, std::vector<uint8_t> &value)
132 {
133     if (kvStorePtr_ == nullptr) {
134         MEDIA_ERR_LOG("kvStorePtr_ is nullptr");
135         return E_HAS_DB_ERROR;
136     }
137 
138     MediaLibraryTracer tracer;
139     tracer.Start("MediaLibraryKvStore::Query");
140     std::vector<uint8_t> tmp;
141     Key k(key);
142     Value v(tmp);
143     Status status = kvStorePtr_->Get(k, v);
144     value = v.Data();
145     if (status != Status::SUCCESS) {
146         MEDIA_ERR_LOG("query failed, status %{public}d", status);
147     }
148     return static_cast<int32_t>(status);
149 }
150 
AddEmptyValue(std::vector<std::vector<uint8_t>> & values)151 void AddEmptyValue(std::vector<std::vector<uint8_t>> &values)
152 {
153     std::vector<uint8_t> value = {};
154     values.emplace_back(std::move(value));
155 }
156 
GenEmptyValues(std::vector<std::string> & batchKeys,std::vector<std::vector<uint8_t>> & values)157 void GenEmptyValues(std::vector<std::string> &batchKeys, std::vector<std::vector<uint8_t>> &values)
158 {
159     for (size_t i = 0; i < batchKeys.size(); i++) {
160         std::vector<uint8_t> value = {};
161         values.emplace_back(std::move(value));
162     }
163 }
164 
FillBatchValues(std::vector<std::string> & batchKeys,std::vector<std::vector<uint8_t>> & values,std::shared_ptr<DistributedKv::SingleKvStore> kvStorePtr)165 int32_t FillBatchValues(std::vector<std::string> &batchKeys, std::vector<std::vector<uint8_t>> &values,
166     std::shared_ptr<DistributedKv::SingleKvStore> kvStorePtr)
167 {
168     DataQuery dataQuery;
169     dataQuery.Between(batchKeys.back(), batchKeys.front());
170     std::shared_ptr<KvStoreResultSet> resultSet;
171     Status status = kvStorePtr->GetResultSet(dataQuery, resultSet);
172     if (status != Status::SUCCESS || resultSet == nullptr) {
173         MEDIA_ERR_LOG("GetResultSet error occur, status: %{public}d", status);
174         return static_cast<int32_t>(status);
175     }
176 
177     if (!resultSet->MoveToNext()) {
178         // This may happen if all images in this group is not generated.
179         MEDIA_ERR_LOG("ResultSet is empty.");
180         GenEmptyValues(batchKeys, values);
181         return static_cast<int32_t>(status);
182     }
183     auto begin = batchKeys.crbegin();
184     auto end = batchKeys.crend();
185     bool isEndOfResultSet = false;
186     while (begin != end) {
187         if (isEndOfResultSet) {
188             AddEmptyValue(values);
189             ++begin;
190             continue;
191         }
192         Entry entry;
193         status = resultSet->GetEntry(entry);
194         if (status != Status::SUCCESS) {
195             MEDIA_ERR_LOG("GetEntry error occur, status: %{public}d", status);
196             return static_cast<int32_t>(status);
197         }
198 
199         int result = std::strcmp(entry.key.ToString().c_str(), (*begin).c_str());
200         if (result == 0) {
201             std::vector<uint8_t>&& value = entry.value;
202             values.emplace_back(std::move(value));
203             ++begin;
204             if (!resultSet->MoveToNext()) {
205                 isEndOfResultSet = true;
206             }
207         } else if (result < 0) {
208             // This may happen if image is hidden or trashed by user.
209             if (!resultSet->MoveToNext()) {
210                 isEndOfResultSet = true;
211             }
212         } else {
213             // This may happen if image is not generated.
214             AddEmptyValue(values);
215             ++begin;
216         }
217     }
218     status = kvStorePtr->CloseResultSet(resultSet);
219     return static_cast<int32_t>(status);
220 }
221 
BatchQuery(std::vector<std::string> & batchKeys,std::vector<std::vector<uint8_t>> & values)222 int32_t MediaLibraryKvStore::BatchQuery(
223     std::vector<std::string> &batchKeys, std::vector<std::vector<uint8_t>> &values)
224 {
225     if (kvStorePtr_ == nullptr) {
226         MEDIA_ERR_LOG("kvStorePtr_ is nullptr");
227         return E_HAS_DB_ERROR;
228     }
229 
230     if (batchKeys.empty()) {
231         MEDIA_ERR_LOG("batchKeys is empty");
232         return E_ERR;
233     }
234 
235     std::sort(batchKeys.begin(), batchKeys.end(), [](std::string a, std::string b) {return a > b;});
236     MediaLibraryTracer tracer;
237     tracer.Start("MediaLibraryKvStore::BatchQuery");
238     return FillBatchValues(batchKeys, values, kvStorePtr_);
239 }
240 
Close()241 bool MediaLibraryKvStore::Close()
242 {
243     MediaLibraryTracer tracer;
244     tracer.Start("MediaLibraryKvStore::Close");
245     if (kvStorePtr_ == nullptr) {
246         MEDIA_ERR_LOG("kvStorePtr_ is nullptr");
247         return true;
248     }
249 
250     Status status = dataManager_.CloseKvStore(KVSTORE_APPID, kvStorePtr_);
251     if (status != Status::SUCCESS) {
252         MEDIA_ERR_LOG("close KvStore failed, status %{public}d", status);
253         return false;
254     }
255     kvStorePtr_ = nullptr;
256     return true;
257 }
258 
GetKvStoreOption(DistributedKv::Options & options,const KvStoreRoleType & roleType,const std::string & baseDir)259 bool MediaLibraryKvStore::GetKvStoreOption(
260     DistributedKv::Options &options, const KvStoreRoleType &roleType, const std::string &baseDir)
261 {
262     if (roleType == KvStoreRoleType::OWNER) {
263         options.createIfMissing = true;
264         options.role = RoleType::OWNER;
265     } else if (roleType == KvStoreRoleType::VISITOR) {
266         options.createIfMissing = false;
267         options.role = RoleType::VISITOR;
268     } else {
269         MEDIA_ERR_LOG("GetKvStoreOption invalid role");
270         return false;
271     }
272     options.group.groupDir = baseDir;
273     options.encrypt = false;
274     options.backup = false;
275     options.autoSync = false;
276     options.securityLevel = SecurityLevel::S3;
277     options.kvStoreType = KvStoreType::LOCAL_ONLY;
278     return true;
279 }
280 
RebuildKvStore(const KvStoreValueType & valueType,const std::string & baseDir)281 int32_t MediaLibraryKvStore::RebuildKvStore(const KvStoreValueType &valueType, const std::string &baseDir)
282 {
283     MEDIA_INFO_LOG("Start RebuildKvStore, type %{public}d", valueType);
284     MediaLibraryTracer tracer;
285     tracer.Start("MediaLibraryKvStore::RebuildKvStore");
286     Status status;
287     tracer.Start("DeleteKvStore");
288     if (valueType == KvStoreValueType::MONTH_ASTC) {
289         status = dataManager_.DeleteKvStore(KVSTORE_APPID, KVSTORE_MONTH_STOREID, baseDir);
290     } else if (valueType == KvStoreValueType::YEAR_ASTC) {
291         status = dataManager_.DeleteKvStore(KVSTORE_APPID, KVSTORE_YEAR_STOREID, baseDir);
292     } else {
293         MEDIA_ERR_LOG("Invalid value type: %{public}d", valueType);
294         return E_ERR;
295     }
296     tracer.Finish();
297 
298     if (status != Status::SUCCESS) {
299         MEDIA_ERR_LOG("Delete kvstore failed, type %{public}d, status %{public}d", valueType, status);
300         return static_cast<int32_t>(status);
301     }
302 
303     int32_t err = Init(KvStoreRoleType::OWNER, valueType, baseDir);
304     if (err != E_OK) {
305         MEDIA_ERR_LOG("Rebuild kvstore failed, type %{public}d, status %{public}d", valueType, err);
306         return err;
307     }
308 
309     if (!Close()) {
310         return E_ERR;
311     }
312     MEDIA_INFO_LOG("RebuildKvStore finish, type %{public}d", valueType);
313     return E_OK;
314 }
315 
BatchInsert(const std::vector<DistributedKv::Entry> & entries)316 int32_t MediaLibraryKvStore::BatchInsert(const std::vector<DistributedKv::Entry> &entries)
317 {
318     if (kvStorePtr_ == nullptr) {
319         MEDIA_ERR_LOG("KvStorePtr is nullptr");
320         return E_HAS_DB_ERROR;
321     }
322     if (entries.empty()) {
323         MEDIA_ERR_LOG("Entries is empty");
324         return E_ERR;
325     }
326 
327     MediaLibraryTracer tracer;
328     tracer.Start("MediaLibraryKvStore::BatchInsert");
329     Status status = kvStorePtr_->PutBatch(entries);
330     if (status != Status::SUCCESS) {
331         MEDIA_ERR_LOG("Batch insert failed, status %{public}d", status);
332         return static_cast<int32_t>(status);
333     }
334     return E_OK;
335 }
336 
InitSingleKvstore(const KvStoreRoleType & roleType,const std::string & storeId,const std::string & baseDir)337 int32_t MediaLibraryKvStore::InitSingleKvstore(const KvStoreRoleType &roleType,
338     const std::string &storeId, const std::string &baseDir)
339 {
340     MediaLibraryTracer tracer;
341     tracer.Start("MediaLibraryKvStore::InitKvStore");
342     Options options;
343     if (roleType == KvStoreRoleType::OWNER) {
344         options.createIfMissing = true;
345         options.role = RoleType::OWNER;
346     } else if (roleType == KvStoreRoleType::VISITOR) {
347         options.createIfMissing = false;
348         options.role = RoleType::VISITOR;
349     } else {
350         MEDIA_ERR_LOG("GetKvStoreOption invalid role");
351         return E_ERR;
352     }
353     options.group.groupDir = baseDir;
354     options.encrypt = false;
355     options.backup = false;
356     options.autoSync = false;
357     options.securityLevel = SecurityLevel::S3;
358     options.kvStoreType = KvStoreType::LOCAL_ONLY;
359 
360     MEDIA_INFO_LOG("InitKvStore baseDir %{public}s", options.group.groupDir.c_str());
361     Status status = dataManager_.GetSingleKvStore(options, KVSTORE_APPID, {storeId}, kvStorePtr_);
362     if (status != Status::SUCCESS) {
363         MEDIA_ERR_LOG("Init kvstore failed, status:%{public}d", status);
364         return static_cast<int32_t>(status);
365     }
366     return E_OK;
367 }
368 
PutAllValueToNewKvStore(std::shared_ptr<MediaLibraryKvStore> & newKvstore)369 int32_t MediaLibraryKvStore::PutAllValueToNewKvStore(std::shared_ptr<MediaLibraryKvStore> &newKvstore)
370 {
371     if (kvStorePtr_ == nullptr) {
372         MEDIA_ERR_LOG("KvStorePtr is nullptr");
373         return E_HAS_DB_ERROR;
374     }
375 
376     MediaLibraryTracer tracer;
377     tracer.Start("MediaLibraryKvStore::PutAllValueToNewKvStore");
378     MEDIA_INFO_LOG("Start PutAllValueToNewKvStore");
379     DataQuery dataQuery;
380     dataQuery.Between("", "Z");
381     std::shared_ptr<KvStoreResultSet> resultSet;
382     Status status = kvStorePtr_->GetResultSet(dataQuery, resultSet);
383     if (status != Status::SUCCESS || resultSet == nullptr) {
384         MEDIA_ERR_LOG("GetResultSet error occur, status: %{public}d", status);
385         return static_cast<int32_t>(status);
386     }
387 
388     std::vector<Entry> entryList;
389     while (resultSet->MoveToNext()) {
390         Entry entry;
391         status = resultSet->GetEntry(entry);
392         if (status != Status::SUCCESS) {
393             MEDIA_ERR_LOG("GetEntry error occur, status: %{public}d", status);
394             return static_cast<int32_t>(status);
395         }
396 
397         entryList.emplace_back(std::move(entry));
398         if (entryList.size() >= KVSTORE_MAX_NUMBER_BATCH_INSERT) {
399             newKvstore->BatchInsert(entryList);
400             entryList.clear();
401         }
402     }
403     if (!entryList.empty()) {
404         newKvstore->BatchInsert(entryList);
405     }
406     status = kvStorePtr_->CloseResultSet(resultSet);
407     MEDIA_INFO_LOG("End PutAllValueToNewKvStore");
408     return static_cast<int32_t>(status);
409 }
410 } // namespace OHOS::Media