Directory: | ./ |
---|---|
File: | pdserv/src/Database.cpp |
Date: | 2024-12-15 04:08:34 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 75 | 98 | 76.5% |
Branches: | 41 | 330 | 12.4% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /***************************************************************************** | ||
2 | * | ||
3 | * $Id$ | ||
4 | * | ||
5 | * Copyright 2015 Richard Hacker (lerichi at gmx dot net) | ||
6 | * | ||
7 | * This file is part of the pdserv library. | ||
8 | * | ||
9 | * The pdserv library is free software: you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU Lesser General Public License as published | ||
11 | * by the Free Software Foundation, either version 3 of the License, or (at | ||
12 | * your option) any later version. | ||
13 | * | ||
14 | * The pdserv library is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | ||
17 | * License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public License | ||
20 | * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>. | ||
21 | * | ||
22 | *****************************************************************************/ | ||
23 | |||
24 | #include "config.h" | ||
25 | |||
26 | #include "Database.h" | ||
27 | #include "Parameter.h" | ||
28 | #include "Debug.h" | ||
29 | |||
30 | #include <db.h> | ||
31 | #include <cstring> | ||
32 | #include <ctime> | ||
33 | #include <log4cplus/logger.h> | ||
34 | #include <log4cplus/loggingmacros.h> | ||
35 | |||
36 | using namespace PdServ; | ||
37 | |||
38 | struct pdata { | ||
39 | uint8_t version; | ||
40 | uint8_t classid; | ||
41 | size_t nelem; | ||
42 | struct timespec mtime; | ||
43 | char value[]; | ||
44 | }; | ||
45 | |||
46 | ///////////////////////////////////////////////////////////////////////////// | ||
47 | 12 | Database::Database(const log4cplus::Logger& l, const std::string& path): | |
48 | 12 | log(l) | |
49 | { | ||
50 | int ret; | ||
51 | |||
52 | 12 | db = 0; | |
53 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if (path.empty()) |
54 | ✗ | return; | |
55 | |||
56 | 12 | ret = db_create(&db, NULL, 0); | |
57 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret) { |
58 | // System error | ||
59 | ✗ | LOG4CPLUS_ERROR(log, | |
60 | LOG4CPLUS_TEXT("Fatal BDB system error: db_create(): ") | ||
61 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
62 | ✗ | db = 0; | |
63 | ✗ | return; | |
64 | } | ||
65 | |||
66 | 12 | ret = db->open(db, NULL, path.c_str(), NULL, DB_BTREE, DB_CREATE, 0666); | |
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret != 0) { |
68 | ✗ | LOG4CPLUS_ERROR(log, | |
69 | LOG4CPLUS_TEXT("db->open(): ") | ||
70 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret)) | ||
71 | << ' ' | ||
72 | << LOG4CPLUS_STRING_TO_TSTRING(path)); | ||
73 | ✗ | close(); | |
74 | } | ||
75 | } | ||
76 | |||
77 | ///////////////////////////////////////////////////////////////////////////// | ||
78 | 24 | Database::~Database() | |
79 | { | ||
80 | 12 | close(); | |
81 | 12 | } | |
82 | |||
83 | ///////////////////////////////////////////////////////////////////////////// | ||
84 | 5 | Database::operator bool() const | |
85 | { | ||
86 | 5 | return db; | |
87 | } | ||
88 | |||
89 | ///////////////////////////////////////////////////////////////////////////// | ||
90 | 12 | void Database::close() | |
91 | { | ||
92 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12 | if (!db) |
93 | ✗ | return; | |
94 | |||
95 | 12 | int ret = db->close(db, 0); | |
96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret) { |
97 | // Complain | ||
98 | ✗ | LOG4CPLUS_ERROR(log, | |
99 | LOG4CPLUS_TEXT("db->close(): ") | ||
100 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
101 | } | ||
102 | |||
103 | 12 | db = 0; | |
104 | } | ||
105 | |||
106 | ///////////////////////////////////////////////////////////////////////////// | ||
107 | 6 | bool Database::read(const Parameter* p, | |
108 | const char** buf, const struct timespec** mtime) const | ||
109 | { | ||
110 | 6 | *buf = 0; | |
111 | |||
112 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if (!db) |
113 | ✗ | return false; | |
114 | |||
115 | 6 | DBT key, value; | |
116 | |||
117 | // Clear the DBT structures | ||
118 | 6 | ::memset(&key, 0, sizeof( key)); | |
119 | 6 | ::memset(&value, 0, sizeof(value)); | |
120 | |||
121 | // Set key structure for path | ||
122 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | char pathBuf[p->path.size()]; |
123 |
2/4✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
|
6 | ::memcpy(pathBuf, p->path.data(), p->path.size()); |
124 | 6 | key.data = pathBuf; | |
125 | 6 | key.size = p->path.size(); | |
126 | |||
127 | // Fetch the data | ||
128 |
1/2✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
6 | int ret = db->get(db, NULL, &key, &value, 0); |
129 | 6 | const struct pdata *data = (struct pdata*)value.data; | |
130 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (ret != 0) { |
131 | // Only complain if the key is found and there is some other error | ||
132 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (ret != DB_NOTFOUND) |
133 | ✗ | LOG4CPLUS_ERROR(log, | |
134 | LOG4CPLUS_TEXT("db->get(): ") | ||
135 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
136 | else | ||
137 | LOG4CPLUS_TRACE(log, | ||
138 | LOG4CPLUS_TEXT("Key does not exist: ") | ||
139 | << LOG4CPLUS_STRING_TO_TSTRING(p->path)); | ||
140 | } | ||
141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | else if (value.size < sizeof(struct pdata)) { |
142 | ✗ | LOG4CPLUS_ERROR(log, | |
143 | LOG4CPLUS_TEXT("Header length insufficient: ") | ||
144 | << value.size << " instead of at least " | ||
145 | << sizeof(struct pdata)); | ||
146 | } | ||
147 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | else if (data->version == 1) { |
148 | 4 | const size_t len = sizeof(struct pdata) + p->memSize; | |
149 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (value.size < len) { |
150 | ✗ | LOG4CPLUS_WARN(log, | |
151 | LOG4CPLUS_TEXT("Data size changed: ") | ||
152 | << LOG4CPLUS_STRING_TO_TSTRING(p->path)); | ||
153 | } | ||
154 |
2/4✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 4 times.
|
4 | else if (data->classid != p->dtype.primary()) { |
155 | ✗ | LOG4CPLUS_WARN(log, | |
156 | LOG4CPLUS_TEXT("Data type changed: ") | ||
157 | << LOG4CPLUS_STRING_TO_TSTRING(p->path)); | ||
158 | } | ||
159 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
|
4 | else if (data->nelem != p->dim.nelem) { |
160 | ✗ | LOG4CPLUS_WARN(log, | |
161 | LOG4CPLUS_TEXT("Element count changed: ") | ||
162 | << LOG4CPLUS_STRING_TO_TSTRING(p->path)); | ||
163 | } | ||
164 | else { | ||
165 | 4 | *buf = data->value; | |
166 | 4 | *mtime = &data->mtime; | |
167 | 4 | return true; | |
168 | } | ||
169 | } | ||
170 | else { | ||
171 | ✗ | LOG4CPLUS_ERROR(log, | |
172 | LOG4CPLUS_TEXT("Unknown header version: ") | ||
173 | << data->version); | ||
174 | } | ||
175 | |||
176 | 8 | return false; | |
177 | } | ||
178 | |||
179 | ///////////////////////////////////////////////////////////////////////////// | ||
180 | 4 | void Database::save(const Parameter* p, | |
181 | const char* buf, const struct timespec* mtime) | ||
182 | { | ||
183 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (!db) |
184 | ✗ | return; | |
185 | |||
186 | 4 | DBT key, value; | |
187 | |||
188 | // Clear the DBT structures | ||
189 | 4 | ::memset(&key, 0, sizeof( key)); | |
190 | 4 | ::memset(&value, 0, sizeof(value)); | |
191 | |||
192 | // Set key structure for path | ||
193 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | char pathBuf[p->path.size()]; |
194 |
2/4✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
|
4 | ::memcpy(pathBuf, p->path.data(), p->path.size()); |
195 | 4 | key.data = pathBuf; | |
196 | 4 | key.size = p->path.size(); | |
197 | |||
198 | // Set value structure | ||
199 | 4 | const size_t buflen = sizeof(struct pdata) + p->memSize; | |
200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | char valueBuf[buflen]; |
201 | 4 | struct pdata* data = (struct pdata*)valueBuf; | |
202 | 4 | data->version = 1; | |
203 |
1/2✓ Branch 15 taken 4 times.
✗ Branch 16 not taken.
|
4 | data->classid = p->dtype.primary(); |
204 | 4 | data->mtime = *mtime; | |
205 | 4 | data->nelem = p->dim.nelem; | |
206 |
2/4✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
|
4 | ::memcpy(data->value, buf, p->memSize); |
207 | 4 | value.data = valueBuf; | |
208 | 4 | value.size = buflen; | |
209 | |||
210 |
1/2✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | int ret = db->put(db, NULL, &key, &value, DB_AUTO_COMMIT); |
211 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (ret != 0) { |
212 | // Complain | ||
213 | ✗ | LOG4CPLUS_ERROR(log, | |
214 | LOG4CPLUS_TEXT("db->put(): ") | ||
215 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
216 | 4 | } | |
217 | } | ||
218 | |||
219 | ///////////////////////////////////////////////////////////////////////////// | ||
220 | 6 | void Database::checkKeys(const std::set<std::string>& keySet) | |
221 | { | ||
222 | 6 | DBT key, value; | |
223 | 6 | DBC *dbcp; | |
224 | int ret; | ||
225 | |||
226 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if (!db) |
227 | ✗ | return; | |
228 | |||
229 | LOG4CPLUS_TRACE(log, LOG4CPLUS_TEXT("Cleaning up database")); | ||
230 | |||
231 |
2/4✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
|
6 | if ((ret = db->cursor(db, NULL, &dbcp, 0)) != 0) { |
232 | ✗ | LOG4CPLUS_ERROR(log, | |
233 | LOG4CPLUS_TEXT("db->cursor(): ") | ||
234 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
235 | ✗ | return; | |
236 | } | ||
237 | |||
238 | // Clear the DBT structures | ||
239 | 6 | ::memset(&key, 0, sizeof( key)); | |
240 | 6 | ::memset(&value, 0, sizeof(value)); | |
241 | |||
242 |
3/4✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 6 times.
|
10 | while ((ret = dbcp->c_get(dbcp, &key, &value, DB_NEXT)) == 0) { |
243 |
1/2✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
8 | std::string _key((const char*)key.data, key.size); |
244 | LOG4CPLUS_TRACE(log, | ||
245 | LOG4CPLUS_TEXT("check key ") | ||
246 | << LOG4CPLUS_STRING_TO_TSTRING(_key)); | ||
247 |
2/4✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 4 times.
|
4 | if (keySet.find(_key) == keySet.end()) { |
248 | ✗ | if ((ret = dbcp->c_del(dbcp, 0)) == 0) { | |
249 | LOG4CPLUS_TRACE(log, | ||
250 | LOG4CPLUS_TEXT("Erased key ") | ||
251 | << LOG4CPLUS_STRING_TO_TSTRING(_key)); | ||
252 | } | ||
253 | else | ||
254 | ✗ | LOG4CPLUS_ERROR(log, | |
255 | LOG4CPLUS_TEXT("db->c_del(): ") | ||
256 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
257 | } | ||
258 | } | ||
259 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret != DB_NOTFOUND) { |
260 | ✗ | LOG4CPLUS_ERROR(log, | |
261 | LOG4CPLUS_TEXT("db->c_get(): ") | ||
262 | << LOG4CPLUS_C_STR_TO_TSTRING(db_strerror(ret))); | ||
263 | } | ||
264 | } | ||
265 |