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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

41.78
/ext/lmdb-safe/lmdb-safe.hh
1
#pragma once
2

3
#include "config.h"
4

5
#include <stdexcept>
6
#include <string_view>
7
#include <lmdb.h>
8
#include <map>
9
#include <thread>
10
#include <memory>
11
#include <string>
12
#include <cstring>
13
#include <mutex>
14
#include <vector>
15
#include <algorithm>
16
#include <arpa/inet.h>
17

18
#ifndef DNSDIST
19
#include <boost/range/detail/common.hpp>
20
#include <cstdint>
21
#include <netinet/in.h>
22
#endif
23

24
using std::string_view;
25

26
/* open issues:
27
 *
28
 * - Missing convenience functions (string_view, string).
29
 */
30

31
/*
32
 * The error strategy. Anything that "should never happen" turns into an exception. But
33
 * things like 'duplicate entry' or 'no such key' are for you to deal with.
34
 */
35

36
/*
37
 * Thread safety: we are as safe as lmdb. You can talk to MDBEnv from as many threads as
38
 * you want.
39
 */
40

41
/*
42
 * MDBDbi is our only 'value type' object, as 1) a dbi is actually an integer and 2) per
43
 * LMDB documentation, we never close it.
44
 */
45
class MDBDbi
46
{
47
public:
48
  MDBDbi(): d_dbi(-1)
49
  {
8✔
50
  }
8✔
51
  explicit MDBDbi(MDB_env* env, MDB_txn* txn, string_view dbname, int flags);
52

53
  operator const MDB_dbi&() const
54
  {
267✔
55
    return d_dbi;
267✔
56
  }
267✔
57

58
  MDB_dbi d_dbi;
59
};
60

61
class MDBRWTransactionImpl;
62
class MDBROTransactionImpl;
63

64
using MDBROTransaction = std::unique_ptr<MDBROTransactionImpl>;
65
using MDBRWTransaction = std::unique_ptr<MDBRWTransactionImpl>;
66

67
class MDBEnv
68
{
69
public:
70
  MDBEnv(const char* fname, int flags, int mode, uint64_t mapsizeMB);
71

72
  ~MDBEnv()
73
  {
16✔
74
    // Only a single thread may call this function. All transactions, databases, and
75
    // cursors must already be closed before calling this function
76
    mdb_env_close(d_env);
16✔
77
    // but, elsewhere, docs say database handles do not need to be closed?
78
  }
16✔
79

80
  MDBDbi openDB(string_view dbname, int flags);
81

82
  MDBRWTransaction getRWTransaction();
83
  MDBROTransaction getROTransaction();
84

85
  operator MDB_env*& ()
86
  {
×
87
    return d_env;
×
88
  }
×
89
  MDB_env* d_env;
90

91
  int getRWTX();
92
  void incRWTX();
93
  void decRWTX();
94
  int getROTX();
95
  void incROTX();
96
  void decROTX();
97
private:
98
  std::mutex d_openmut;
99
  std::mutex d_countmutex;
100
  std::map<std::thread::id, int> d_RWtransactionsOut;
101
  std::map<std::thread::id, int> d_ROtransactionsOut;
102
};
103

104
std::shared_ptr<MDBEnv> getMDBEnv(const char* fname, int flags, int mode, uint64_t mapsizeMB=(sizeof(void *)==4) ? 100 : 16000);
105

106
#ifndef DNSDIST
107

108
struct MDBOutVal; // forward declaration because of how the functions below tie in with MDBOutVal
109

