• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12321902803

13 Dec 2024 07:34PM UTC coverage: 66.359% (+1.6%) from 64.78%
12321902803

Pull #14970

github

web-flow
Merge e3a7df61c into 3dfd8e317
Pull Request #14970: boost > std optional

26084 of 54744 branches covered (47.65%)

Branch coverage included in aggregate %.

14 of 15 new or added lines in 2 files covered. (93.33%)

1863 existing lines in 52 files now uncovered.

85857 of 113946 relevant lines covered (75.35%)

4412729.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.47
/ext/lmdb-safe/lmdb-safe.cc
1
#include "config.h"
2
#include "lmdb-safe.hh"
3

4
#include <fcntl.h>
5
#include <mutex>
6
#include <memory>
7
#include <sys/stat.h>
8
#include <cstring>
9
#include <map>
10

11
#ifndef DNSDIST
12
#include "../../pdns/gettime.hh"
13
#endif
14

15
using std::string;
16
using std::runtime_error;
17
using std::tuple;
18
using std::weak_ptr;
19

20
static string MDBError(int rc)
21
{
×
22
  return mdb_strerror(rc);
×
23
}
×
24

25
#ifndef DNSDIST
26

27
namespace LMDBLS {
28
  // this also returns a pointer to the string's data. Do not hold on to it too long!
29
  const LSheader* LSassertFixedHeaderSize(std::string_view val) {
1,405,982✔
30
    // cerr<<"val.size()="<<val.size()<<endl;
31
    if (val.size() < LS_MIN_HEADER_SIZE) {
1,405,982!
UNCOV
32
      throw std::runtime_error("LSheader too short");
×
UNCOV
33
    }
×
34

35
    return reinterpret_cast<const LSheader*>(val.data());
1,405,982✔
36
  }
1,405,982✔
37

38
  size_t LScheckHeaderAndGetSize(std::string_view val, size_t datasize) {
422,922✔
39
    const LSheader* lsh = LSassertFixedHeaderSize(val);
422,922✔
40

41
    if (lsh->d_version != 0) {
422,922!
UNCOV
42
      throw std::runtime_error("LSheader has wrong version (not zero)");
×
UNCOV
43
    }
×
44

45
    size_t headersize = LS_MIN_HEADER_SIZE;
422,922✔
46

47
    unsigned char* tmp = (unsigned char*)val.data();
422,922✔
48
    uint16_t numextra = (tmp[LS_NUMEXTRA_OFFSET] << 8) + tmp[LS_NUMEXTRA_OFFSET+1];
422,922✔
49

50
    headersize += numextra * LS_BLOCK_SIZE;
422,922✔
51

52
    if (val.size() < headersize) {
422,922!
UNCOV
53
      throw std::runtime_error("LSheader too short for promised extra data");
×
UNCOV
54
    }
×
55

56
    if (datasize && val.size() < (headersize+datasize)) {
422,922!
UNCOV
57
      throw std::runtime_error("Trailing data after LSheader has wrong size");
×
UNCOV
58
    }
×
59

60
    return headersize;
422,922✔
61
  }
422,922✔
62

63
  size_t LScheckHeaderAndGetSize(const MDBOutVal *val, size_t datasize) {
422,922✔
64
    return LScheckHeaderAndGetSize(val->getNoStripHeader<string_view>(), datasize);
422,922✔
65
  }
422,922✔
66

67
  bool LSisDeleted(std::string_view val) {
975,441✔
68
    const LSheader* lsh = LSassertFixedHeaderSize(val);
975,441✔
69

70
    return (lsh->d_flags & LS_FLAG_DELETED) != 0;
975,441✔
71
  }
975,441✔
72

73
  uint64_t LSgetTimestamp(std::string_view val) {
7,619✔
74
    const LSheader* lsh = LSassertFixedHeaderSize(val);
7,619✔
75

76
    return lsh->getTimestamp();
7,619✔
77
  }
7,619✔
78
  bool s_flag_deleted{false};
79
}
80

81
#endif /* #ifndef DNSDIST */
82

83
MDBDbi::MDBDbi(MDB_env* /* env */, MDB_txn* txn, const string_view dbname, int flags) : d_dbi(-1)
84
{
22,242✔
85
  // A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
86

87
  int rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi);
22,242!
88
  if(rc)
22,242!
89
    throw std::runtime_error("Unable to open named database: " + MDBError(rc));
×
90

91
  // Database names are keys in the unnamed database, and may be read but not written.
92
}
22,242✔
93

