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 #ifdef RELATIONAL_STORE
16 #include "relational_store_instance.h"
17
18 #include "db_common.h"
19 #include "db_dfx_adapter.h"
20 #include "db_errno.h"
21 #include "sqlite_relational_store.h"
22 #include "log_print.h"
23
24 namespace DistributedDB {
25 RelationalStoreInstance *RelationalStoreInstance::instance_ = nullptr;
26 std::mutex RelationalStoreInstance::instanceLock_;
27
28 static std::mutex storeLock_;
29 static std::map<std::string, IRelationalStore *> dbs_;
30
RelationalStoreInstance()31 RelationalStoreInstance::RelationalStoreInstance()
32 {}
33
GetInstance()34 RelationalStoreInstance *RelationalStoreInstance::GetInstance()
35 {
36 std::lock_guard<std::mutex> lockGuard(instanceLock_);
37 if (instance_ == nullptr) {
38 instance_ = new (std::nothrow) RelationalStoreInstance();
39 if (instance_ == nullptr) {
40 LOGE("failed to new RelationalStoreManager!");
41 return nullptr;
42 }
43 }
44 return instance_;
45 }
46
ReleaseDataBaseConnection(RelationalStoreConnection * connection)47 int RelationalStoreInstance::ReleaseDataBaseConnection(RelationalStoreConnection *connection)
48 {
49 if (connection == nullptr) {
50 return -E_INVALID_DB;
51 }
52 auto manager = RelationalStoreInstance::GetInstance();
53 if (manager == nullptr) {
54 return -E_OUT_OF_MEMORY;
55 }
56 std::string identifier = connection->GetIdentifier();
57 manager->EnterDBOpenCloseProcess(identifier);
58 int errCode = connection->Close();
59 manager->ExitDBOpenCloseProcess(identifier);
60
61 if (errCode != E_OK) {
62 LOGE("Release db connection failed. %d", errCode);
63 }
64 return errCode;
65 }
66
GetFromCache(const RelationalDBProperties & properties,int & errCode)67 static IRelationalStore *GetFromCache(const RelationalDBProperties &properties, int &errCode)
68 {
69 errCode = E_OK;
70 std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, "");
71 std::lock_guard<std::mutex> lockGuard(storeLock_);
72 auto iter = dbs_.find(identifier);
73 if (iter == dbs_.end()) {
74 errCode = -E_NOT_FOUND;
75 return nullptr;
76 }
77
78 auto *db = iter->second;
79 if (db == nullptr) {
80 LOGE("Store cache is nullptr, there may be a logic error");
81 errCode = -E_INTERNAL_ERROR;
82 return nullptr;
83 }
84 db->IncObjRef(db);
85 return db;
86 }
87
88 // Save to IKvDB to the global map
RemoveKvDBFromCache(const RelationalDBProperties & properties)89 void RelationalStoreInstance::RemoveKvDBFromCache(const RelationalDBProperties &properties)
90 {
91 std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, "");
92 std::lock_guard<std::mutex> lockGuard(storeLock_);
93 dbs_.erase(identifier);
94 }
95
SaveRelationalDBToCache(IRelationalStore * store,const RelationalDBProperties & properties)96 void RelationalStoreInstance::SaveRelationalDBToCache(IRelationalStore *store, const RelationalDBProperties &properties)
97 {
98 std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, "");
99 std::lock_guard<std::mutex> lockGuard(storeLock_);
100 if (dbs_.count(identifier) == 0) {
101 dbs_.insert(std::pair<std::string, IRelationalStore *>(identifier, store));
102 }
103 }
104
OpenDatabase(const RelationalDBProperties & properties,int & errCode)105 IRelationalStore *RelationalStoreInstance::OpenDatabase(const RelationalDBProperties &properties, int &errCode)
106 {
107 auto db = new (std::nothrow) SQLiteRelationalStore();
108 if (db == nullptr) {
109 errCode = -E_OUT_OF_MEMORY;
110 LOGE("Failed to get relational store! err:%d", errCode);
111 return nullptr;
112 }
113
114 db->OnClose([this, properties]() {
115 LOGI("Remove from the cache");
116 this->RemoveKvDBFromCache(properties);
117 });
118
119 errCode = db->Open(properties);
120 if (errCode != E_OK) {
121 LOGE("Failed to open db! err:%d", errCode);
122 RefObject::KillAndDecObjRef(db);
123 return nullptr;
124 }
125 db->WakeUpSyncer();
126
127 SaveRelationalDBToCache(db, properties);
128 return db;
129 }
130
GetDataBase(const RelationalDBProperties & properties,int & errCode,bool isNeedIfOpened)131 IRelationalStore *RelationalStoreInstance::GetDataBase(const RelationalDBProperties &properties, int &errCode,
132 bool isNeedIfOpened)
133 {
134 auto *db = GetFromCache(properties, errCode);
135 if (db != nullptr && !isNeedIfOpened) {
136 RefObject::DecObjRef(db);
137 errCode = -E_ALREADY_OPENED;
138 LOGI("Database has already been opened.");
139 return nullptr;
140 } else if (db != nullptr) {
141 LOGD("Get db from cache.");
142 return db;
143 }
144
145 // file lock
146 RelationalStoreInstance *manager = RelationalStoreInstance::GetInstance();
147 if (manager == nullptr) {
148 errCode = -E_OUT_OF_MEMORY;
149 return nullptr;
150 }
151
152 db = manager->OpenDatabase(properties, errCode);
153 if (errCode != E_OK) {
154 LOGE("Create database failed, errCode = [%d]", errCode);
155 }
156 return db;
157 }
158
159 namespace {
CheckCompatibility(const RelationalDBProperties & prop,const RelationalDBProperties & existedProp)160 int CheckCompatibility(const RelationalDBProperties &prop, const RelationalDBProperties &existedProp)
161 {
162 std::string canonicalDir = prop.GetStringProp(DBProperties::DATA_DIR, "");
163 if (canonicalDir.empty() || canonicalDir != existedProp.GetStringProp(DBProperties::DATA_DIR, "")) {
164 LOGE("Failed to check store path, the input path does not match with cached store.");
165 return -E_INVALID_ARGS;
166 }
167 if (prop.GetIntProp(RelationalDBProperties::DISTRIBUTED_TABLE_MODE, DistributedTableMode::SPLIT_BY_DEVICE) !=
168 existedProp.GetIntProp(RelationalDBProperties::DISTRIBUTED_TABLE_MODE, DistributedTableMode::SPLIT_BY_DEVICE)) {
169 LOGE("Failed to check table mode.");
170 return -E_INVALID_ARGS;
171 }
172
173 if (prop.IsEncrypted() != existedProp.IsEncrypted()) {
174 LOGE("Failed to check cipher args.");
175 return -E_INVALID_PASSWD_OR_CORRUPTED_DB;
176 }
177 if (prop.IsEncrypted() &&
178 (prop.GetPasswd() != existedProp.GetPasswd() || prop.GetIterTimes() != existedProp.GetIterTimes() ||
179 !DBCommon::IsSameCipher(prop.GetCipherType(), existedProp.GetCipherType()))) {
180 LOGE("Failed to check cipher args.");
181 return -E_INVALID_PASSWD_OR_CORRUPTED_DB;
182 }
183
184 if (prop.GetBoolProp(DBProperties::SYNC_DUAL_TUPLE_MODE, false) !=
185 existedProp.GetBoolProp(DBProperties::SYNC_DUAL_TUPLE_MODE, false)) {
186 LOGE("Failed to check dual tuple sync mode for rdb.");
187 return -E_MODE_MISMATCH;
188 }
189 return E_OK;
190 }
191 }
192
GetDatabaseConnection(const RelationalDBProperties & properties,int & errCode,bool isNeedIfOpened)193 RelationalStoreConnection *RelationalStoreInstance::GetDatabaseConnection(const RelationalDBProperties &properties,
194 int &errCode, bool isNeedIfOpened)
195 {
196 std::string identifier = properties.GetStringProp(DBProperties::IDENTIFIER_DATA, "");
197 LOGD("Begin to get [%s] database connection.", STR_MASK(DBCommon::TransferStringToHex(identifier)));
198 RelationalStoreInstance *manager = RelationalStoreInstance::GetInstance();
199 if (manager == nullptr) {
200 errCode = -E_OUT_OF_MEMORY;
201 return nullptr;
202 }
203 manager->EnterDBOpenCloseProcess(properties.GetStringProp(DBProperties::IDENTIFIER_DATA, ""));
204 RelationalStoreConnection *connection = nullptr;
205 IRelationalStore *db = GetDataBase(properties, errCode, isNeedIfOpened);
206 if (db == nullptr) {
207 DBDfxAdapter::ReportBehavior(
208 {__func__, Scene::OPEN_CONN, State::BEGIN, Stage::GET_DB, StageResult::FAIL, errCode});
209 LOGE("Failed to open the db:%d", errCode);
210 goto END;
211 }
212
213 errCode = CheckCompatibility(properties, db->GetProperties());
214 if (errCode != E_OK) {
215 DBDfxAdapter::ReportBehavior(
216 {__func__, Scene::OPEN_CONN, State::BEGIN, Stage::CHECK_OPT, StageResult::FAIL, errCode});
217 goto END;
218 }
219
220 connection = db->GetDBConnection(errCode);
221 if (connection == nullptr) { // not kill db, Other operations like import may be used concurrently
222 DBDfxAdapter::ReportBehavior(
223 {__func__, Scene::OPEN_CONN, State::BEGIN, Stage::GET_DB_CONN, StageResult::FAIL, errCode});
224 LOGE("Failed to get the db connect for delegate:%d", errCode);
225 }
226
227 END:
228 RefObject::DecObjRef(db); // restore the reference increased by the cache.
229 manager->ExitDBOpenCloseProcess(properties.GetStringProp(DBProperties::IDENTIFIER_DATA, ""));
230 return connection;
231 }
232
EnterDBOpenCloseProcess(const std::string & identifier)233 void RelationalStoreInstance::EnterDBOpenCloseProcess(const std::string &identifier)
234 {
235 std::unique_lock<std::mutex> lock(relationalDBOpenMutex_);
236 relationalDBOpenCondition_.wait(lock, [this, &identifier]() {
237 return this->relationalDBOpenSet_.count(identifier) == 0;
238 });
239 (void)relationalDBOpenSet_.insert(identifier);
240 }
241
ExitDBOpenCloseProcess(const std::string & identifier)242 void RelationalStoreInstance::ExitDBOpenCloseProcess(const std::string &identifier)
243 {
244 std::unique_lock<std::mutex> lock(relationalDBOpenMutex_);
245 (void)relationalDBOpenSet_.erase(identifier);
246 relationalDBOpenCondition_.notify_all();
247 }
248
Dump(int fd)249 void RelationalStoreInstance::Dump(int fd)
250 {
251 std::lock_guard<std::mutex> autoLock(storeLock_);
252 for (const auto &entry : dbs_) {
253 RefObject::IncObjRef(entry.second);
254 entry.second->Dump(fd);
255 RefObject::DecObjRef(entry.second);
256 }
257 }
258 } // namespace DistributedDB
259 #endif