110
namespace LMDBLS {
111
  class __attribute__((__packed__)) LSheader {
112
  private:
113
    static auto bswap64(uint64_t value) -> uint64_t
114
    {
115
#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) || !defined(__ORDER_BIG_ENDIAN__)
116
#error "your compiler does not define byte order macros"
117
#endif
118

119
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
120
      // FIXME: Do something more portable than __builtin_bswap64.
121
      return __builtin_bswap64(value);
122
#else
123
      return value;
124
#endif
125
    }
126

127
  public:
128
    uint64_t d_timestamp;
129
    uint64_t d_txnid;
130
    uint8_t d_version;
131
    uint8_t d_flags;
132
    uint32_t d_reserved{};
133
    uint16_t d_numextra;
134

135
    // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
136
    LSheader(uint64_t timestamp, uint64_t txnid, uint8_t flags = 0, uint8_t version = 0, uint8_t numextra = 0) :
137
      d_timestamp(bswap64(timestamp)),
138
      d_txnid(bswap64(txnid)),
139
      d_version(version),
140
      d_flags(flags),
141
      d_numextra(htons(numextra))
142
    {
143
    }
144

145
    std::string toString() {
146
      return std::string((char*)this, sizeof(*this)) + std::string(ntohs(d_numextra)*8, '\0');
147
    }
148

149
    [[nodiscard]] uint64_t getTimestamp() const {
150
      return bswap64(d_timestamp);
151
    }
152
  };
153

154
  static_assert(sizeof(LSheader)==24, "LSheader size is wrong");
155

156
  const size_t LS_MIN_HEADER_SIZE = sizeof(LSheader);
157
  const size_t LS_BLOCK_SIZE = 8;
158
  const size_t LS_NUMEXTRA_OFFSET = 22;
159
  const uint8_t LS_FLAG_DELETED = 0x01;
160

161
  const LSheader* LSassertFixedHeaderSize(std::string_view val);
162
  size_t LScheckHeaderAndGetSize(std::string_view val, size_t datasize=0);
163
  size_t LScheckHeaderAndGetSize(const MDBOutVal *val, size_t datasize=0);
164
  bool LSisDeleted(std::string_view val);
165
  uint64_t LSgetTimestamp(std::string_view val);
166

167
  extern bool s_flag_deleted;
168
}
169

170
#endif /* ifndef DNSDIST */
171

172
template <class T>
173
auto hostToNetworkByteOrder(T value) -> T;
174

175
template <class T>
176
auto networkToHostByteOrder(T value) -> T;
177

178
template <>
179
inline auto hostToNetworkByteOrder(uint32_t value) -> uint32_t
180
{
×
181
  return htonl(value);
×
182
}
×
183

184
template <>
185
inline auto networkToHostByteOrder(uint32_t value) -> uint32_t
186
{
×
187
  return ntohl(value);
×
188
}
×
189

190
struct MDBOutVal
191
{
192
  operator MDB_val&()
193
  {
×
194
    return d_mdbval;
×
195
  }
×
196

197
  template <class T>
198
  T get() const;
199

200
#ifndef DNSDIST
201
  template <class T>
202
  T getNoStripHeader() const;
203
#endif
204

205
  MDB_val d_mdbval;
206
};
207

208
#ifndef DNSDIST
209
template <class T>
210
inline T MDBOutVal::get() const
211
{
212
  T ret{};
213
  size_t offset = LMDBLS::LScheckHeaderAndGetSize(this, sizeof(ret));
214
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
215
  memcpy(&ret, static_cast<const char*>(d_mdbval.mv_data) + offset, sizeof(ret));
216
  ret = networkToHostByteOrder(ret);
217
  return ret;
218
}
219

220
template <class T>
221
inline T MDBOutVal::getNoStripHeader() const
222
{
223
  T ret{};
224
  if (d_mdbval.mv_size != sizeof(ret)) {
225
    throw std::runtime_error("MDB data has wrong length for type");
226
  }
227

228
  memcpy(&ret, d_mdbval.mv_data, sizeof(ret));
229
  ret = networkToHostByteOrder(ret);
230
  return ret;
231
}
232
#endif /* ifndef DNSDIST */
233

234
#ifdef DNSDIST
235

236
template <>
237
inline std::string MDBOutVal::get<std::string>() const
238
{
56✔
239
  return {static_cast<char*>(d_mdbval.mv_data), d_mdbval.mv_size};
56✔
240
}
56✔
241

242
template <>
243
inline std::string_view MDBOutVal::get<std::string_view>() const
244
{
×
245
  return {static_cast<char*>(d_mdbval.mv_data), d_mdbval.mv_size};
×
246
}
×
247

248
#else
249

250
template <>
251
inline std::string MDBOutVal::get<std::string>() const
252
{
253
  size_t offset = LMDBLS::LScheckHeaderAndGetSize(this);
254
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
255
  return {static_cast<char*>(d_mdbval.mv_data) + offset, d_mdbval.mv_size - offset};
256
}
257

258
template <>
259
inline std::string_view MDBOutVal::get<std::string_view>() const
260
{
261
  size_t offset = LMDBLS::LScheckHeaderAndGetSize(this);
262
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
263
  return {static_cast<char*>(d_mdbval.mv_data) + offset, d_mdbval.mv_size - offset};
264
}
265

