GCC Code Coverage Report


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