94
MDBEnv::MDBEnv(const char* fname, int flags, int mode, uint64_t mapsizeMB)
95
{
2,306✔
96
  mdb_env_create(&d_env);
2,306✔
97
  if(mdb_env_set_mapsize(d_env, mapsizeMB * 1048576))
2,306!
98
    throw std::runtime_error("setting map size");
×
99
    /*
100
Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(),
101
    */
102

103
  mdb_env_set_maxdbs(d_env, 128);
2,306✔
104

105
  // we need MDB_NOTLS since we rely on its semantics
106
  if(int rc=mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) {
2,306!
107
    // If this function fails, mdb_env_close() must be called to discard the MDB_env handle.
108
    mdb_env_close(d_env);
×
109
    throw std::runtime_error("Unable to open database file "+std::string(fname)+": " + MDBError(rc));
×
110
  }
×
111

112
  if ((flags & MDB_RDONLY) == 0) {
2,306!
113
    // Check for stale readers to prevent unbridled database growth.
114
    // Only do this when in RW mode since it affects the file.
115
    mdb_reader_check(d_env, nullptr);
2,306✔
116
  }
2,306✔
117
}
2,306✔
118

119
void MDBEnv::incROTX()
120
{
85,781✔
121
  std::lock_guard<std::mutex> l(d_countmutex);
85,781✔
122
  ++d_ROtransactionsOut[std::this_thread::get_id()];
85,781✔
123
}
85,781✔
124

125
void MDBEnv::decROTX()
126
{
85,781✔
127
  std::lock_guard<std::mutex> l(d_countmutex);
85,781✔
128
  --d_ROtransactionsOut[std::this_thread::get_id()];
85,781✔
129
}
85,781✔
130

131
void MDBEnv::incRWTX()
132
{
25,435✔
133
  std::lock_guard<std::mutex> l(d_countmutex);
25,435✔
134
  ++d_RWtransactionsOut[std::this_thread::get_id()];
25,435✔
135
}
25,435✔
136

137
void MDBEnv::decRWTX()
138
{
25,435✔
139
  std::lock_guard<std::mutex> l(d_countmutex);
25,435✔
140
  --d_RWtransactionsOut[std::this_thread::get_id()];
25,435✔
141
}
25,435✔
142

143
int MDBEnv::getRWTX()
144
{
110,824✔
145
  std::lock_guard<std::mutex> l(d_countmutex);
110,824✔
146
  return d_RWtransactionsOut[std::this_thread::get_id()];
110,824✔
147
}
110,824✔
148
int MDBEnv::getROTX()
149
{
25,043✔
150
  std::lock_guard<std::mutex> l(d_countmutex);
25,043✔
151
  return d_ROtransactionsOut[std::this_thread::get_id()];
25,043✔
152
}
25,043✔
153

154

155
std::shared_ptr<MDBEnv> getMDBEnv(const char* fname, int flags, int mode, uint64_t mapsizeMB)
156
{
4,736✔
157
  struct Value
4,736✔
158
  {
4,736✔
159
    weak_ptr<MDBEnv> wp;
4,736✔
160
    int flags;
4,736✔
161
  };
4,736✔
162

163
  static std::map<tuple<dev_t, ino_t>, Value> s_envs;
4,736✔
164
  static std::mutex mut;
4,736✔
165

166
  struct stat statbuf;
4,736✔
167
  if(stat(fname, &statbuf)) {
4,736✔
168
    if(errno != ENOENT)
257!
169
      throw std::runtime_error("Unable to stat prospective mdb database: "+string(strerror(errno)));
×
170
    else {
257✔
171
      std::lock_guard<std::mutex> l(mut);
257✔
172
      auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, mapsizeMB);
257✔
173
      if(stat(fname, &statbuf))
257!
174
        throw std::runtime_error("Unable to stat prospective mdb database: "+string(strerror(errno)));
×
175
      auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
257✔
176
      s_envs[key] = {fresh, flags};
257✔
177
      return fresh;
257✔
178
    }
257✔
179
  }
257✔
180

181
  std::lock_guard<std::mutex> l(mut);
4,479✔
182
  auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
4,479✔
183
  auto iter = s_envs.find(key);
4,479✔
184
  if(iter != s_envs.end()) {
4,479✔
185
    auto sp = iter->second.wp.lock();
3,216✔
186
    if(sp) {
3,216✔
187
      if(iter->second.flags != flags)
2,430!
188
        throw std::runtime_error("Can't open mdb with differing flags");
×
189

190
      return sp;
2,430✔
191
    }
2,430✔
192
    else {
786✔
193
      s_envs.erase(iter); // useful if make_shared fails
786✔
194
    }
786✔
195
  }
3,216✔
196

197
  auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, mapsizeMB);
2,049✔
198
  s_envs[key] = {fresh, flags};