266
template <>
267
inline std::string MDBOutVal::getNoStripHeader<std::string>() const
268
{
269
  return {static_cast<char*>(d_mdbval.mv_data), d_mdbval.mv_size};
270
}
271

272
template <>
273
inline std::string_view MDBOutVal::getNoStripHeader<std::string_view>() const
274
{
275
  return {static_cast<char*>(d_mdbval.mv_data), d_mdbval.mv_size};
276
}
277

278
#endif  // ifdef DNSDIST
279

280
class MDBInVal
281
{
282
public:
283
  MDBInVal(const MDBOutVal& rhs) :
284
    d_mdbval(rhs.d_mdbval)
285
  {
×
286
  }
×
287

288
#ifndef DNSDIST
289
  template <class T>
290
  MDBInVal(T rhs)
291
  {
292
    auto rhsNetworkOrder = hostToNetworkByteOrder(rhs);
293
    static_assert(sizeof(rhsNetworkOrder) <= sizeof(d_memory));
294
    memcpy(&d_memory[0], &rhsNetworkOrder, sizeof(rhsNetworkOrder));
295
    d_mdbval.mv_size = sizeof(rhs);
296
    d_mdbval.mv_data = static_cast<void*>(d_memory);
297
  }
298
#endif
299

300
  MDBInVal(const char* rhs)
301
  {
6✔
302
    d_mdbval.mv_size = strlen(rhs);
6✔
303
    d_mdbval.mv_data = (void*)rhs;
6✔
304
  }
6✔
305

306
  MDBInVal(const string_view& rhs)
307
  {
×
308
    d_mdbval.mv_size = rhs.size();
×
309
    d_mdbval.mv_data = (void*)rhs.data();
×
310
  }
×
311

312
  MDBInVal(const std::string& rhs)
313
  {
164✔
314
    d_mdbval.mv_size = rhs.size();
164✔
315
    d_mdbval.mv_data = (void*)rhs.data();
164✔
316
  }
164✔
317

318
  template<typename T>
319
  static MDBInVal fromStruct(const T& rhs)
320
  {
321
    MDBInVal ret;
322
    ret.d_mdbval.mv_size = sizeof(T);
323
    ret.d_mdbval.mv_data = (void*)&rhs;
324
    return ret;
325
  }
326

327
  operator MDB_val&()
328
  {
×
329
    return d_mdbval;
×
330
  }
×
331

332
  // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
333
  MDB_val d_mdbval{};
334

335
private:
336
  MDBInVal(){}
×
337
#ifndef DNSDIST
338
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
339
  char d_memory[sizeof(uint64_t)]{};
340
#endif
341
};
342

343
class MDBROCursor;
344

345
class MDBROTransactionImpl
346
{
347
protected:
348
  MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn);
349

350
private:
351
  static MDB_txn *openROTransaction(MDBEnv *env, MDB_txn *parent, int flags=0);
352

353
  MDBEnv* d_parent;
354
  std::vector<MDBROCursor*> d_cursors;
355

356
protected:
357
  MDB_txn* d_txn;
358

359
  void closeROCursors();
360

361
public:
362
  explicit MDBROTransactionImpl(MDBEnv* parent, int flags=0);
363

364
  MDBROTransactionImpl(const MDBROTransactionImpl& src) = delete;
365
  MDBROTransactionImpl &operator=(const MDBROTransactionImpl& src) = delete;
366

367
  // The move constructor/operator cannot be made safe due to Object Slicing with MDBRWTransaction.
368
  MDBROTransactionImpl(MDBROTransactionImpl&& rhs) = delete;
369
  MDBROTransactionImpl &operator=(MDBROTransactionImpl &&rhs) = delete;
370

371
  virtual ~MDBROTransactionImpl();
372

373
  virtual void abort();
374
  virtual void commit();
375

376
  int get(MDB_dbi dbi, const MDBInVal& key, MDBOutVal& val)
377
  {
85✔
378
    if(!d_txn)
85!
379
      throw std::runtime_error("Attempt to use a closed RO transaction for get");
×
380

381
    int rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
85✔
382
                     const_cast<MDB_val*>(&val.d_mdbval));
85✔
383

384
    if(rc && rc != MDB_NOTFOUND) {
85!
385
      throw std::runtime_error("getting data: " + std::string(mdb_strerror(rc)));
×
386
    }
×
387

388
#ifndef DNSDIST
389
    if(rc != MDB_NOTFOUND) {  // key was found, value was retrieved
390
      std::string sval = val.getNoStripHeader<std::string>();
391
      if (LMDBLS::LSisDeleted(sval)) {  // but it was deleted
392
        rc = MDB_NOTFOUND;
393
      }
394
    }
