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