384 lines
10 KiB
C++
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);
|
|
};
|
|
}
|
|
|