395
#endif
396

397
    return rc;
85✔
398
  }
85✔
399

400
  int get(MDB_dbi dbi, const MDBInVal& key, string_view& val)
401
  {
×
402
    MDBOutVal out;
×
403
    int rc = get(dbi, key, out);
×
404
    if(!rc)
×
405
      val = out.get<string_view>();
×
406
    return rc;
×
407
  }
×
408

409

410
  // this is something you can do, readonly
411
  MDBDbi openDB(string_view dbname, int flags)
412
  {
8✔
413
    return MDBDbi( d_parent->d_env, d_txn, dbname, flags);
8✔
414
  }
8✔
415

416
  MDBROCursor getCursor(const MDBDbi&);
417
  MDBROCursor getROCursor(const MDBDbi&);
418

419
  operator MDB_txn*()
420
  {
×
421
    return d_txn;
×
422
  }
×
423

424
  inline operator bool() const {
×
425
    return d_txn;
×
426
  }
×
427

428
  inline MDBEnv &environment()
429
  {
8✔
430
    return *d_parent;
8✔
431
  }
8✔
432
};
433

434
/*
435
   A cursor in a read-only transaction must be closed explicitly, before or after its transaction ends. It can be reused with mdb_cursor_renew() before finally closing it.
436

437
   "If the parent transaction commits, the cursor must not be used again."
438
*/
439

