|
|
|
/* This file is part of KDevelop
|
|
|
|
Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include "catalog.h"
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqdatastream.h>
|
|
|
|
|
|
|
|
#include <krandomsequence.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <db.h>
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
struct _Catalog_Private
|
|
|
|
{
|
|
|
|
TQString dbName;
|
|
|
|
|
|
|
|
DB* dbp;
|
|
|
|
TQMap<TQCString, DB*> indexList;
|
|
|
|
KRandomSequence rnd;
|
|
|
|
bool enabled;
|
|
|
|
|
|
|
|
_Catalog_Private()
|
|
|
|
: dbp( 0 ), enabled( true )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasIndex( const TQCString& name ) const
|
|
|
|
{
|
|
|
|
return indexList.contains( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
DB* index( const TQCString& name )
|
|
|
|
{
|
|
|
|
return indexList[ name ];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool addItem( DB* dbp, const TQCString& id, const Tag& tag )
|
|
|
|
{
|
|
|
|
Q_ASSERT( dbp != 0 );
|
|
|
|
|
|
|
|
DBT key, data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
std::memset( &key, 0, sizeof(key) );
|
|
|
|
std::memset( &data, 0, sizeof(data) );
|
|
|
|
|
|
|
|
TQByteArray a1;
|
|
|
|
{
|
|
|
|
TQDataStream stream( a1, IO_WriteOnly );
|
|
|
|
stream << id;
|
|
|
|
key.data = a1.data();
|
|
|
|
key.size = a1.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQByteArray a2;
|
|
|
|
{
|
|
|
|
TQDataStream stream( a2, IO_WriteOnly );
|
|
|
|
tag.store( stream );
|
|
|
|
data.data = a2.data();
|
|
|
|
data.size = a2.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dbp->put( dbp, 0, &key, &data, 0 );
|
|
|
|
|
|
|
|
return ret == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool addItem( DB* dbp, const TQVariant& id, const TQCString& v )
|
|
|
|
{
|
|
|
|
Q_ASSERT( dbp != 0 );
|
|
|
|
|
|
|
|
DBT key, data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
std::memset( &key, 0, sizeof(key) );
|
|
|
|
std::memset( &data, 0, sizeof(data) );
|
|
|
|
|
|
|
|
TQByteArray a1;
|
|
|
|
{
|
|
|
|
TQDataStream stream( a1, IO_WriteOnly );
|
|
|
|
stream << id;
|
|
|
|
key.data = a1.data();
|
|
|
|
key.size = a1.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQByteArray a2;
|
|
|
|
{
|
|
|
|
TQDataStream stream( a2, IO_WriteOnly );
|
|
|
|
stream << v;
|
|
|
|
data.data = a2.data();
|
|
|
|
data.size = a2.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dbp->put( dbp, 0, &key, &data, 0 );
|
|
|
|
|
|
|
|
return ret == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::Catalog
|
|
|
|
*/
|
|
|
|
Catalog::Catalog()
|
|
|
|
: d( new _Catalog_Private() )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::~Catalog
|
|
|
|
*/
|
|
|
|
Catalog::~Catalog()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
delete( d );
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::indexList() const
|
|
|
|
*/
|
|
|
|
TQValueList<TQCString> Catalog::indexList() const
|
|
|
|
{
|
|
|
|
TQValueList<TQCString> l;
|
|
|
|
TQMap<TQCString, DB*>::Iterator it = d->indexList.begin();
|
|
|
|
while( it != d->indexList.end() ){
|
|
|
|
l << it.key();
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Catalog::enabled() const
|
|
|
|
{
|
|
|
|
return d->enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Catalog::setEnabled( bool isEnabled )
|
|
|
|
{
|
|
|
|
d->enabled = isEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::addIndex( const TQString& name )
|
|
|
|
@todo document these functions
|
|
|
|
*/
|
|
|
|
void Catalog::addIndex( const TQCString& name )
|
|
|
|
{
|
|
|
|
Q_ASSERT( d->dbp != 0 );
|
|
|
|
|
|
|
|
TQMap<TQCString, DB*>::Iterator it = d->indexList.find( name );
|
|
|
|
if( it == d->indexList.end() ){
|
|
|
|
DB* dbp = 0;
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = db_create(&dbp, 0, 0)) != 0) {
|
|
|
|
kdDebug() << "db_create: " << db_strerror(ret) << endl;
|
|
|
|
return /*false*/;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = dbp->set_flags(dbp, DB_DUP | DB_DUPSORT)) != 0) {
|
|
|
|
dbp->err(dbp, ret, "set_flags: DB_DUP | DB_DUPSORT");
|
|
|
|
dbp->close( dbp, 0 );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQFileInfo fileInfo( d->dbName );
|
|
|
|
TQString indexName = fileInfo.dirPath(true) + "/" + fileInfo.baseName(true) + "." + TQString(name) + ".idx";
|
|
|
|
|
|
|
|
if( (ret = dbp->set_cachesize( dbp, 0, 2 * 1024 * 1024, 0 )) != 0 ){
|
|
|
|
kdDebug() << "set_cachesize: " << db_strerror(ret) << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = dbp->open(
|
|
|
|
dbp, NULL, TQFile::encodeName( indexName ).data(), 0, DB_BTREE, DB_CREATE, 0664)) != 0) {
|
|
|
|
kdDebug() << "db_open: " << db_strerror(ret) << endl;
|
|
|
|
dbp->close( dbp, 0 );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->indexList[ name ] = dbp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::close()
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Catalog::close()
|
|
|
|
{
|
|
|
|
d->dbName = TQString();
|
|
|
|
|
|
|
|
TQMap<TQCString, DB*>::Iterator it = d->indexList.begin();
|
|
|
|
while( it != d->indexList.end() ){
|
|
|
|
if( it.data() ){
|
|
|
|
it.data()->close( it.data(), 0 );
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
d->indexList.clear();
|
|
|
|
|
|
|
|
if( d->dbp != 0 ){
|
|
|
|
d->dbp->close( d->dbp, 0 );
|
|
|
|
d->dbp = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::open( const TQString& dbName )
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Catalog::open( const TQString& dbName )
|
|
|
|
{
|
|
|
|
Q_ASSERT( d->dbp == 0 );
|
|
|
|
|
|
|
|
d->dbName = dbName;
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = db_create(&d->dbp, 0, 0)) != 0) {
|
|
|
|
kdDebug() << "db_create: " << db_strerror(ret) << endl;
|
|
|
|
return /*false*/;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = d->dbp->set_flags(d->dbp, DB_RECNUM)) != 0) {
|
|
|
|
d->dbp->err(d->dbp, ret, "set_flags: DB_RECNUM");
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (ret = d->dbp->set_cachesize( d->dbp, 0, 2 * 1024 * 1024, 0 )) != 0 ){
|
|
|
|
kdDebug() << "set_cachesize: " << db_strerror(ret) << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = d->dbp->open(
|
|
|
|
d->dbp, NULL, d->dbName.local8Bit(), 0, DB_BTREE, DB_CREATE, 0664)) != 0) {
|
|
|
|
kdDebug() << "db_open: " << db_strerror(ret) << endl;
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::dbName() const
|
|
|
|
*/
|
|
|
|
|
|
|
|
TQString Catalog::dbName() const
|
|
|
|
{
|
|
|
|
return d->dbName;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::isValid() const
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool Catalog::isValid() const
|
|
|
|
{
|
|
|
|
return d->dbp != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::addItem( Tag& tag )
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Catalog::addItem( Tag& tag )
|
|
|
|
{
|
|
|
|
if( tag.name().isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQCString id = generateId();
|
|
|
|
|
|
|
|
tag.setId( id );
|
|
|
|
if( d->addItem(d->dbp, id, tag) ){
|
|
|
|
TQMap<TQCString, DB*>::Iterator it = d->indexList.begin();
|
|
|
|
while( it != d->indexList.end() ){
|
|
|
|
if( tag.hasAttribute(it.key()) )
|
|
|
|
d->addItem( it.data(), tag.attribute(it.key()), id );
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::getItemById( const TQString& id )
|
|
|
|
*/
|
|
|
|
|
|
|
|
Tag Catalog::getItemById( const TQCString& id )
|
|
|
|
{
|
|
|
|
Q_ASSERT( d->dbp != 0 );
|
|
|
|
|
|
|
|
DBT key, data;
|
|
|
|
std::memset( &key, 0, sizeof(key) );
|
|
|
|
std::memset( &data, 0, sizeof(data) );
|
|
|
|
|
|
|
|
TQByteArray a1;
|
|
|
|
{
|
|
|
|
TQDataStream stream( a1, IO_WriteOnly );
|
|
|
|
stream << id;
|
|
|
|
key.data = a1.data();
|
|
|
|
key.size = a1.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = d->dbp->get( d->dbp, 0, &key, &data, 0 );
|
|
|
|
Q_ASSERT( ret == 0 );
|
|
|
|
|
|
|
|
Tag tag;
|
|
|
|
|
|
|
|
if( ret == 0 ){
|
|
|
|
TQByteArray a;
|
|
|
|
a.setRawData( (const char*) data.data, data.size );
|
|
|
|
TQDataStream stream( a, IO_ReadOnly );
|
|
|
|
tag.load( stream );
|
|
|
|
a.resetRawData( (const char*) data.data, data.size );
|
|
|
|
}
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::sync()
|
|
|
|
*/
|
|
|
|
|
|
|
|
void Catalog::sync()
|
|
|
|
{
|
|
|
|
Q_ASSERT( d->dbp != 0 );
|
|
|
|
d->dbp->sync( d->dbp, 0 );
|
|
|
|
|
|
|
|
TQMap<TQCString, DB*>::Iterator it = d->indexList.begin();
|
|
|
|
while( it != d->indexList.end() ){
|
|
|
|
it.data()->sync( it.data(), 0 );
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn Catalog::query( const TQValueList<QueryArgument>& args )
|
|
|
|
*/
|
|
|
|
|
|
|
|
TQValueList<Tag> Catalog::query( const TQValueList<QueryArgument>& args )
|
|
|
|
{
|
|
|
|
TQValueList<Tag> tags;
|
|
|
|
|
|
|
|
DBT key, data;
|
|
|
|
|
|
|
|
DBC** cursors = new DBC* [ args.size() + 1 ];
|
|
|
|
|
|
|
|
TQValueList< TQPair<TQCString,TQVariant> >::ConstIterator it = args.begin();
|
|
|
|
int current = 0;
|
|
|
|
while( it != args.end() ){
|
|
|
|
TQCString indexName = (*it).first;
|
|
|
|
TQVariant value = (*it).second;
|
|
|
|
|
|
|
|
if( d->hasIndex(indexName) ) {
|
|
|
|
DB* dbp = d->index( indexName );
|
|
|
|
Q_ASSERT( dbp != 0 );
|
|
|
|
|
|
|
|
std::memset( &key, 0, sizeof(key) );
|
|
|
|
std::memset( &data, 0, sizeof(data) );
|
|
|
|
|
|
|
|
TQByteArray a1;
|
|
|
|
{
|
|
|
|
TQDataStream stream( a1, IO_WriteOnly );
|
|
|
|
stream << value;
|
|
|
|
key.data = a1.data();
|
|
|
|
key.size = a1.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
DBC* cursor = 0;
|
|
|
|
int rtn = dbp->cursor( dbp, 0, &cursor, 0 );
|
|
|
|
|
|
|
|
if ( rtn == 0 ) {
|
|
|
|
|
|
|
|
rtn = cursor->c_get( cursor, &key, &data, DB_SET );
|
|
|
|
|
|
|
|
if ( rtn == 0 ) {
|
|
|
|
cursors[ current++ ] = cursor;
|
|
|
|
}
|
|
|
|
else if ( rtn != DB_NOTFOUND) {
|
|
|
|
kdDebug() << "fetching cursor failed: " << db_strerror(rtn) << endl;
|
|
|
|
cursor->c_close(cursor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kdDebug() << "creating cursor failed: " << db_strerror(rtn) << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
cursors[ current ] = 0;
|
|
|
|
|
|
|
|
if( current == args.size() ) {
|
|
|
|
|
|
|
|
DBC* join_curs = 0;
|
|
|
|
int rtn = d->dbp->join( d->dbp, cursors, &join_curs, 0 );
|
|
|
|
|
|
|
|
if ( rtn == 0 ) {
|
|
|
|
|
|
|
|
std::memset( &key, 0, sizeof(key) );
|
|
|
|
std::memset( &data, 0, sizeof(data) );
|
|
|
|
|
|
|
|
while( join_curs->c_get(join_curs, &key, &data, 0) == 0 ) {
|
|
|
|
|
|
|
|
TQByteArray a2;
|
|
|
|
a2.setRawData( (const char*) data.data, data.size );
|
|
|
|
TQDataStream s( a2, IO_ReadOnly );
|
|
|
|
Tag tag;
|
|
|
|
tag.load( s );
|
|
|
|
a2.resetRawData( (const char*) data.data, data.size );
|
|
|
|
tags << tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
join_curs->c_close( join_curs );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kdDebug() << "joining results failed: " << db_strerror(rtn) << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBC** c = cursors;
|
|
|
|
while( *c != 0 ){
|
|
|
|
(*c)->c_close( *c );
|
|
|
|
++c;
|
|
|
|
}
|
|
|
|
delete[] cursors;
|
|
|
|
|
|
|
|
return tags;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQCString Catalog::generateId()
|
|
|
|
{
|
|
|
|
static int n = 1;
|
|
|
|
TQCString asStr;
|
|
|
|
asStr.sprintf( "%05d", n++ );
|
|
|
|
return asStr;
|
|
|
|
}
|
|
|
|
|