reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
BerkeleyDb.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include <fcntl.h>
6#include "rutil/ResipAssert.h"
7#include <cstdlib>
8
9#include "rutil/Data.hxx"
10#include "rutil/DataStream.hxx"
11#include "rutil/Logger.hxx"
12
13#include "repro/AbstractDb.hxx"
14#include "repro/BerkeleyDb.hxx"
16
17using namespace resip;
18using namespace repro;
19using namespace std;
20
21#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
22//#define USE_DBENV // Required for transaction support
23
28
29
30BerkeleyDb::BerkeleyDb( const Data& dbPath, const Data& dbName )
31{
32 init(dbPath, dbName);
33}
34
35
36void
37BerkeleyDb::init( const Data& dbPath, const Data& dbName )
38{
39 Data filePath(dbPath);
40
41 // An empty path is how you specify the current working directory as a path
42 if ( !filePath.empty() )
43 {
44#ifdef WIN32
45 filePath += '\\';
46#else
47 filePath += '/';
48#endif
49 }
50
51 if ( dbName.empty() )
52 {
53 DebugLog( << "No BerkeleyDb prefix specified - using default" );
54 filePath += "repro";
55 }
56 else
57 {
58 filePath += dbName;
59 }
60
61 InfoLog( << "Using BerkeleyDb prefixed with " << filePath );
62
63 mSane = true;
64
65 // Create Environment
66 int ret;
67#ifdef USE_DBENV
68 mEnv = new DbEnv(DB_CXX_NO_EXCEPTIONS);
69 resip_assert(mEnv);
70 ret = mEnv->open(0, DB_CREATE | // If the env does not exist, then create it
71 DB_INIT_LOCK | // Initialize Locking (needed for transactions)
72 DB_INIT_LOG | // Initialize Logging (needed for transactions)
73 DB_INIT_MPOOL | // Initialize the cache (needed for transactions)
74 DB_INIT_TXN | // Initialize transactions
75 DB_RECOVER | // Run normal recovery
76 DB_THREAD, // Free-thread the env handle
77 0 /* mode */);
78 if(ret != 0)
79 {
80 ErrLog( <<"Could not open environment: " << db_strerror(ret));
81 mSane = false;
82 return;
83 }
84 mEnv->txn_checkpoint(0, 0, 0); // Note: a checkpoint is run when this last is created and when it is destroyed
85#else
86 mEnv = 0;
87#endif
88
89#ifdef USE_DBENV
90 bool enableTransactions = false;
91#endif
92 bool secondaryIndex = false;
93 Data secondaryFileName;
94 for (int i=0;i<MaxTable;i++)
95 {
96#ifdef USE_DBENV
97 enableTransactions = false;
98#endif
99 // if the line bellow seems wrong, you need to check which version
100 // of db you have - it is likely an very out of date version
101 // still trying to figure this out so email fluffy if you have
102 // problems and include your version the DB_VERSION_STRING found
103 // in your db4/db.h file.
104 Data fileName( filePath );
105 switch (i)
106 {
107 case UserTable:
108 fileName += "_user"; break;
110 fileName += "_tlspeeridentity"; break;
111 case RouteTable:
112 fileName += "_route"; break;
113 case AclTable:
114 fileName += "_acl"; break;
115 case ConfigTable:
116 fileName += "_config"; break;
117 case StaticRegTable:
118 fileName += "_staticreg"; break;
119 case FilterTable:
120 fileName += "_filter"; break;
121 case SiloTable:
122 fileName += "_silo";
123#ifdef USE_DBENV
124 enableTransactions = true;
125#endif
126 secondaryIndex = true;
127 break;
128 default:
129 resip_assert(0);
130 }
131
132 if(!secondaryIndex)
133 {
134 fileName += ".db";
135 }
136 else
137 {
138 secondaryFileName = fileName;
139 fileName += ".db";
140 secondaryFileName += "_idx1.db";
141 }
142
143 mTableInfo[i].mDb = new Db(mEnv, DB_CXX_NO_EXCEPTIONS);
144 resip_assert(mTableInfo[i].mDb);
145
146 DebugLog( << "About to open Berkeley DB: " << fileName );
147 ret = mTableInfo[i].mDb->open(0,
148 fileName.c_str(),
149 0,
150 DB_BTREE,
151#ifdef USE_DBENV
152 DB_CREATE | DB_THREAD | (enableTransactions ? DB_AUTO_COMMIT : 0),
153#else
154 DB_CREATE | DB_THREAD,
155#endif
156 0);
157 if(ret != 0)
158 {
159 ErrLog( <<"Could not open database " << fileName << ": " << db_strerror(ret));
160 mSane = false;
161 return;
162 }
163
164 // Open a cursor on the database
165 ret = mTableInfo[i].mDb->cursor(0, &mTableInfo[i].mCursor, 0);
166 if(ret != 0)
167 {
168 ErrLog( <<"Could not cursor on database " << fileName << ": " << db_strerror(ret));
169 mSane = false;
170 return;
171 }
172 resip_assert(mTableInfo[i].mCursor);
173
174 DebugLog( << "Opened Berkeley DB: " << fileName );
175
176
177 if(secondaryIndex)
178 {
179 mTableInfo[i].mSecondaryDb = new Db(mEnv, DB_CXX_NO_EXCEPTIONS);
180 resip_assert(mTableInfo[i].mSecondaryDb);
181
182 ret = mTableInfo[i].mSecondaryDb->set_flags(DB_DUP);
183 if(ret!=0)
184 {
185 ErrLog( <<"Could not set database " << secondaryFileName << " to allow duplicates: " << db_strerror(ret));
186 mSane = false;
187 return;
188 }
189
190 DebugLog( << "About to open secondary Berkeley DB: " << secondaryFileName );
191 ret = mTableInfo[i].mSecondaryDb->open(0,
192 secondaryFileName.c_str(),
193 0,
194 DB_BTREE,
195#ifdef USE_DBENV
196 DB_CREATE | DB_THREAD | (enableTransactions ? DB_AUTO_COMMIT : 0),
197#else
198 DB_CREATE | DB_THREAD,
199#endif
200 0);
201 if(ret != 0)
202 {
203 ErrLog( <<"Could not open secondary database " << secondaryFileName << ": " << db_strerror(ret));
204 mSane = false;
205 return;
206 }
207
208 // Associate Secondary Database with Primary
209 mTableInfo[i].mSecondaryDb->set_app_private(this); // retrievable from callback so we can have access to this BerkeleyDb instance
210 ret = mTableInfo[i].mDb->associate(0, mTableInfo[i].mSecondaryDb, &getSecondaryKeyCallback, 0 /* flags */);
211 if(ret != 0)
212 {
213 ErrLog( <<"Could not associate secondary database " << secondaryFileName << ": " << db_strerror(ret));
214 mSane = false;
215 return;
216 }
217 DebugLog( << "Opened secondary Berkeley DB: " << secondaryFileName );
218
219 ret = mTableInfo[i].mSecondaryDb->cursor(0, &mTableInfo[i].mSecondaryCursor, 0);
220 if(ret != 0)
221 {
222 ErrLog( <<"Could not secondary cursor on database " << secondaryFileName << ": " << db_strerror(ret));
223 mSane = false;
224 return;
225 }
226 resip_assert(mTableInfo[i].mSecondaryCursor);
227 }
228 }
229}
230
231
233{
234 for (int i=0;i<MaxTable;i++)
235 {
236 if(mTableInfo[i].mSecondaryCursor)
237 {
238 mTableInfo[i].mSecondaryCursor->close();
239 mTableInfo[i].mSecondaryCursor = 0;
240 }
241
242 if(mTableInfo[i].mCursor)
243 {
244 mTableInfo[i].mCursor->close();
245 mTableInfo[i].mCursor = 0;
246 }
247
248 if(mTableInfo[i].mTransaction)
249 {
250 dbRollbackTransaction((Table)i);
251 }
252
253 // Secondary DB should be closed before primary
254 if(mTableInfo[i].mSecondaryDb)
255 {
256 mTableInfo[i].mSecondaryDb->close(0);
257 delete mTableInfo[i].mSecondaryDb;
258 mTableInfo[i].mSecondaryDb = 0;
259 }
260
261 if(mTableInfo[i].mDb)
262 {
263 mTableInfo[i].mDb->close(0);
264 delete mTableInfo[i].mDb;
265 mTableInfo[i].mDb = 0;
266 }
267 }
268 if(mEnv)
269 {
270 mEnv->txn_checkpoint(0, 0, 0); // Note: a checkpoint is run when this last is created and when it is destroyed
271 delete mEnv;
272 }
273}
274
275
276int
277BerkeleyDb::getSecondaryKeyCallback(Db *db, const Dbt *pkey, const Dbt *pdata, Dbt *skey)
278{
279 BerkeleyDb* bdb = (BerkeleyDb*)db->get_app_private();
280
281 // Find associated table using db pointer
282 Table table = MaxTable;
283 for (int i=MaxTable-1; i >= 0; i--) // search backwards, since tables at the end have the secondary indexes
284 {
285 if(bdb->mTableInfo[i].mSecondaryDb == db)
286 {
287 table = (Table)i;
288 break;
289 }
290 }
291 resip_assert(table != MaxTable);
292
293 Data primaryKey(Data::Share, reinterpret_cast<const char*>(pkey->get_data()), pkey->get_size());
294 Data primaryData(Data::Share, reinterpret_cast<const char*>(pdata->get_data()), pdata->get_size());
295 void* secondaryKey;
296 unsigned int secondaryKeyLen;
297 int rc = bdb->getSecondaryKey(table, primaryKey, primaryData, &secondaryKey, &secondaryKeyLen);
298 skey->set_data(secondaryKey);
299 skey->set_size(secondaryKeyLen);
300 return rc;
301}
302
303
304bool
305BerkeleyDb::dbWriteRecord(const Table table,
306 const resip::Data& pKey,
307 const resip::Data& pData )
308{
309 Dbt key((void*)pKey.data(), (::u_int32_t)pKey.size());
310 Dbt data((void*)pData.data(), (::u_int32_t)pData.size());
311 int ret;
312
313 resip_assert(mTableInfo[table].mDb);
314 ret = mTableInfo[table].mDb->put(mTableInfo[table].mTransaction, &key, &data, 0);
315
316 if(ret == 0 && mTableInfo[table].mTransaction == 0)
317 {
318 // If we are in a transaction, then it will sync on commit
319 mTableInfo[table].mDb->sync(0);
320 if(mTableInfo[table].mSecondaryDb)
321 {
322 mTableInfo[table].mSecondaryDb->sync(0);
323 }
324 }
325 return ret == 0;
326}
327
328
329bool
330BerkeleyDb::dbReadRecord(const Table table,
331 const resip::Data& pKey,
332 resip::Data& pData) const
333{
334 Dbt key((void*)pKey.data(), (::u_int32_t)pKey.size());
335 Dbt data;
336 data.set_flags(DB_DBT_MALLOC); // required for DB_THREAD flag use
337
338 int ret;
339
340 resip_assert(mTableInfo[table].mDb);
341 ret = mTableInfo[table].mDb->get(mTableInfo[table].mTransaction, &key, &data, 0);
342
343 if (ret == DB_NOTFOUND)
344 {
345 // key not found
346 if (data.get_data())
347 {
348 free(data.get_data());
349 }
350 return false;
351 }
352 resip_assert(ret != DB_KEYEMPTY);
353 resip_assert(ret == 0);
354 pData.copy(reinterpret_cast<const char*>(data.get_data()), data.get_size());
355 if (data.get_data())
356 {
357 free(data.get_data());
358 }
359 if(pData.empty())
360 {
361 // this should never happen
362 return false;
363 }
364
365 return true;
366}
367
368
369void
370BerkeleyDb::dbEraseRecord(const Table table,
371 const resip::Data& pKey,
372 bool isSecondaryKey) // allows deleting records from a table that supports secondary keying using a secondary key
373{
374 Dbt key((void*) pKey.data(), (::u_int32_t)pKey.size());
375
376 Db* db = mTableInfo[table].mDb;
377 if(isSecondaryKey && mTableInfo[table].mSecondaryDb)
378 {
379 db = mTableInfo[table].mSecondaryDb;
380 }
381 resip_assert(db);
382 db->del(mTableInfo[table].mTransaction, &key, 0);
383 if(mTableInfo[table].mTransaction == 0)
384 {
385 // If we are in a transaction, then it will sync on commit
386 mTableInfo[table].mDb->sync(0);
387 if(mTableInfo[table].mSecondaryDb)
388 {
389 mTableInfo[table].mSecondaryDb->sync(0);
390 }
391 }
392}
393
394
396BerkeleyDb::dbNextKey(const Table table,
397 bool first)
398{
399 Dbt key, data;
400 int ret;
401
402 resip_assert(mTableInfo[table].mDb);
403 ret = mTableInfo[table].mCursor->get(&key, &data, first ? DB_FIRST : DB_NEXT);
404 if (ret == DB_NOTFOUND)
405 {
406 return Data::Empty;
407 }
408 resip_assert(ret == 0);
409
410 Data d(Data::Share, reinterpret_cast<const char*>(key.get_data()), key.get_size());
411 return d;
412}
413
414
415bool
416BerkeleyDb::dbNextRecord(const Table table,
417 const resip::Data& key,
418 resip::Data& data,
419 bool forUpdate, // specifies to use DB_RMW flag to write lock reads
420 bool first)
421{
422 Dbt dbkey((void*) key.data(), (::u_int32_t)key.size());
423 Dbt dbdata;
424 int ret;
425
426 resip_assert(mTableInfo[table].mSecondaryCursor);
427 if(mTableInfo[table].mSecondaryCursor == 0)
428 {
429 // Iterating across multiple records with a common key is only
430 // supported on Seconday databases where duplicate keys exist
431 return false;
432 }
433
434 unsigned int flags = 0;
435 if(key.empty())
436 {
437 flags = first ? DB_FIRST : DB_NEXT;
438 }
439 else
440 {
441 flags = first ? DB_SET : DB_NEXT_DUP;
442 }
443
444#ifdef USE_DBENV
445 if(forUpdate)
446 {
447 flags |= DB_RMW;
448 }
449#endif
450
451 ret = mTableInfo[table].mSecondaryCursor->get(&dbkey, &dbdata, flags);
452 if (ret == DB_NOTFOUND)
453 {
454 return false;
455 }
456 resip_assert(ret == 0);
457 data.copy(reinterpret_cast<const char*>(dbdata.get_data()), dbdata.get_size());
458
459 return true;
460}
461
462bool
463BerkeleyDb::dbBeginTransaction(const Table table)
464{
465#ifdef USE_DBENV
466 // For now - we support transactions on the primary table only
467 resip_assert(mDb);
468 resip_assert(mTableInfo[table].mTransaction == 0);
469 int ret = mTableInfo[table].mDb->get_env()->txn_begin(0 /* parent trans*/, &mTableInfo[table].mTransaction, 0);
470 if(ret != 0)
471 {
472 ErrLog( <<"Could not begin transaction: " << db_strerror(ret));
473 return false;
474 }
475
476 // Open new Cursors - since cursors used in a transaction must be opened and closed within the transaction
477 if(mTableInfo[table].mCursor)
478 {
479 mTableInfo[table].mCursor->close();
480 mTableInfo[table].mCursor = 0;
481 }
482
483 ret = mTableInfo[table].mDb->cursor(mTableInfo[table].mTransaction, &mTableInfo[table].mCursor, 0);
484 if(ret != 0)
485 {
486 ErrLog( <<"Could not open cursor for transaction: " << db_strerror(ret));
487 }
488#endif
489
490 return true;
491}
492
493bool
494BerkeleyDb::dbCommitTransaction(const Table table)
495{
496 bool success = true;
497#ifdef USE_DBENV
498 resip_assert(mDb);
499 resip_assert(mTableInfo[table].mTransaction);
500
501 // Close the cursor - since cursors used in a transaction must be opened and closed within the transaction
502 if(mTableInfo[table].mCursor)
503 {
504 mTableInfo[table].mCursor->close();
505 mTableInfo[table].mCursor = 0;
506 }
507
508 int ret = mTableInfo[table].mTransaction->commit(0);
509 mTableInfo[table].mTransaction = 0;
510 if(ret != 0)
511 {
512 ErrLog( <<"Could not commit transaction: " << db_strerror(ret));
513 success = false;
514 }
515
516 // Reopen a cursor for general use
517 mTableInfo[table].mDb->cursor(0, &mTableInfo[table].mCursor, 0);
518#endif
519
520 return success;
521}
522
523bool
524BerkeleyDb::dbRollbackTransaction(const Table table)
525{
526 bool success = true;
527#ifdef USE_DBENV
528 resip_assert(mDb);
529 resip_assert(mTableInfo[table].mTransaction);
530
531 // Close the cursor - since cursors used in a transaction must be opened and closed within the transaction
532 if(mTableInfo[table].mCursor)
533 {
534 mTableInfo[table].mCursor->close();
535 mTableInfo[table].mCursor = 0;
536 }
537
538 int ret = mTableInfo[table].mTransaction->abort();
539 mTableInfo[table].mTransaction = 0;
540 if(ret != 0)
541 {
542 success = false;
543 }
544
545 // Reopen a cursor for general use
546 mTableInfo[table].mDb->cursor(0, &mTableInfo[table].mCursor, 0);
547#endif
548
549 return success;
550}
551
552
553/* ====================================================================
554 * The Vovida Software License, Version 1.0
555 *
556 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
557 *
558 * Redistribution and use in source and binary forms, with or without
559 * modification, are permitted provided that the following conditions
560 * are met:
561 *
562 * 1. Redistributions of source code must retain the above copyright
563 * notice, this list of conditions and the following disclaimer.
564 *
565 * 2. Redistributions in binary form must reproduce the above copyright
566 * notice, this list of conditions and the following disclaimer in
567 * the documentation and/or other materials provided with the
568 * distribution.
569 *
570 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
571 * and "Vovida Open Communication Application Library (VOCAL)" must
572 * not be used to endorse or promote products derived from this
573 * software without prior written permission. For written
574 * permission, please contact vocal@vovida.org.
575 *
576 * 4. Products derived from this software may not be called "VOCAL", nor
577 * may "VOCAL" appear in their name, without prior written
578 * permission of Vovida Networks, Inc.
579 *
580 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
581 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
582 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
583 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
584 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
585 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
586 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
587 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
588 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
589 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
590 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
591 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
592 * DAMAGE.
593 *
594 * ====================================================================
595 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
static const struct nv flags[]
Definition adig.c:62
virtual int getSecondaryKey(const Table table, const Key &key, const resip::Data &data, void **secondaryKey, unsigned int *secondaryKeyLen)
virtual ~BerkeleyDb()
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
@ Share
The Data instance will use the buffer in a read-only mode.
Definition Data.hxx:266
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
Data & copy(const char *buf, size_type length)
Functional equivalent of: *this = Data(buf, length) but avoids the intermediate allocation and free.
Definition Data.cxx:701
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;