440
template<class Transaction, class T>
441
class MDBGenCursor
442
{
443
private:
444
  std::vector<T*> *d_registry;
445
  MDB_cursor* d_cursor{nullptr};
446
public:
447
  MDB_txn* d_txn{nullptr}; // ew, public
448
  uint64_t d_txtime{0};
449

450
  MDBGenCursor():
451
    d_registry(nullptr),
452
    d_cursor(nullptr),
453
    d_txn(nullptr)
454
  {
455

456
  }
457

458
  MDBGenCursor(std::vector<T*> &registry, MDB_cursor *cursor, MDB_txn *txn=nullptr, uint64_t txtime=0):
459
    d_registry(&registry),
460
    d_cursor(cursor),
461
    d_txn(txn),
462
    d_txtime(txtime)
463
  {
14✔
464
    registry.emplace_back(static_cast<T*>(this));
14✔
465
  }
14✔
466

467
private:
468
  void move_from(MDBGenCursor *src)
469
  {
470
    if (!d_registry) {
471
      return;
472
    }
473

474
    auto iter = std::find(d_registry->begin(),
475
                          d_registry->end(),
476
                          src);
477
    if (iter != d_registry->end()) {
478
      *iter = static_cast<T*>(this);
479
    } else {
480
      d_registry->emplace_back(static_cast<T*>(this));
481
    }
482
  }
483

484
public:
485
  MDBGenCursor(const MDBGenCursor &src) = delete;
486

487
  MDBGenCursor(MDBGenCursor &&src) noexcept:
488
    d_registry(src.d_registry),
489
    d_cursor(src.d_cursor)
490
  {
491
    move_from(&src);
492
    src.d_registry = nullptr;
493
    src.d_cursor = nullptr;
494
  }
495

496
  MDBGenCursor &operator=(const MDBGenCursor &src) = delete;
497

498
  MDBGenCursor &operator=(MDBGenCursor &&src) noexcept
499
  {
500
    d_registry = src.d_registry;
501
    d_cursor = src.d_cursor;
502
    move_from(&src);
503
    src.d_registry = nullptr;
504
    src.d_cursor = nullptr;
505
    return *this;
506
  }
507

508
  ~MDBGenCursor()
509
  {
14✔
510
    close();
14✔
511
  }
14✔
512

513
  /*
514
   to support (skip) entries marked deleted=1 in the LS header, we need to do some magic here
515
   this table notes, for each cursor op:
516
   * the maximum number of entries we may need to look at (1 or inf)
517
   * the subsequent op that needs to be done to skip over a deleted entry (or MDB_NOTFOUND to give up and say no)
518
   (table partially copied from http://www.lmdb.tech/doc/group__mdb.html#ga1206b2af8b95e7f6b0ef6b28708c9127 which I hope is a stable URL)
519
   (ops only relevant for DUPSORT/DUPFIXED have been omitted)
520
   (table is grouped by "skip op")
521

522
  | base op            | maxentries | skip op      | doc description of base op
523
  | MDB_FIRST          | inf        | MDB_NEXT     | Position at first key/data item
524
  | MDB_NEXT           | inf        | MDB_NEXT     | Position at next data item
525
  | MDB_SET_RANGE      | inf        | MDB_NEXT     | Position at first key greater than or equal to specified key.
526
  | MDB_LAST           | inf        | MDB_PREV     | Position at last key/data item
527
  | MDB_PREV           | inf        | MDB_PREV     | Position at previous data item
528
  | MDB_GET_CURRENT    | 1          | MDB_NOTFOUND | Return key/data at current cursor position
529
  | MDB_SET            | 1          | MDB_NOTFOUND | Position at specified key
530
  | MDB_SET_KEY        | 1          | MDB_NOTFOUND | Position at specified key, return key + data
531
  */
532

533
private:
534
  int skipDeleted(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op, int rc)
535
  {
14✔
536
#ifndef DNSDIST
537
    // when we get here
538
    // * mdb_cursor_get has been called once
539
    // * it did not return an error, but it might have returned MDB_NOTFOUND
540
    // * if it returned MDB_NOTFOUND, there is nothing for us to do and we pass that on
541

542
    if (rc == MDB_NOTFOUND) {
543
      return rc;
544
    }
545

546
    // when we get here
547
    // * mdb_cursor_get has been called at least once
548
    // * it found an entry, as far as LMDB is concerned, so key+data contain something
549
    // * but that might be a LS deleted=1 entry
550
    // * we know the cursor op that got us here
551

552
    while (true) {
553
      auto sval = data.getNoStripHeader<std::string_view>();
554

555
      if (!LMDBLS::LSisDeleted(sval)) {
556
        // done!
557

558
        return rc;
559
      }
560

561
      // the found entry is set deleted, so we need to do something
562

563
      // if this was a 1-entry op, this is the end
564
      if (op == MDB_GET_CURRENT || op == MDB_SET || op == MDB_SET_KEY) {
565
        return MDB_NOTFOUND;
566
      }
567

568
      // otherwise, we need to try to carry on
569
      // all ops that do not map to NOTFOUND map to NEXT or PREV, including NEXT and PREV themselves
570
      // so we just override the op to NEXT or PREV
571
      if (op == MDB_FIRST || op == MDB_NEXT || op == MDB_SET_RANGE) {
572
        op = MDB_NEXT;
573
      }
574
      else if (op == MDB_LAST || op == MDB_PREV) {
575
        op = MDB_PREV;
576
      }
577
      else {
578
        throw std::runtime_error("got unsupported mdb cursor op");
579
      }
580

581
      rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
582
      if(rc && rc != MDB_NOTFOUND) {
583
         throw std::runtime_error("Unable to get from cursor: " + std::string(mdb_strerror(rc)));
584
      }
585

586
      if (rc == MDB_NOTFOUND) {
587
        // we ended up finding nothing, so tell the caller
588
        return rc;
589
      }
590

591
      // when we get here
592
      // * the situation is just like the last time I wrote "when we get here"
593
      // * except mdb_cursor_get has been called at least twice
594
      // * so let's go back
595
    }
596
#else /* ifndef DNSDIST */
597
    return rc;
14✔
598
#endif
14✔
599
  }
14✔
600

601
public:
602
  int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
603
  {
604
    int rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
605
    if(rc && rc != MDB_NOTFOUND)
606
       throw std::runtime_error("Unable to get from cursor: " + std::string(mdb_strerror(rc)));
607
    return skipDeleted(key, data, op, rc);
608
  }
609

610
  int find(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data)
611
  {
612
    key.d_mdbval = in.d_mdbval;
613
    int rc=mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET);
614
    if(rc && rc != MDB_NOTFOUND)
615
       throw std::runtime_error("Unable to find from cursor: " + std::string(mdb_strerror(rc)));
616
    return skipDeleted(key, data, MDB_SET, rc);
617
  }
618

619
  int lower_bound(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data)
620
  {
14✔
621
    key.d_mdbval = in.d_mdbval;
14✔
622

623
    int rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET_RANGE);
14✔
624
    if(rc && rc != MDB_NOTFOUND)
14!
625
       throw std::runtime_error("Unable to lower_bound from cursor: " + std::string(mdb_strerror(rc)));
×
626
    return skipDeleted(key, data, MDB_SET_RANGE, rc);
14✔
627
  }
