QxtGlobalShortcut/src/berkeley/qxtbdb.cpp
2011-09-06 15:21:07 +02:00

384 lines
10 KiB
C++

#include "qxtbdb.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include <QFileInfo>
#include <QBuffer>
#include <QDataStream>
#include <QVariant>
static void qxtBDBDatabaseErrorHandler(const BerkeleyDB::DB_ENV*, const char* a, const char* b)
{
qDebug("QxtBDBDatabase: %s, %s", a, b);
}
QxtBdb::QxtBdb()
{
isOpen = false;
if (db_create(&db, NULL, 0) != 0)
qFatal("db_create failed");
db->set_errcall(db, qxtBDBDatabaseErrorHandler);
}
QxtBdb::~QxtBdb()
{
db->close(db, 0);
}
bool QxtBdb::open(QString path, OpenFlags f)
{
Q_ASSERT(!isOpen);
if (QFileInfo(path).exists())
{
BerkeleyDB::DB * tdb;
if (db_create(&tdb, NULL, 0) != 0)
qFatal("db_create failed");
if (tdb->verify(tdb, qPrintable(path), NULL, NULL, 0) == DB_VERIFY_BAD)
qCritical("QxtBdb::open Database '%s' is corrupted.", qPrintable(path));
}
int flags = 0;
if (f&CreateDatabase)
flags |= DB_CREATE;
if (f&ReadOnly)
flags |= DB_RDONLY;
if (f&LockFree)
flags |= DB_THREAD;
isOpen = (db->open(db, /* DB structure pointer */
NULL, /* Transaction pointer */
qPrintable(path), /* On-disk file that holds the database. */
NULL, /* Optional logical database name */
BerkeleyDB::DB_BTREE, /* Database access method */
flags, /* Open flags */
0)
== 0);
return isOpen;
}
QxtBdb::OpenFlags QxtBdb::openFlags()
{
if (!isOpen)
return 0;
OpenFlags f;
BerkeleyDB::u_int32_t open_flags;
db->get_open_flags(db, &open_flags);
if (open_flags&DB_CREATE)
f |= CreateDatabase;
if (open_flags&DB_RDONLY)
f |= ReadOnly;
if (open_flags&DB_THREAD)
f |= LockFree;
return f;
}
bool QxtBdb::flush()
{
if (!isOpen)
return false;
return (db->sync(db, 0) == 0);
}
/*!
low level get function.
serialised key and value with the given meta ids.
always reads <b>and</b> writes both key and value, if given.
use this when doing operations that require the key to be read out of the db.
*/
bool QxtBdb::get(void* key, int keytype, void* value, int valuetype, BerkeleyDB::u_int32_t flags, BerkeleyDB::DBC * cursor) const
{
BerkeleyDB::DBT dbkey, dbvalue;
::memset(&dbkey, 0, sizeof(BerkeleyDB::DBT));
::memset(&dbvalue, 0, sizeof(BerkeleyDB::DBT));
if (key)
{
QByteArray d_key;
QBuffer buffer(&d_key);
buffer.open(QIODevice::WriteOnly);
QDataStream s(&buffer);
if (!QMetaType::save(s, keytype, key))
qCritical("QMetaType::save failed. is your key registered with the QMetaType?");
buffer.close();
dbkey.size = d_key.size();
dbkey.data = ::malloc(d_key.size());
::memcpy(dbkey.data, d_key.data(), d_key.size());
}
if (value)
{
QByteArray d_value;
QBuffer buffer(&d_value);
buffer.open(QIODevice::WriteOnly);
QDataStream s(&buffer);
if (!QMetaType::save(s, valuetype, value))
qCritical("QMetaType::save failed. is your value registered with the QMetaType?");
buffer.close();
dbvalue.size = d_value.size();
dbvalue.data = ::malloc(d_value.size());
::memcpy(dbvalue.data, d_value.data(), d_value.size());
}
dbvalue.ulen = 0;
dbvalue.flags = DB_DBT_USERMEM;
dbkey.ulen = 0;
dbkey.flags = DB_DBT_USERMEM;
int ret;
if (cursor)
ret = cursor->c_get(cursor, &dbkey, &dbvalue, flags);
else
ret = db->get(db, NULL, &dbkey, &dbvalue, flags);
if (ret != DB_BUFFER_SMALL)
{
::free(dbvalue.data);
::free(dbkey.data);
return (ret == 0);
}
dbvalue.ulen = dbvalue.size;
dbvalue.data =::malloc(dbvalue.size);
dbkey.ulen = dbkey.size;
dbkey.data =::malloc(dbkey.size);
if (cursor)
ret = cursor->c_get(cursor, &dbkey, &dbvalue, flags);
else
ret = db->get(db, NULL, &dbkey, &dbvalue, flags);
QByteArray d_value = QByteArray::fromRawData((const char*) dbvalue.data, dbvalue.size);
QByteArray d_key = QByteArray::fromRawData((const char*) dbkey.data, dbkey.size);
if (ret != 0)
{
::free(dbvalue.data);
::free(dbkey.data);
return false;
}
if (key)
{
QBuffer buffer(&d_key);
buffer.open(QIODevice::ReadOnly);
QDataStream s(&buffer);
if (!QMetaType::load(s, keytype, key))
qCritical("QMetaType::load failed. is your key registered with the QMetaType?");
buffer.close();
}
if (value)
{
QBuffer buffer(&d_value);
buffer.open(QIODevice::ReadOnly);
QDataStream s(&buffer);
if (!QMetaType::load(s, valuetype, value))
qCritical("QMetaType::load failed. is your value registered with the QMetaType?");
buffer.close();
}
::free(dbvalue.data);
::free(dbkey.data);
return true;
}
/*!
low level get function.
serialised key and value with the given meta ids.
doesn't write to the key. use this when doing operations that require the key to be passed.
*/
bool QxtBdb::get(const void* key, int keytype, void* value, int valuetype, BerkeleyDB::u_int32_t flags, BerkeleyDB::DBC * cursor) const
{
BerkeleyDB::DBT dbkey, dbvalue;
memset(&dbkey, 0, sizeof(BerkeleyDB::DBT));
memset(&dbvalue, 0, sizeof(BerkeleyDB::DBT));
if (key)
{
QByteArray d_key;
QBuffer buffer(&d_key);
buffer.open(QIODevice::WriteOnly);
QDataStream s(&buffer);
if (!QMetaType::save(s, keytype, key))
qCritical("QMetaType::save failed. is your key registered with the QMetaType?");
buffer.close();
dbkey.size = d_key.size();
dbkey.data = ::malloc(d_key.size());
::memcpy(dbkey.data, d_key.data(), d_key.size());
}
if (value)
{
QByteArray d_value;
QBuffer buffer(&d_value);
buffer.open(QIODevice::WriteOnly);
QDataStream s(&buffer);
if (!QMetaType::save(s, valuetype, value))
qCritical("QMetaType::save failed. is your value registered with the QMetaType?");
buffer.close();
dbvalue.size = d_value.size();
dbvalue.data = ::malloc(d_value.size());
::memcpy(dbvalue.data, d_value.data(), d_value.size());
}
dbvalue.ulen = 0;
dbvalue.flags = DB_DBT_USERMEM;
dbkey.ulen = 0;
dbkey.flags = 0;
dbkey.flags = DB_DBT_USERMEM; //it's my memory, ffs. stop deleting it! >_<
int ret;
if (cursor)
ret = cursor->c_get(cursor, &dbkey, &dbvalue, flags);
else
ret = db->get(db, NULL, &dbkey, &dbvalue, flags);
if (ret != DB_BUFFER_SMALL)
{
::free(dbvalue.data);
::free(dbkey.data);
return (ret == 0);
}
dbvalue.ulen = dbvalue.size;
dbvalue.data =::malloc(dbvalue.size);
if (cursor)
ret = cursor->c_get(cursor, &dbkey, &dbvalue, flags);
else
ret = db->get(db, NULL, &dbkey, &dbvalue, flags);
QByteArray d_value((const char*) dbvalue.data, dbvalue.size);
QByteArray d_key((const char*) dbkey.data, dbkey.size);
::free(dbvalue.data);
::free(dbkey.data);
Q_ASSERT_X(ret != DB_BUFFER_SMALL, Q_FUNC_INFO, "QxtBdb::get bdb inists on retriving the key for this operation. You need to specify a non const key. (or just specify a non const void* with the value of 0, i'll delete the key for you after bdb fetched it, so you don't need to bother)");
if (ret != 0)
{
return false;
}
if (value)
{
QBuffer buffer(&d_value);
buffer.open(QIODevice::ReadOnly);
QDataStream s(&buffer);
if (!QMetaType::load(s, valuetype, value))
qCritical("QMetaType::load failed. is your value registered with the QMetaType?");
buffer.close();
}
return true;
}
QString QxtBdb::dbErrorCodeToString(int e)
{
switch (e)
{
case DB_LOCK_DEADLOCK:
return QString("Dead locked (%1)").arg(e);
case DB_SECONDARY_BAD:
return QString("Bad Secondary index (%1)").arg(e);
case DB_RUNRECOVERY:
return QString("Database corrupted. Run Recovery. (%1)").arg(e);
default:
return QString("Unknown error %1").arg(e);
};
}