/* * SQL Backend for the OPIE-Contact Database. * * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de) * * ===================================================================== * This program 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. * ===================================================================== * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ * Revision 1.1 2003/09/22 14:31:16 eilers * Added first experimental incarnation of sql-backend for addressbook. * Some modifications to be able to compile the todo sql-backend. * A lot of changes fill follow... * */ #include "ocontactaccessbackend_sql.h" #include #include #include #include #include #include #include #include #include #include #include /* * Implementation of used query types * CREATE query * LOAD query * INSERT * REMOVE * CLEAR */ namespace { /** * CreateQuery for the Todolist Table */ class CreateQuery : public OSQLQuery { public: CreateQuery(); ~CreateQuery(); QString query()const; }; /** * Clears (delete) a Table */ class ClearQuery : public OSQLQuery { public: ClearQuery(); ~ClearQuery(); QString query()const; }; /** * LoadQuery * this one queries for all uids */ class LoadQuery : public OSQLQuery { public: LoadQuery(); ~LoadQuery(); QString query()const; }; /** * inserts/adds a OContact to the table */ class InsertQuery : public OSQLQuery { public: InsertQuery(const OContact& ); ~InsertQuery(); QString query()const; private: OContact m_contact; }; /** * removes one from the table */ class RemoveQuery : public OSQLQuery { public: RemoveQuery(int uid ); ~RemoveQuery(); QString query()const; private: int m_uid; }; /** * a find query for noncustom elements */ class FindQuery : public OSQLQuery { public: FindQuery(int uid); FindQuery(const QArray& ); ~FindQuery(); QString query()const; private: QString single()const; QString multi()const; QArray m_uids; int m_uid; }; /** * a find query for custom elements */ class FindCustomQuery : public OSQLQuery { public: FindCustomQuery(int uid); FindCustomQuery(const QArray& ); ~FindCustomQuery(); QString query()const; private: QString single()const; QString multi()const; QArray m_uids; int m_uid; }; // We using three tables to store the information: // 1. addressbook : It contains General information about the contact (non custom) // 2. dates : Stuff like birthdays, anniversaries, etc. // 3. custom_data : Not official supported entries // All tables are connected by the uid of the contact. // Maybe I should add a table for meta-information ? CreateQuery::CreateQuery() : OSQLQuery() {} CreateQuery::~CreateQuery() {} QString CreateQuery::query()const { QString qu; qu += "create table addressbook( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id));"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; // qu += "create table dates( uid PRIMARY KEY, type, day, month, year, hour, minute, second );"; return qu; } ClearQuery::ClearQuery() : OSQLQuery() {} ClearQuery::~ClearQuery() {} QString ClearQuery::query()const { QString qu = "drop table addressbook;"; qu += "drop table custom_data;"; qu += "drop table dates;"; return qu; } // Distinct loading is not very fast. If I expect that every person has just // one (and always one) 'Last Name', I can request all uid's for existing lastnames, // which is faster.. // But this may not be true for all entries, like company contacts.. // The current AddressBook application handles this problem, but other may not.. (eilers) #define __USE_SUPERFAST_LOADQUERY LoadQuery::LoadQuery() : OSQLQuery() {} LoadQuery::~LoadQuery() {} QString LoadQuery::query()const { QString qu; #ifndef __USE_SUPERFAST_LOADQUERY qu += "select distinct uid from addressbook"; #else qu += "select uid from addressbook where type = 'Last Name'"; #endif return qu; } InsertQuery::InsertQuery( const OContact& contact ) : OSQLQuery(), m_contact( contact ) { } InsertQuery::~InsertQuery() { } /* * converts from a OContact to a query */ QString InsertQuery::query()const{ // Get all information out of the contact-class // Remember: The category is stored in contactMap, too ! QMap contactMap = m_contact.toMap(); QMap customMap = m_contact.toExtraMap(); QMap addressbook_db; // Get the translation from the ID to the String QMap transMap = OContactFields::idToUntrFields(); for( QMap::Iterator it = contactMap.begin(); it != contactMap.end(); ++it ){ switch ( it.key() ){ case Qtopia::Birthday:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.birthday(); addressbook_db.insert( transMap[it.key()], QString("%1-%2-%3") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ) ); } break; case Qtopia::Anniversary:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.anniversary(); addressbook_db.insert( transMap[it.key()], QString("%1-%2-%3") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ) ); } break; case Qtopia::AddressUid: // Ignore UID break; default: // Translate id to String addressbook_db.insert( transMap[it.key()], it.data() ); break; } } // Now convert this whole stuff into a SQL String, beginning with // the addressbook table.. QString qu; // qu += "begin transaction;"; int id = 0; for( QMap::Iterator it = addressbook_db.begin(); it != addressbook_db.end(); ++it ){ qu += "insert into addressbook VALUES(" + QString::number( m_contact.uid() ) + "," + QString::number( id++ ) + ",'" + it.key() //.latin1() + "'," + "0" // Priority for future enhancements + ",'" + it.data() //.latin1() + "');"; } // Now add custom data.. id = 0; for( QMap::Iterator it = customMap.begin(); it != customMap.end(); ++it ){ qu += "insert into custom_data VALUES(" + QString::number( m_contact.uid() ) + "," + QString::number( id++ ) + ",'" + it.key() //.latin1() + "'," + "0" // Priority for future enhancements + ",'" + it.data() //.latin1() + "');"; } // qu += "commit;"; qWarning("add %s", qu.latin1() ); return qu; } RemoveQuery::RemoveQuery(int uid ) : OSQLQuery(), m_uid( uid ) {} RemoveQuery::~RemoveQuery() {} QString RemoveQuery::query()const { QString qu = "DELETE from addressbook where uid = " + QString::number(m_uid) + ";"; qu += "DELETE from dates where uid = " + QString::number(m_uid) + ";"; qu += "DELETE from custom_data where uid = " + QString::number(m_uid) + ";"; return qu; } FindQuery::FindQuery(int uid) : OSQLQuery(), m_uid( uid ) { } FindQuery::FindQuery(const QArray& ints) : OSQLQuery(), m_uids( ints ){ } FindQuery::~FindQuery() { } QString FindQuery::query()const{ // if ( m_uids.count() == 0 ) return single(); } /* else return multi(); } QString FindQuery::multi()const { QString qu = "select uid, type, value from addressbook where"; for (uint i = 0; i < m_uids.count(); i++ ) { qu += " UID = " + QString::number( m_uids[i] ) + " OR"; } qu.remove( qu.length()-2, 2 ); // Hmmmm.. return qu; } */ QString FindQuery::single()const{ QString qu = "select uid, type, value from addressbook where uid = "; qu += QString::number(m_uid); return qu; } FindCustomQuery::FindCustomQuery(int uid) : OSQLQuery(), m_uid( uid ) { } FindCustomQuery::FindCustomQuery(const QArray& ints) : OSQLQuery(), m_uids( ints ){ } FindCustomQuery::~FindCustomQuery() { } QString FindCustomQuery::query()const{ // if ( m_uids.count() == 0 ) return single(); } QString FindCustomQuery::single()const{ QString qu = "select uid, type, value from custom_data where uid = "; qu += QString::number(m_uid); return qu; } }; /* --------------------------------------------------------------------------- */ OContactAccessBackend_SQL::OContactAccessBackend_SQL ( const QString& /* appname */, const QString& filename ): m_changed(false) { qWarning("C'tor OContactAccessBackend_SQL starts"); QTime t; t.start(); /* Expecting to access the default filename if nothing else is set */ if ( filename.isEmpty() ){ m_fileName = Global::applicationFileName( "addressbook","addressbook.db" ); } else m_fileName = filename; // Get the standart sql-driver from the OSQLManager.. OSQLManager man; m_driver = man.standard(); m_driver->setUrl( m_fileName ); load(); qWarning("C'tor OContactAccessBackend_SQL ends: %d", t.elapsed() ); } bool OContactAccessBackend_SQL::load () { if (!m_driver->open() ) return false; // Don't expect that the database exists. // It is save here to create the table, even if it // do exist. ( Is that correct for all databases ?? ) CreateQuery creat; OSQLResult res = m_driver->query( &creat ); update(); return true; } bool OContactAccessBackend_SQL::reload() { return load(); } bool OContactAccessBackend_SQL::save() { return m_driver->close(); } void OContactAccessBackend_SQL::clear () { ClearQuery cle; OSQLResult res = m_driver->query( &cle ); CreateQuery qu; res = m_driver->query(&qu); } bool OContactAccessBackend_SQL::wasChangedExternally() { return false; } QArray OContactAccessBackend_SQL::allRecords() const { // FIXME: Think about cute handling of changed tables.. // Thus, we don't have to call update here... if ( m_changed ) ((OContactAccessBackend_SQL*)this)->update(); return m_uids; } bool OContactAccessBackend_SQL::add ( const OContact &newcontact ) { InsertQuery ins( newcontact ); OSQLResult res = m_driver->query( &ins ); if ( res.state() == OSQLResult::Failure ) return false; int c = m_uids.count(); m_uids.resize( c+1 ); m_uids[c] = newcontact.uid(); return true; } bool OContactAccessBackend_SQL::remove ( int uid ) { RemoveQuery rem( uid ); OSQLResult res = m_driver->query(&rem ); if ( res.state() == OSQLResult::Failure ) return false; m_changed = true; return true; } bool OContactAccessBackend_SQL::replace ( const OContact &contact ) { if ( !remove( contact.uid() ) ) return false; return add( contact ); } OContact OContactAccessBackend_SQL::find ( int uid ) const { qWarning("OContactAccessBackend_SQL::find()"); QTime t; t.start(); OContact retContact( requestNonCustom( uid ) ); retContact.setExtraMap( requestCustom( uid ) ); qWarning("OContactAccessBackend_SQL::find() needed: %d", t.elapsed() ); return retContact; } QArray OContactAccessBackend_SQL::queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() ) { QArray nix(0); return nix; } QArray OContactAccessBackend_SQL::matchRegexp( const QRegExp &r ) const { QArray nix(0); return nix; } const uint OContactAccessBackend_SQL::querySettings() { return 0; } bool OContactAccessBackend_SQL::hasQuerySettings (uint querySettings) const { return false; } QArray OContactAccessBackend_SQL::sorted( bool asc, int , int , int ) { QTime t; t.start(); // Not implemented.. QString query = "SELECT uid FROM addressbook WHERE type = 'Last Name' "; query += "ORDER BY value "; if ( !asc ) query += "DESC"; qWarning("sorted query is: %s", query.latin1() ); OSQLRawQuery raw( query ); OSQLResult res = m_driver->query( &raw ); if ( res.state() != OSQLResult::Success ){ QArray empty; return empty; } QArray list = extractUids( res ); qWarning("sorted needed %d ms!", t.elapsed() ); return list; } void OContactAccessBackend_SQL::update() { qWarning("Update starts"); QTime t; t.start(); // Now load the database set and extract the uid's // which will be held locally LoadQuery lo; OSQLResult res = m_driver->query(&lo); if ( res.state() != OSQLResult::Success ) return; m_uids = extractUids( res ); m_changed = false; qWarning("Update ends %d", t.elapsed() ); } QArray OContactAccessBackend_SQL::extractUids( OSQLResult& res ) const { qWarning("extractUids"); QTime t; t.start(); OSQLResultItem::ValueList list = res.results(); OSQLResultItem::ValueList::Iterator it; QArray ints(list.count() ); qWarning(" count = %d", list.count() ); int i = 0; for (it = list.begin(); it != list.end(); ++it ) { ints[i] = (*it).data("uid").toInt(); i++; } qWarning("extractUids ready: count2 = %d needs %d ms", i, t.elapsed() ); return ints; } QMap OContactAccessBackend_SQL::requestNonCustom( int uid ) const { QTime t; t.start(); QMap nonCustomMap; int t2needed = 0; QTime t2; t2.start(); FindQuery query( uid ); OSQLResult res_noncustom = m_driver->query( &query ); t2needed = t2.elapsed(); if ( res_noncustom.state() == OSQLResult::Failure ) { qWarning("OSQLResult::Failure in find query !!"); QMap empty; return empty; } int t3needed = 0; QTime t3; t3.start(); QMap translateMap = OContactFields::untrFieldsToId(); OSQLResultItem::ValueList list = res_noncustom.results(); OSQLResultItem::ValueList::Iterator it = list.begin(); for ( ; it != list.end(); ++it ) { if ( (*it).data("type") != "" ){ int typeId = translateMap[(*it).data( "type" )]; switch( typeId ){ case Qtopia::Birthday: case Qtopia::Anniversary:{ // Birthday and Anniversary are encoded special ( yyyy-mm-dd ) QStringList list = QStringList::split( '-', (*it).data( "value" ) ); QStringList::Iterator lit = list.begin(); int year = (*lit).toInt(); qWarning("1. %s", (*lit).latin1()); int month = (*(++lit)).toInt(); qWarning("2. %s", (*lit).latin1()); int day = (*(++lit)).toInt(); qWarning("3. %s", (*lit).latin1()); qWarning( "RequestNonCustom->Converting:%s to Year: %d, Month: %d, Day: %d ", (*it).data( "value" ).latin1(), year, month, day ); QDate date( year, month, day ); nonCustomMap.insert( typeId, OConversion::dateToString( date ) ); } break; default: nonCustomMap.insert( typeId, (*it).data( "value" ) ); } } } // Add UID to Map.. nonCustomMap.insert( Qtopia::AddressUid, QString::number( uid ) ); t3needed = t3.elapsed(); qWarning("RequestNonCustom needed: ins:%d, query: %d, mapping: %d", t.elapsed(), t2needed, t3needed ); return nonCustomMap; } QMap OContactAccessBackend_SQL::requestCustom( int uid ) const { QTime t; t.start(); QMap customMap; FindCustomQuery query( uid ); OSQLResult res_custom = m_driver->query( &query ); if ( res_custom.state() == OSQLResult::Failure ) { qWarning("OSQLResult::Failure in find query !!"); QMap empty; return empty; } OSQLResultItem::ValueList list = res_custom.results(); OSQLResultItem::ValueList::Iterator it = list.begin(); for ( ; it != list.end(); ++it ) { customMap.insert( (*it).data( "type" ), (*it).data( "value" ) ); } qWarning("RequestCustom needed: %d", t.elapsed() ); return customMap; }