14✔
628

629

630
  int nextprev(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
631
  {
632
    int rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, op);
633
    if(rc && rc != MDB_NOTFOUND)
634
       throw std::runtime_error("Unable to prevnext from cursor: " + std::string(mdb_strerror(rc)));
635
    return skipDeleted(key, data, op, rc);
636
  }
637

638
  int next(MDBOutVal& key, MDBOutVal& data)
639
  {
640
    return nextprev(key, data, MDB_NEXT);
641
  }
642

643
  int prev(MDBOutVal& key, MDBOutVal& data)
644
  {
645
    return nextprev(key, data, MDB_PREV);
646
  }
647

648
  int currentlast(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
649
  {
650
    int rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, op);
651
    if(rc && rc != MDB_NOTFOUND)
652
       throw std::runtime_error("Unable to next from cursor: " + std::string(mdb_strerror(rc)));
653
    return skipDeleted(key, data, op, rc);
654
  }
655

656
  int current(MDBOutVal& key, MDBOutVal& data)
657
  {
658
    return currentlast(key, data, MDB_GET_CURRENT);
659
  }
660
  int last(MDBOutVal& key, MDBOutVal& data)
661
  {
662
    return currentlast(key, data, MDB_LAST);
663
  }
664
  int first(MDBOutVal& key, MDBOutVal& data)
665
  {
666
    return currentlast(key, data, MDB_FIRST);
667
  }
668

669
  operator MDB_cursor*()
670
  {
×
671
    return d_cursor;
×
672
  }
×
673

674
  operator bool() const
675
  {
676
    return d_cursor;
677
  }
678

679
  void close()
680
  {
28✔
681
    if (d_registry) {
28!
682
      auto iter = std::find(d_registry->begin(),
28✔
683
                            d_registry->end(),
28✔
684
                            static_cast<T*>(this));
28✔
685
      if (iter != d_registry->end()) {
28!
686
        d_registry->erase(iter);
28✔
687
      }
28✔
688
      d_registry = nullptr;
28✔
689
    }
28✔
690
    if (d_cursor) {
28!
691
      mdb_cursor_close(d_cursor);
28✔
692
      d_cursor = nullptr;
28✔
693
    }
28✔
694
  }
28✔
695
};
696

697
class MDBROCursor : public MDBGenCursor<MDBROTransactionImpl, MDBROCursor>
698
{
699
public:
700
  MDBROCursor() = default;
701
  using MDBGenCursor<MDBROTransactionImpl, MDBROCursor>::MDBGenCursor;
702
  MDBROCursor(const MDBROCursor& src) = delete;
703
  MDBROCursor(MDBROCursor&& src) = default;
704
  MDBROCursor& operator=(const MDBROCursor& src) = delete;
705
  MDBROCursor& operator=(MDBROCursor&& src) = default;
706
  ~MDBROCursor() = default;
707
};
708

709
class MDBRWCursor;
710

