/* This file is part of KDevelop Copyright (C) 2003 Roberto Raggi 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 #include #include #include #include #include #include #include #include #include struct _Catalog_Private { TQString dbName; DB* dbp; TQMap indexList; KRandomSequence rnd; bool enabled; _Catalog_Private() : dbp( 0 ), enabled( true ) { } bool hasIndex( const TQCString& name ) const { return indexList.tqcontains( 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 Catalog::indexList() const { TQValueList l; TQMap::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::Iterator it = d->indexList.tqfind( 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::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::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::Iterator it = d->indexList.begin(); while( it != d->indexList.end() ){ it.data()->sync( it.data(), 0 ); ++it; } } /*! \fn Catalog::query( const TQValueList& args ) */ TQValueList Catalog::query( const TQValueList& args ) { TQValueList tags; DBT key, data; DBC** cursors = new DBC* [ args.size() + 1 ]; TQValueList< TQPair >::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; }