2,049✔
199

200
  return fresh;
2,049✔
201
}
4,479✔
202

203

204
MDBDbi MDBEnv::openDB(const string_view dbname, int flags)
205
{
22,242✔
206
  unsigned int envflags;
22,242✔
207
  mdb_env_get_flags(d_env, &envflags);
22,242✔
208
  /*
209
    This function must not be called from multiple concurrent transactions in the same process. A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
210
  */
211
  std::lock_guard<std::mutex> l(d_openmut);
22,242✔
212

213
  if(!(envflags & MDB_RDONLY)) {
22,242!
214
    auto rwt = getRWTransaction();
22,242✔
215
    MDBDbi ret = rwt->openDB(dbname, flags);
22,242✔
216
    rwt->commit();
22,242✔
217
    return ret;
22,242✔
218
  }
22,242✔
219

UNCOV
220
  MDBDbi ret;
×
UNCOV
221
  {
×
UNCOV
222
    auto rwt = getROTransaction();
×
UNCOV
223
    ret = rwt->openDB(dbname, flags);
×
UNCOV
224
  }
×
UNCOV
225
  return ret;
×
226
}
22,242✔
227

228
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn):
229
  MDBROTransactionImpl(parent, txn)
230

231
{
25,435✔
232

233
}
25,435✔
234

235
MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, int flags)
236
{
25,043✔
237
  MDB_txn *result;
25,043✔
238
  if(env->getROTX() || env->getRWTX())
25,043!
239
    throw std::runtime_error("Duplicate RW transaction");
×
240

241
  if(int rc=mdb_txn_begin(env->d_env, parent, flags, &result))
25,043!
242
    throw std::runtime_error("Unable to start RW transaction: "+std::string(mdb_strerror(rc)));
×
243

244
  env->incRWTX();
25,043✔
245
  return result;
25,043✔
246
}
25,043✔
247

248
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv* parent, int flags):
249
  MDBRWTransactionImpl(parent, openRWTransaction(parent, nullptr, flags))
250
{
25,043✔
251
#ifndef DNSDIST
25,043✔
252
  struct timespec tp;
25,043✔
253

254
  gettime(&tp, true);
25,043✔
255

256
  d_txtime = tp.tv_sec * (1000 * 1000 * 1000) + tp.tv_nsec;
25,043✔
257
#endif
25,043✔
258
}
25,043✔
259

260
MDBRWTransactionImpl::~MDBRWTransactionImpl()
261
{
25,435✔
262
  abort();
25,435✔
263
}
25,435✔
264

265
void MDBRWTransactionImpl::commit()
266
{
24,995✔
267
  closeRORWCursors();
24,995✔
268
  if (!d_txn) {
24,995!
269
    return;
×
270
  }
×
271

272
  if(int rc = mdb_txn_commit(d_txn)) {
24,995!
273
    throw std::runtime_error("committing: " + std::string(mdb_strerror(rc)));
×
274
  }
×
275
  environment().decRWTX();
24,995✔
276
  d_txn = nullptr;
24,995✔
277
}
24,995✔
278

279
void MDBRWTransactionImpl::abort()
280
{
25,470✔
281
  closeRORWCursors();
25,470✔
282
  if (!d_txn) {
25,470✔
283
    return;
25,030✔
284
  }
25,030✔
285

286
  mdb_txn_abort(d_txn);
440✔
287
  // prevent the RO destructor from cleaning up the transaction itself
288
  environment().decRWTX();
440✔
289
  d_txn = nullptr;
440✔
290
}
440✔
291

292
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn):
293
  d_parent(parent),
294
  d_cursors(),
295
  d_txn(txn)
296
{
111,216✔
297

298
}
111,216✔
299

300
MDB_txn *MDBROTransactionImpl::openROTransaction(MDBEnv *env, MDB_txn *parent, int flags)
301
{
85,781✔
302
  if(env->getRWTX())
85,781!
303
    throw std::runtime_error("Duplicate RO transaction");
×
304

305
  /*
306
    A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */
307
  MDB_txn *result = nullptr;
85,781✔
308

309
  if(int rc=mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result))
85,781!
310
    throw std::runtime_error("Unable to start RO transaction: "+string(mdb_strerror(rc)));
×
311

312
  env->incROTX();
85,781✔
313

314
  return result;
85,781✔
315
}
85,781✔
316

317
void MDBROTransactionImpl::closeROCursors()
318
{
161,681✔
319
  // we need to move the vector away to ensure that the cursors don’t mess with our iteration.
320
  std::vector<MDBROCursor*> buf;
161,681✔
321
  std::swap(d_cursors, buf);
161,681✔
322
  for (auto &cursor: buf) {
161,681!
323
    cursor->close();
×
324
  }
×
325
}
161,681✔
326