711
class MDBRWTransactionImpl : public MDBROTransactionImpl
712
{
713
protected:
714
  MDBRWTransactionImpl(MDBEnv* parent, MDB_txn* txn);
715

716
private:
717
  static MDB_txn* openRWTransaction(MDBEnv* env, MDB_txn* parent, int flags);
718

719
  std::vector<MDBRWCursor*> d_rw_cursors;
720

721
  uint64_t d_txtime{0};
722

723
  void closeRWCursors();
724
  inline void closeRORWCursors()
725
  {
4✔
726
    closeROCursors();
4✔
727
    closeRWCursors();
4✔
728
  }
4✔
729

730
public:
731
  explicit MDBRWTransactionImpl(MDBEnv* parent, int flags = 0);
732

733
  MDBRWTransactionImpl(const MDBRWTransactionImpl& rhs) = delete;
734
  MDBRWTransactionImpl(MDBRWTransactionImpl&& rhs) = delete;
735
  MDBRWTransactionImpl& operator=(const MDBRWTransactionImpl& rhs) = delete;
736
  MDBRWTransactionImpl& operator=(MDBRWTransactionImpl&& rhs) = delete;
737

738
  ~MDBRWTransactionImpl() override;
739

740
  void commit() override;
741
  void abort() override;
742

743
  void clear(MDB_dbi dbi);
744

745
#ifndef DNSDIST
746
  void put(MDB_dbi dbi, const MDBInVal& key, const MDBInVal& val, int flags = 0)
747
  {
748
    if (d_txn == nullptr) {
749
      throw std::runtime_error("Attempt to use a closed RW transaction for put");
750
    }
751

752
    size_t txid = mdb_txn_id(d_txn);
753

754
    if (d_txtime == 0) {
755
      throw std::runtime_error("got zero txtime");
756
    }
757

758
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
759
    std::string ins = LMDBLS::LSheader(d_txtime, txid).toString() + std::string((const char*)val.d_mdbval.mv_data, val.d_mdbval.mv_size);
760

761
    MDBInVal pval = ins;
762

763
    int mdbPutRc = mdb_put(d_txn, dbi,
764
                           // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
765
                           const_cast<MDB_val*>(&key.d_mdbval),
766
                           const_cast<MDB_val*>(&pval.d_mdbval), flags);
767
    if (mdbPutRc != 0) {
768
      throw std::runtime_error("putting data: " + std::string(mdb_strerror(mdbPutRc)));
769
    }
770
  }
771
#else
772
  void put(MDB_dbi dbi, const MDBInVal& key, const MDBInVal& val, int flags = 0)
773
  {
8✔
774
    if (!d_txn)
8!
775
      throw std::runtime_error("Attempt to use a closed RW transaction for put");
×
776
    int rc;
8✔
777
    if ((rc = mdb_put(d_txn, dbi,
8!
778
                      const_cast<MDB_val*>(&key.d_mdbval),
8✔
779
                      const_cast<MDB_val*>(&val.d_mdbval), flags)))
8✔
780
      throw std::runtime_error("putting data: " + std::string(mdb_strerror(rc)));
×
781
  }
8✔
782
#endif
783

784
  int del(MDBDbi& dbi, const MDBInVal& key)
785
  {
×
786
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
×
787
    int mdbDelRc = mdb_del(d_txn, dbi, (MDB_val*)&key.d_mdbval, nullptr);
×
788
    if ((mdbDelRc != 0) && mdbDelRc != MDB_NOTFOUND) {
×
789
      throw std::runtime_error("deleting data: " + std::string(mdb_strerror(mdbDelRc)));
×
790
    }
×
791
#ifndef DNSDIST
×
792
    if (mdbDelRc != MDB_NOTFOUND && LMDBLS::s_flag_deleted) {
×
793
      // if it did exist, we need to mark it as deleted now
×
794

×
795
      size_t txid = mdb_txn_id(d_txn);
×
796
      if (d_txtime == 0) {
×
797
        throw std::runtime_error("got zero txtime");
×
798
      }
×
799

×
800
      std::string ins =
×
801
        // std::string((const char*)&txid, sizeof(txid)) +
×
802
        LMDBLS::LSheader(d_txtime, txid, LMDBLS::LS_FLAG_DELETED).toString();
×
803

×
804
      MDBInVal pval = ins;
×
805

×
806
      mdbDelRc = mdb_put(d_txn, dbi,
×
807
                         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
×
808
                         const_cast<MDB_val*>(&key.d_mdbval),
×
809
                         const_cast<MDB_val*>(&pval.d_mdbval), 0);
×
810
      if (mdbDelRc != 0) {
×
811
        throw std::runtime_error("marking data deleted: " + std::string(mdb_strerror(mdbDelRc)));
×
812
      }
×
813
    }
×
814
#endif
×
815
    return mdbDelRc;
×
816
  }
×
817

818
  int get(MDBDbi& dbi, const MDBInVal& key, MDBOutVal& val)
819
  {
×
820
    if (d_txn == nullptr) {
×
821
      throw std::runtime_error("Attempt to use a closed RW transaction for get");
×
822
    }
×
823

×
824
    int mdbGetRc = mdb_get(d_txn, dbi,
×
825
                           // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
×
826
                           const_cast<MDB_val*>(&key.d_mdbval),
×
827
                           const_cast<MDB_val*>(&val.d_mdbval));
×
828
    if ((mdbGetRc != 0) && mdbGetRc != MDB_NOTFOUND) {
×
829
      throw std::runtime_error("getting data: " + std::string(mdb_strerror(mdbGetRc)));
×
830
    }
×
831

×
832
#ifndef DNSDIST
×
833
    if (mdbGetRc != MDB_NOTFOUND) { // key was found, value was retrieved
×
834
      auto sval = val.getNoStripHeader<std::string_view>();
×
835
      if (LMDBLS::LSisDeleted(sval)) { // but it was deleted
×
836
        mdbGetRc = MDB_NOTFOUND;
×
837
      }
×
838
    }
×
839
#endif
×
840

×
841
    return mdbGetRc;
×
842
  }
×
843

844
  MDBDbi openDB(string_view dbname, int flags)
845
  {
4✔
846
    return MDBDbi(environment().d_env, d_txn, dbname, flags);
4✔
847
  }
4✔
848

849
  MDBRWCursor getRWCursor(const MDBDbi&);
850
  MDBRWCursor getCursor(const MDBDbi&);
851

852
  MDBRWTransaction getRWTransaction();
853
  MDBROTransaction getROTransaction();
854
};
855

