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 "sqlite_multi_ver_data_storage.h"
18 
19 #include <climits>
20 #include <string>
21 #include <vector>
22 #include <algorithm>
23 
24 #include "db_constant.h"
25 #include "db_types.h"
26 #include "log_print.h"
27 #include "sqlite_utils.h"
28 #include "multi_ver_kv_entry.h"
29 #include "multi_ver_value_object.h"
30 #include "value_hash_calc.h"
31 #include "db_common.h"
32 #include "multi_ver_natural_store.h"
33 #include "platform_specific.h"
34 
35 namespace DistributedDB {
36 namespace {
37     const std::string CREATE_TABLE_SQL =
38         "CREATE TABLE IF NOT EXISTS version_data(key BLOB, value BLOB, oper_flag INTEGER, version INTEGER, " \
39         "timestamp INTEGER, ori_timestamp INTEGER, hash_key BLOB, " \
40         "PRIMARY key(hash_key, version));";
41     const std::string CREATE_TABLE_VERSION_INDEX_SQL =
42         "CREATE INDEX IF NOT EXISTS version_index ON version_data (version);";
43     const std::string CREATE_TABLE_FLAG_INDEX_SQL =
44         "CREATE INDEX IF NOT EXISTS flag_index ON version_data (oper_flag);";
45 
46     const std::size_t MAX_READ_CONNECT_NUM = 16;
47 }
48 
SQLiteMultiVerDataStorage()49 SQLiteMultiVerDataStorage::SQLiteMultiVerDataStorage()
50     : writeTransaction_(nullptr),
51       writeTransactionUsed_(false)
52 {}
53 
~SQLiteMultiVerDataStorage()54 SQLiteMultiVerDataStorage::~SQLiteMultiVerDataStorage()
55 {
56     writeTransaction_ = nullptr;
57 }
58 
CheckVersion(const Property & property,bool & isDbExist) const59 int SQLiteMultiVerDataStorage::CheckVersion(const Property &property, bool &isDbExist) const
60 {
61     int dbVer = 0;
62     int errCode = GetVersion(property, dbVer, isDbExist);
63     if (errCode != E_OK) {
64         LOGE("[DataStorage][CheckVer] GetVersion failed, errCode=%d.", errCode);
65         return errCode;
66     }
67     if (!isDbExist) {
68         return E_OK;
69     }
70     LOGD("[DataStorage][CheckVer] DbVersion=%d, CurVersion=%d.", dbVer, MULTI_VER_DATA_STORAGE_VERSION_CURRENT);
71     if (dbVer > MULTI_VER_DATA_STORAGE_VERSION_CURRENT) {
72         LOGE("[DataStorage][CheckVer] Version Not Support!");
73         return -E_VERSION_NOT_SUPPORT;
74     }
75     return E_OK;
76 }
77 
GetVersion(const Property & property,int & version,bool & isDbExisted) const78 int SQLiteMultiVerDataStorage::GetVersion(const Property &property, int &version, bool &isDbExisted) const
79 {
80     std::string uri = property.path + "/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" +
81         DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
82     isDbExisted = OS::CheckPathExistence(uri);
83     if (isDbExisted) {
84         std::vector<std::string> tableVect;
85         OpenDbProperties option = {uri, property.isNeedCreate, false, tableVect, property.cipherType, property.passwd};
86         return SQLiteUtils::GetVersion(option, version);
87     }
88     return E_OK;
89 }
90 
Open(const Property & property)91 int SQLiteMultiVerDataStorage::Open(const Property &property)
92 {
93     // only set the property para or create the database and the table?
94     // whether create the transactions.
95     property_ = property;
96     uri_ = property.path + "/" + property_.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" +
97         DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
98     std::vector<std::string> tableVect;
99     tableVect.push_back(CREATE_TABLE_SQL);
100     tableVect.push_back(CREATE_TABLE_VERSION_INDEX_SQL);
101     tableVect.push_back(CREATE_TABLE_FLAG_INDEX_SQL);
102 
103     OpenDbProperties option = {uri_, property.isNeedCreate, false, tableVect, property.cipherType, property.passwd};
104     sqlite3 *db = nullptr;
105     int errCode = SQLiteUtils::OpenDatabase(option, db);
106     if (errCode != E_OK) {
107         LOGE("Open the multi ver data store error:%d", errCode);
108         goto END;
109     }
110 
111     // Version had been check before open and currently no upgrade to do
112     errCode = SQLiteUtils::SetUserVer(option, MULTI_VER_DATA_STORAGE_VERSION_CURRENT);
113     if (errCode != E_OK) {
114         LOGE("Init the version multi ver store error:%d", errCode);
115     }
116 
117 END:
118     if (db != nullptr) {
119         (void)sqlite3_close_v2(db);
120         db = nullptr;
121     }
122 
123     return errCode;
124 }
125 
126 // start one write transaction
127 // do the transaction initialization and call the start transaction;
StartWrite(KvDataType dataType,IKvDBMultiVerTransaction * & transaction)128 int SQLiteMultiVerDataStorage::StartWrite(KvDataType dataType, IKvDBMultiVerTransaction *&transaction)
129 {
130     (void)dataType;
131     std::unique_lock<std::mutex> lock(transactionMutex_);
132     // if same thread. return nullptr.
133     if (std::this_thread::get_id() == writeHolderId_) {
134         transaction = nullptr;
135         return -E_BUSY;
136     }
137 
138     if (writeTransaction_ != nullptr) {
139         writeCondition_.wait(lock, [&] {
140             return !writeTransactionUsed_;
141         });
142 
143         writeTransactionUsed_ = true;
144         writeHolderId_ = std::this_thread::get_id();
145         transaction = writeTransaction_;
146         return E_OK;
147     }
148 
149     transaction = new (std::nothrow) SQLiteMultiVerTransaction();
150     if (transaction == nullptr) {
151         LOGE("Failed to create the SQLite write transaction");
152         return -E_OUT_OF_MEMORY;
153     }
154 
155     // initialize the transaction.
156     int errCode = static_cast<SQLiteMultiVerTransaction *>(transaction)->Initialize(uri_, false,
157         property_.cipherType, property_.passwd);
158     if (errCode != E_OK) {
159         LOGE("Init write transaction failed:%d", errCode);
160         delete transaction;
161         transaction = nullptr;
162         return errCode;
163     }
164 
165     writeTransaction_ = static_cast<SQLiteMultiVerTransaction *>(transaction);
166     writeTransactionUsed_ = true;
167     writeHolderId_ = std::this_thread::get_id();
168     return E_OK;
169 }
170 
171 // do the first step of commit record.
172 // commit the transaction, and avoid other operation reading the new data.
CommitWritePhaseOne(IKvDBMultiVerTransaction * transaction,const UpdateVerTimestamp & multiVerTimestamp)173 int SQLiteMultiVerDataStorage::CommitWritePhaseOne(IKvDBMultiVerTransaction *transaction,
174     const UpdateVerTimestamp &multiVerTimestamp)
175 {
176     if (transaction == nullptr) {
177         LOGE("Invalid transaction!");
178         return -E_INVALID_DB;
179     }
180     // Get versionInfo from transaction.
181     // Call the commit of the sqlite.
182     Version versionInfo = transaction->GetVersion();
183 
184     if (multiVerTimestamp.isNeedUpdate) {
185         (void)transaction->UpdateTimestampByVersion(versionInfo, multiVerTimestamp.timestamp);
186     }
187 
188     int errCode = transaction->CommitTransaction();
189     if (errCode != E_OK) {
190         auto sqliteTransaction = static_cast<SQLiteMultiVerTransaction *>(transaction);
191         if (transaction != nullptr) {
192             (void)sqliteTransaction->Reset(property_.cipherType, property_.passwd);
193         }
194         LOGE("SQLite commit the transaction failed:%d", errCode);
195     }
196     return errCode;
197 }
198 
199 // when the commit history update failed, need delete the commit
RollbackWritePhaseOne(IKvDBMultiVerTransaction * transaction,const Version & versionInfo)200 int SQLiteMultiVerDataStorage::RollbackWritePhaseOne(IKvDBMultiVerTransaction *transaction,
201     const Version &versionInfo)
202 {
203     if (transaction == nullptr) {
204         LOGE("Invalid transaction!");
205         return -E_INVALID_DB;
206     }
207 
208     SQLiteMultiVerTransaction *sqliteTransaction = static_cast<SQLiteMultiVerTransaction *>(transaction);
209     sqliteTransaction->StartTransaction();
210     int errCode = sqliteTransaction->ClearEntriesByVersion(versionInfo);
211     if (errCode == E_OK) {
212         sqliteTransaction->CommitTransaction();
213     } else {
214         sqliteTransaction->RollBackTransaction();
215     }
216 
217     return errCode;
218 }
219 
220 // Rollback the write transaction.
RollbackWrite(IKvDBMultiVerTransaction * transaction)221 int SQLiteMultiVerDataStorage::RollbackWrite(IKvDBMultiVerTransaction *transaction)
222 {
223     if (transaction == nullptr) {
224         LOGE("Invalid transaction!");
225         return -E_INVALID_DB;
226     }
227     // call the rollback of the sqlite.
228     int errCode = static_cast<SQLiteMultiVerTransaction *>(transaction)->RollBackTransaction();
229     if (errCode != E_OK) {
230         (void)static_cast<SQLiteMultiVerTransaction *>(transaction)->Reset(property_.cipherType, property_.passwd);
231         LOGE("SQLite rollback failed:%d", errCode);
232     }
233     return errCode;
234 }
235 
236 // should update the flag indicated that other operating could read the new record.
CommitWritePhaseTwo(IKvDBMultiVerTransaction * transaction)237 void SQLiteMultiVerDataStorage::CommitWritePhaseTwo(IKvDBMultiVerTransaction *transaction)
238 {
239     // just change the head version?
240     (void)transaction;
241 }
242 
243 // Get one start transaction.
StartRead(KvDataType dataType,const Version & versionInfo,int & errCode)244 IKvDBMultiVerTransaction *SQLiteMultiVerDataStorage::StartRead(KvDataType dataType,
245     const Version &versionInfo, int &errCode)
246 {
247     (void)dataType;
248     std::unique_lock<std::mutex> lock(transactionMutex_);
249     for (auto &iter : readTransactions_) {
250         if (iter.second) {
251             iter.second = false;
252             (iter.first)->SetVersion(versionInfo);
253             errCode = E_OK;
254             return iter.first;
255         }
256     }
257     // need wait.
258     if (readTransactions_.size() > MAX_READ_CONNECT_NUM) {
259         LOGE("Over the max transaction num");
260         errCode = -E_BUSY;
261         return nullptr;
262     }
263 
264     IKvDBMultiVerTransaction *transaction = new (std::nothrow) SQLiteMultiVerTransaction;
265     if (transaction == nullptr) {
266         errCode = -E_OUT_OF_MEMORY;
267         return nullptr;
268     }
269     errCode = static_cast<SQLiteMultiVerTransaction *>(transaction)->Initialize(uri_,
270         true, property_.cipherType, property_.passwd);
271     if (errCode != E_OK) {
272         delete transaction;
273         transaction = nullptr;
274         return nullptr;
275     }
276 
277     transaction->SetVersion(versionInfo);
278     readTransactions_.insert(std::make_pair(transaction, false));
279     return transaction;
280 }
281 
282 // Release the transaction created.
ReleaseTransaction(IKvDBMultiVerTransaction * transaction)283 void SQLiteMultiVerDataStorage::ReleaseTransaction(IKvDBMultiVerTransaction *transaction)
284 {
285     // whether need manage the transaction.
286     std::unique_lock<std::mutex> lock(transactionMutex_);
287     if (transaction == nullptr) {
288         LOGE("Invalid transaction!");
289         return;
290     }
291 
292     if (transaction == writeTransaction_) {
293         static_cast<SQLiteMultiVerTransaction *>(writeTransaction_)->ResetVersion();
294         writeTransactionUsed_ = false;
295         writeHolderId_ = std::thread::id();
296         writeCondition_.notify_all();
297         return;
298     }
299 
300     auto iter = readTransactions_.find(transaction);
301     if (iter != readTransactions_.end()) {
302         static_cast<SQLiteMultiVerTransaction *>(iter->first)->ResetVersion();
303         iter->second = true;
304     }
305     return;
306 }
307 
Close()308 void SQLiteMultiVerDataStorage::Close()
309 {
310     std::lock_guard<std::mutex> lock(transactionMutex_);
311     // close all the transaction?
312     for (auto iter = readTransactions_.begin(); iter != readTransactions_.end(); iter++) {
313         if (iter->first != nullptr) {
314             delete iter->first;
315         }
316     }
317     readTransactions_.clear();
318 
319     if (writeTransaction_ != nullptr) {
320         delete writeTransaction_;
321         writeTransaction_ = nullptr;
322     }
323 }
324 
RunRekeyLogic(CipherType type,const CipherPassword & passwd)325 int SQLiteMultiVerDataStorage::RunRekeyLogic(CipherType type, const CipherPassword &passwd)
326 {
327     (void)type;
328     // openDatabase to get the sqlite3 pointer
329     std::vector<std::string> tableVect;
330     tableVect.push_back(CREATE_TABLE_SQL);
331     sqlite3 *db = nullptr;
332     OpenDbProperties option = {uri_, property_.isNeedCreate, false, tableVect, property_.cipherType, property_.passwd};
333     int errCode = SQLiteUtils::OpenDatabase(option, db);
334     if (errCode != E_OK) {
335         LOGE("Open db error:%d", errCode);
336         return errCode;
337     }
338 
339     // execute rekey
340     errCode = SQLiteUtils::Rekey(db, passwd);
341     if (errCode != E_OK) {
342         LOGE("multi ver data rekey failed:%d", errCode);
343     }
344     // close db
345     (void)sqlite3_close_v2(db);
346     db = nullptr;
347 
348     return errCode;
349 }
350 
RunExportLogic(CipherType type,const CipherPassword & passwd,const std::string & dbDir)351 int SQLiteMultiVerDataStorage::RunExportLogic(CipherType type, const CipherPassword &passwd, const std::string &dbDir)
352 {
353     // openDatabase to get the sqlite3 pointer
354     std::vector<std::string> tableVect;
355     sqlite3 *db = nullptr;
356     OpenDbProperties option = {uri_, true, false, tableVect, property_.cipherType, property_.passwd};
357     int errCode = SQLiteUtils::OpenDatabase(option, db);
358     if (errCode != E_OK) {
359         LOGE("Open db error:%d", errCode);
360         return errCode;
361     }
362 
363     // execute export
364     std::string newDbName = dbDir + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
365     errCode = SQLiteUtils::ExportDatabase(db, type, passwd, newDbName);
366     if (errCode != E_OK) {
367         LOGE("multi ver data export failed:%d", errCode);
368     }
369     // close db
370     (void)sqlite3_close_v2(db);
371     db = nullptr;
372 
373     return errCode;
374 }
375 
BackupCurrentDatabase(const Property & property,const std::string & dir)376 int SQLiteMultiVerDataStorage::BackupCurrentDatabase(const Property &property, const std::string &dir)
377 {
378     std::string currentDb = property.path + "/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" +
379         DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
380     std::string dstDb = dir + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
381     int errCode = DBCommon::CopyFile(currentDb, dstDb);
382     if (errCode != E_OK) {
383         LOGE("Copy the local current db error:%d", errCode);
384     }
385     return errCode;
386 }
387 
ImportDatabase(const Property & property,const std::string & dir,const CipherPassword & passwd)388 int SQLiteMultiVerDataStorage::ImportDatabase(const Property &property, const std::string &dir,
389     const CipherPassword &passwd)
390 {
391     std::string currentDb = property.path + "/" + property.identifierName + "/" + DBConstant::MULTI_SUB_DIR + "/" +
392         DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
393     std::string srcDb = dir + "/" + DBConstant::MULTI_VER_DATA_STORE + DBConstant::DB_EXTENSION;
394     int errCode = SQLiteUtils::ExportDatabase(srcDb, property.cipherType, passwd, currentDb, property.passwd);
395     if (errCode != E_OK) {
396         LOGE("import the multi ver data db error:%d", errCode);
397     }
398     return E_OK;
399 }
400 } // namespace DistributedDB
401 #endif