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 <thread>
19 #include <algorithm>
20
21 #include "db_common.h"
22 #include "db_errno.h"
23 #include "sqlite_relational_store.h"
24 #include "log_print.h"
25
26 namespace DistributedDB {
27 RelationalStoreInstance *RelationalStoreInstance::instance_ = nullptr;
28 std::mutex RelationalStoreInstance::instanceLock_;
29
30 static std::mutex storeLock_;
31 static std::map<std::string, IRelationalStore *> dbs_;
32
RelationalStoreInstance()33 RelationalStoreInstance::RelationalStoreInstance()
34 {}
35
GetInstance()36 RelationalStoreInstance *RelationalStoreInstance::GetInstance()
37 {
38 std::lock_guard<std::mutex> lockGuard(instanceLock_);
39 if (instance_ == nullptr) {
40 instance_ = new (std::nothrow) RelationalStoreInstance();
41 if (instance_ == nullptr) {
42 LOGE("failed to new RelationalStoreManager!");
43 return nullptr;
44 }
45 }
46 return instance_;
47 }
48
ReleaseDataBaseConnection(RelationalStoreConnection * connection)49 int RelationalStoreInstance::ReleaseDataBaseConnection(RelationalStoreConnection *connection)
50 {
51 if (connection == nullptr) {
52 return -E_INVALID_DB;
53 }
54 auto manager = RelationalStoreInstance::GetInstance();
55 if (manager == nullptr) {
56 return -E_OUT_OF_MEMORY;
57 }
58 std::string identifier = connection->GetIdentifier();
59 manager->EnterDBOpenCloseProcess(identifier);
60 int errCode = connection->Close();
61 manager->ExitDBOpenCloseProcess(identifier);
62
63 if (errCode != E_OK) {
64 LOGE("Release db connection failed. %d", errCode);
65 }
66 return errCode;
67 }
68
CheckDatabaseFileStatus(const std::string & id)69 int RelationalStoreInstance::CheckDatabaseFileStatus(const std::string &id)
70 {
71 std::lock_guard<std::mutex> lockGuard(storeLock_);
72 if (dbs_.count(id) != 0 && dbs_[id] != nullptr) {
73 return -E_BUSY;
74 }
75 return E_OK;
76 }
77
GetFromCache(const RelationalDBProperties & properties,int & errCode)78 static IRelationalStore *GetFromCache(const RelationalDBProperties &properties, int &errCode)
79 {
80 errCode = E_OK;
81 std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, "");
82 auto iter = dbs_.find(identifier);
83 if (iter == dbs_.end()) {
84 errCode = -E_NOT_FOUND;
85 return nullptr;
86 }
87
88 auto *db = iter->second;
89 if (db == nullptr) {
90 LOGE("Store cache is nullptr, there may be a logic error");
91 errCode = -E_INTERNAL_ERROR;
92 return nullptr;
93 }
94 db->IncObjRef(db);
95 return db;
96 }
97
98 // Save to IKvDB to the global map
RemoveKvDBFromCache(const RelationalDBProperties & properties)99 void RelationalStoreInstance::RemoveKvDBFromCache(const RelationalDBProperties &properties)
100 {
101 std::lock_guard<std::mutex> lockGuard(storeLock_);
102 std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, "");
103 dbs_.erase(identifier);
104 }
105
SaveRelationalDBToCache(IRelationalStore * store,const RelationalDBProperties & properties)106 void RelationalStoreInstance::SaveRelationalDBToCache(IRelationalStore *store, const RelationalDBProperties &properties)
107 {
108 if (store == nullptr) {
109 return;
110 }
111
112 {
113 std::string identifier = properties.GetStringProp(RelationalDBProperties::IDENTIFIER_DATA, "");
114 store->WakeUpSyncer();
115 if (dbs_.count(identifier) == 0) {
116 dbs_.insert(std::pair<std::string, IRelationalStore *>(identifier, store));
117 }
118 }
119 }
120
OpenDatabase(const RelationalDBProperties & properties,int & errCode)121 IRelationalStore *RelationalStoreInstance::OpenDatabase(const RelationalDBProperties &properties, int &errCode)
122 {
123 auto db = new (std::nothrow) SQLiteRelationalStore();
124 if (db == nullptr) {
125 errCode = -E_OUT_OF_MEMORY;
126 LOGE("Failed to get relational store! err:%d", errCode);
127 return nullptr;
128 }
129
130 db->OnClose([this, properties]() {
131 LOGI("Remove from the cache");
132 this->RemoveKvDBFromCache(properties);
133 });
134
135 errCode = db->Open(properties);
136 if (errCode != E_OK) {
137 LOGE("Failed to open db! err:%d", errCode);
138 RefObject::KillAndDecObjRef(db);
139 return nullptr;
140 }
141
142 SaveRelationalDBToCache(db, properties);
143 return db;
144 }
145
GetDataBase(const RelationalDBProperties & properties,int & errCode)146 IRelationalStore *RelationalStoreInstance::GetDataBase(const RelationalDBProperties &properties, int &errCode)
147 {
148 std::lock_guard<std::mutex> lockGuard(storeLock_);
149 auto *db = GetFromCache(properties, errCode);
150 if (db != nullptr) {
151 LOGD("Get db from cache.");
152 return db;
153 }
154
155 // file lock
156 RelationalStoreInstance *manager = RelationalStoreInstance::GetInstance();
157 if (manager == nullptr) {
158 errCode = -E_OUT_OF_MEMORY;
159 return nullptr;
160 }
161
162 db = manager->OpenDatabase(properties, errCode);
163 if (errCode != E_OK) {
164 LOGE("Create data base failed, errCode = [%d]", errCode);
165 }
166 return db;
167 }
168
GetDatabaseConnection(const RelationalDBProperties & properties,int & errCode)169 RelationalStoreConnection *RelationalStoreInstance::GetDatabaseConnection(const RelationalDBProperties &properties,
170 int &errCode)
171 {
172 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
173 LOGD("Begin to get [%s] database connection.", STR_MASK(DBCommon::TransferStringToHex(identifier)));
174 RelationalStoreInstance *manager = RelationalStoreInstance::GetInstance();
175 manager->EnterDBOpenCloseProcess(properties.GetStringProp(DBProperties::IDENTIFIER_DATA, ""));
176 RelationalStoreConnection *connection = nullptr;
177 std::string canonicalDir;
178 IRelationalStore *db = GetDataBase(properties, errCode);
179 if (db == nullptr) {
180 LOGE("Failed to open the db:%d", errCode);
181 goto END;
182 }
183
184 canonicalDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
185 if (canonicalDir.empty() || canonicalDir != db->GetStorePath()) {
186 LOGE("Failed to check store path, the input path does not match with cached store.");
187 errCode = -E_INVALID_ARGS;
188 goto END;
189 }
190
191 connection = db->GetDBConnection(errCode);
192 if (connection == nullptr) { // not kill db, Other operations like import may be used concurrently
193 LOGE("Failed to get the db connect for delegate:%d", errCode);
194 }
195
196 END:
197 RefObject::DecObjRef(db); // restore the reference increased by the cache.
198 manager->ExitDBOpenCloseProcess(properties.GetStringProp(DBProperties::IDENTIFIER_DATA, ""));
199 return connection;
200 }
201
EnterDBOpenCloseProcess(const std::string & identifier)202 void RelationalStoreInstance::EnterDBOpenCloseProcess(const std::string &identifier)
203 {
204 std::unique_lock<std::mutex> lock(relationalDBOpenMutex_);
205 relationalDBOpenCondition_.wait(lock, [this, &identifier]() {
206 return this->relationalDBOpenSet_.count(identifier) == 0;
207 });
208 (void)relationalDBOpenSet_.insert(identifier);
209 }
210
ExitDBOpenCloseProcess(const std::string & identifier)211 void RelationalStoreInstance::ExitDBOpenCloseProcess(const std::string &identifier)
212 {
213 std::unique_lock<std::mutex> lock(relationalDBOpenMutex_);
214 (void)relationalDBOpenSet_.erase(identifier);
215 relationalDBOpenCondition_.notify_all();
216 }
217 } // namespace DistributedDB
218 #endif