856
/* "A cursor in a write-transaction can be closed before its transaction ends, and will
857
 * otherwise be closed when its transaction ends". This is a problem for us since it may
858
 * means we are closing the cursor twice, which is bad.
859
 */
860
class MDBRWCursor : public MDBGenCursor<MDBRWTransactionImpl, MDBRWCursor>
861
{
862
public:
863
  MDBRWCursor() = default;
864
  using MDBGenCursor<MDBRWTransactionImpl, MDBRWCursor>::MDBGenCursor;
865
  MDBRWCursor(const MDBRWCursor &src) = delete;
866
  MDBRWCursor(MDBRWCursor &&src) = default;
867
  MDBRWCursor &operator=(const MDBRWCursor &src) = delete;
868
  MDBRWCursor &operator=(MDBRWCursor &&src) = default;
869
  ~MDBRWCursor() = default;
870

871
#ifndef DNSDIST
872
  void put(const MDBOutVal& key, const MDBInVal& data)
873
  {
874
    size_t txid = mdb_txn_id(this->d_txn);
875

876
    if (d_txtime == 0) { throw std::runtime_error("got zero txtime"); }
877

878
    std::string ins =
879
      LMDBLS::LSheader(d_txtime, txid).toString()+
880
      std::string((const char*)data.d_mdbval.mv_data, data.d_mdbval.mv_size);
881

882
    MDBInVal pval = ins;
883

884
    int rc = mdb_cursor_put(*this,
885
                            const_cast<MDB_val*>(&key.d_mdbval),
886
                            const_cast<MDB_val*>(&pval.d_mdbval), MDB_CURRENT);
887
    if(rc)
888
      throw std::runtime_error("mdb_cursor_put: " + std::string(mdb_strerror(rc)));
889
  }
890
#else
891
  void put(const MDBOutVal& key, const MDBInVal& data)
892
  {
×
893
    int rc = mdb_cursor_put(*this,
×
894
                            const_cast<MDB_val*>(&key.d_mdbval),
×
895
                            const_cast<MDB_val*>(&data.d_mdbval), MDB_CURRENT);
×
896
    if(rc)
×
897
      throw std::runtime_error("mdb_cursor_put: " + std::string(mdb_strerror(rc)));
×
898
  }
×
899
#endif
900

901
#ifndef DNSDIST
902
  int del(int flags=0)
903
  {
904
    MDBOutVal key, val;
905

906
    if (LMDBLS::s_flag_deleted) {
907
      int rc_get = mdb_cursor_get (*this, &key.d_mdbval, &val.d_mdbval, MDB_GET_CURRENT);
908

909
      if(rc_get) {
910
              throw std::runtime_error("getting key to mark data as deleted: " + std::string(mdb_strerror(rc_get)));
911
      }
912

913
      size_t txid = mdb_txn_id(d_txn);
914
      if (d_txtime == 0) { throw std::runtime_error("got zero txtime"); }
915

916
      std::string ins =
917
        LMDBLS::LSheader(d_txtime, txid, LMDBLS::LS_FLAG_DELETED).toString();
918

919
      std::string skey((const char*)key.d_mdbval.mv_data, key.d_mdbval.mv_size);
920

921
      MDBInVal pkey = MDBInVal(skey);
922
      MDBInVal pval = ins;
923

924
      int rc_put = mdb_cursor_put(*this,
925
                     const_cast<MDB_val*>(&pkey.d_mdbval),
926
                     const_cast<MDB_val*>(&pval.d_mdbval), 0 /* MDB_CURRENT */);
927
      if(rc_put) {
928
              throw std::runtime_error("marking data deleted: " + std::string(mdb_strerror(rc_put)));
929
      }
930
      return rc_put;
931
    }
932
    else {
933
      // do a normal delete
934
      return mdb_cursor_del(*this, flags);
935
    }
936
  }
937
#endif
938
};
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