GCC Code Coverage Report


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