327
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, int flags):
328
    MDBROTransactionImpl(parent, openROTransaction(parent, nullptr, flags))
329
{
85,781✔
330

331
}
85,781✔
332

333
MDBROTransactionImpl::~MDBROTransactionImpl()
334
{
111,216✔
335
  // this is safe because C++ will not call overrides of virtual methods in destructors.
336
  MDBROTransactionImpl::commit();
111,216✔
337
}
111,216✔
338

339
void MDBROTransactionImpl::abort()
340
{
×
341
  closeROCursors();
×
342
  // if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
343
  if (d_txn) {
×
344
    d_parent->decROTX();
×
345
    mdb_txn_abort(d_txn); // this appears to work better than abort for r/o database opening
×
346
    d_txn = nullptr;
×
347
  }
×
348
}
×
349

350
void MDBROTransactionImpl::commit()
351
{
111,216✔
352
  closeROCursors();
111,216✔
353
  // if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
354
  if (d_txn) {
111,216✔
355
    d_parent->decROTX();
85,781✔
356
    mdb_txn_commit(d_txn); // this appears to work better than abort for r/o database opening
85,781✔
357
    d_txn = nullptr;
85,781✔
358
  }
85,781✔
359
}
111,216✔
360

361

362

363
void MDBRWTransactionImpl::clear(MDB_dbi dbi)
364
{
×
365
  if(int rc = mdb_drop(d_txn, dbi, 0)) {
×
366
    throw runtime_error("Error clearing database: " + MDBError(rc));
×
367
  }
×
368
}
×
369

370
MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi& dbi)
371
{
63,829✔
372
  MDB_cursor *cursor;
63,829✔
373
  int rc= mdb_cursor_open(d_txn, dbi, &cursor);
63,829✔
374
  if(rc) {
63,829!
375
    throw std::runtime_error("Error creating RW cursor: "+std::string(mdb_strerror(rc)));
×
376
  }
×
377

378
  return MDBRWCursor(d_rw_cursors, cursor, d_txn, d_txtime);
63,829✔
379
}
63,829✔
380

381
MDBRWCursor MDBRWTransactionImpl::getCursor(const MDBDbi &dbi)
382
{
63,056✔
383
  return getRWCursor(dbi);
63,056✔
384
}
63,056✔
385

386
MDBRWTransaction MDBRWTransactionImpl::getRWTransaction()
387
{
392✔
388
  MDB_txn *txn;
392✔
389
  if (int rc = mdb_txn_begin(environment(), *this, 0, &txn)) {
392!
390
    throw std::runtime_error(std::string("failed to start child transaction: ")+mdb_strerror(rc));
×
391
  }
×
392
  // we need to increase the counter here because commit/abort on the child transaction will decrease it
393
  environment().incRWTX();
392✔
394
  return MDBRWTransaction(new MDBRWTransactionImpl(&environment(), txn));
392✔
395
}
392✔
396

397
MDBROTransaction MDBRWTransactionImpl::getROTransaction()
398
{
392✔
399
  return getRWTransaction();
392✔
400
}
392✔
401

402
MDBROTransaction MDBEnv::getROTransaction()
403
{
85,781✔
404
  return MDBROTransaction(new MDBROTransactionImpl(this));
85,781✔
405
}
85,781✔
406
MDBRWTransaction MDBEnv::getRWTransaction()
407
{
25,043✔
408
  return MDBRWTransaction(new MDBRWTransactionImpl(this));
25,043✔
409
}
25,043✔
410

411

412
void MDBRWTransactionImpl::closeRWCursors()
413
{
50,465✔
414
  decltype(d_rw_cursors) buf;
50,465✔
415
  std::swap(d_rw_cursors, buf);
50,465✔
416
  for (auto &cursor: buf) {
50,465!
417
    cursor->close();
×
418
  }
×
419
}
50,465✔
420

421
MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi& dbi)
422
{
16,635✔
423
  return getROCursor(dbi);
16,635✔
424
}
16,635✔
425

426
MDBROCursor MDBROTransactionImpl::getROCursor(const MDBDbi &dbi)
427
{
16,635✔
428
  MDB_cursor *cursor;
16,635✔
429
  int rc= mdb_cursor_open(d_txn, dbi, &cursor);
16,635✔
430
  if(rc) {
16,635!
431
    throw std::runtime_error("Error creating RO cursor: "+std::string(mdb_strerror(rc)));
×
432
  }
×
433
  return MDBROCursor(d_cursors, cursor);
16,635✔
434
}
16,635✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc