
/*
 * This is an auto-generated file.
 * Do not edit! All changes made to it will be lost.
 */


#include <entities.h>
#include <storage/datastore.h>
#include <storage/selectquerybuilder.h>
#include <utils.h>
#include <akonadiserver_debug.h>

#include <qsqldatabase.h>
#include <qsqlquery.h>
#include <qsqlerror.h>
#include <qsqldriver.h>
#include <qvariant.h>
#include <QtCore/QHash>
#include <QtCore/QMutex>
#include <QtCore/QThread>

using namespace Akonadi;
using namespace Akonadi::Server;

static QStringList removeEntry(QStringList list, const QString& entry)
{
  list.removeOne(entry);
  return list;
}



// private class
class SchemaVersion::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , version( 0 )
    
      , generation( 0 )
    
      , version_changed( false )
    
      , generation_changed( false )
    
    {}

    
    int version;
    
    int generation;
    
    bool version_changed : 1;
    
    bool generation_changed : 1;
    

    static void addToCache( const SchemaVersion & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
};


// static members
QAtomicInt SchemaVersion::Private::cacheEnabled(0);
QMutex SchemaVersion::Private::cacheMutex;



void SchemaVersion::Private::addToCache( const SchemaVersion & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
}


// constructor
SchemaVersion::SchemaVersion() : Entity(),
  d( new Private )
{
}

SchemaVersion::SchemaVersion(
  int version, int generation
) :
  Entity(),
  d( new Private )
{

  d->version = version;
  d->version_changed = true;

  d->generation = generation;
  d->generation_changed = true;

}

SchemaVersion::SchemaVersion( const SchemaVersion & other )
  : Entity( other ), d( other.d )
{
}

// destructor
SchemaVersion::~SchemaVersion() {}

// assignment operator
SchemaVersion& SchemaVersion::operator=( const SchemaVersion & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool SchemaVersion::operator==( const SchemaVersion & other ) const
{
  return id() == other.id();
}

// accessor methods
int SchemaVersion::version() const
{
  return d->version;
}

void SchemaVersion::
setVersion( int version )

{
  d->version = version;
  d->version_changed = true;
}

int SchemaVersion::generation() const
{
  return d->generation;
}

void SchemaVersion::
setGeneration( int generation )

{
  d->generation = generation;
  d->generation_changed = true;
}



// SQL table information
QString SchemaVersion::tableName()
{
  static const QString tableName = QStringLiteral( "SchemaVersionTable" );
  return tableName;
}

QStringList SchemaVersion::columnNames()
{
  static const QStringList columns = QStringList()
  
    << versionColumn()
  
    << generationColumn()
  
  ;
  return columns;
}

QStringList SchemaVersion::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << versionFullColumnName()
  
    << generationFullColumnName()
  
  ;
  return columns;
}


QString SchemaVersion::versionColumn()
{
  static const QString column = QStringLiteral( "version" );
  return column;
}

QString SchemaVersion::versionFullColumnName()
{
  static const QString column = QStringLiteral( "SchemaVersionTable.version" );
  return column;
}

QString SchemaVersion::generationColumn()
{
  static const QString column = QStringLiteral( "generation" );
  return column;
}

QString SchemaVersion::generationFullColumnName()
{
  static const QString column = QStringLiteral( "SchemaVersionTable.generation" );
  return column;
}



// count records
int SchemaVersion::count( const QString &column, const QVariant &value )
{
  return Entity::count<SchemaVersion>( column, value );
}

// check existence



// result extraction
QVector< SchemaVersion > SchemaVersion::extractResult( QSqlQuery & query )
{
  QVector<SchemaVersion> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( SchemaVersion(
      
        (query.isNull(0)) ?
          int() :
          
          query.value( 0 ).value<int>()
            ,
        (query.isNull(1)) ?
          int() :
          
          query.value( 1 ).value<int>()
            
    ) );
  }
  return rv;
}

// data retrieval


QVector<SchemaVersion> SchemaVersion::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<SchemaVersion>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<SchemaVersion>();
  }
  return extractResult( qb.query() );
}

QVector<SchemaVersion> SchemaVersion::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<SchemaVersion>();

  SelectQueryBuilder<SchemaVersion> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<SchemaVersion>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const SchemaVersion& entity )
{
  d << "[SchemaVersion: "
  
    << "version = " <<
    
        entity.version()
      << ", "
    << "generation = " <<
    
        entity.generation()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool SchemaVersion::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->version_changed )
      
      qb.setColumnValue( versionColumn(), this->version() );
        
    if ( d->generation_changed )
      
      qb.setColumnValue( generationColumn(), this->generation() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool SchemaVersion::hasPendingChanges() const
{
  return false
  
    || d->version_changed
  
    || d->generation_changed
  ;
}

// update existing data
bool SchemaVersion::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->version_changed ) {
      
      qb.setColumnValue( versionColumn(), this->version() );
        
    }
  
    if ( d->generation_changed ) {
      
      qb.setColumnValue( generationColumn(), this->generation() );
        
    }
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool SchemaVersion::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<SchemaVersion>( column, value );
}



// cache stuff
void SchemaVersion::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
  }
}

void SchemaVersion::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
  }
}

void SchemaVersion::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class Resource::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , name()
    
      , isVirtual( false )
    
      , name_changed( false )
    
      , isVirtual_changed( false )
    
    {}

    
    QString name;
    
    bool isVirtual : 1;
    
    bool name_changed : 1;
    
    bool isVirtual_changed : 1;
    

    static void addToCache( const Resource & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, Resource > idCache;
    
    static QHash<QString, Resource > nameCache;
    
};


// static members
QAtomicInt Resource::Private::cacheEnabled(0);
QMutex Resource::Private::cacheMutex;

QHash<qint64, Resource > Resource::Private::idCache;

QHash<QString, Resource > Resource::Private::nameCache;



void Resource::Private::addToCache( const Resource & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.name(), entry );
      
}


// constructor
Resource::Resource() : Entity(),
  d( new Private )
{
}

Resource::Resource(
  const QString &name, bool isVirtual
) :
  Entity(),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

  d->isVirtual = isVirtual;
  d->isVirtual_changed = true;

}

Resource::Resource(
  qint64 id, const QString &name, bool isVirtual
) :
  Entity( id ),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

  d->isVirtual = isVirtual;
  d->isVirtual_changed = true;

}
Resource::Resource( const Resource & other )
  : Entity( other ), d( other.d )
{
}

// destructor
Resource::~Resource() {}

// assignment operator
Resource& Resource::operator=( const Resource & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool Resource::operator==( const Resource & other ) const
{
  return id() == other.id();
}

// accessor methods
QString Resource::name() const
{
  return d->name;
}

void Resource::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}

bool Resource::isVirtual() const
{
  return d->isVirtual;
}

void Resource::
setIsVirtual( bool isVirtual )

{
  d->isVirtual = isVirtual;
  d->isVirtual_changed = true;
}



// SQL table information
QString Resource::tableName()
{
  static const QString tableName = QStringLiteral( "ResourceTable" );
  return tableName;
}

QStringList Resource::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << nameColumn()
  
    << isVirtualColumn()
  
  ;
  return columns;
}

QStringList Resource::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << nameFullColumnName()
  
    << isVirtualFullColumnName()
  
  ;
  return columns;
}


QString Resource::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString Resource::idFullColumnName()
{
  static const QString column = QStringLiteral( "ResourceTable.id" );
  return column;
}

QString Resource::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString Resource::nameFullColumnName()
{
  static const QString column = QStringLiteral( "ResourceTable.name" );
  return column;
}

QString Resource::isVirtualColumn()
{
  static const QString column = QStringLiteral( "isVirtual" );
  return column;
}

QString Resource::isVirtualFullColumnName()
{
  static const QString column = QStringLiteral( "ResourceTable.isVirtual" );
  return column;
}



// count records
int Resource::count( const QString &column, const QVariant &value )
{
  return Entity::count<Resource>( column, value );
}

// check existence

bool Resource::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool Resource::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< Resource > Resource::extractResult( QSqlQuery & query )
{
  QVector<Resource> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( Resource(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            ,
        (query.isNull(2)) ?
          bool() :
          
          query.value( 2 ).value<bool>()
            
    ) );
  }
  return rv;
}

// data retrieval
Resource Resource::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, Resource>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Resource();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return Resource();
  }
  if ( !qb.query().next() ) {
    return Resource();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const bool value3 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      Resource rv(
  
    value1,
    value2,
    value3
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

Resource Resource::retrieveByName( const QString &name )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, Resource>::const_iterator it = Private::nameCache.constFind(name);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Resource();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nameColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with name"
      << name << "from table" << tableName()
      << qb.query().lastError().text();
    return Resource();
  }
  if ( !qb.query().next() ) {
    return Resource();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    name;
      
    const bool value3 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      Resource rv(
  
    value1,
    value2,
    value3
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

Resource Resource::retrieveByNameOrCreate( const QString &name)
{
  static QMutex lock;
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return Resource();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByName(name);
}


QVector<Resource> Resource::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Resource>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<Resource>();
  }
  return extractResult( qb.query() );
}

QVector<Resource> Resource::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Resource>();

  SelectQueryBuilder<Resource> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<Resource>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables

QVector<Collection> Resource::collections() const
{
  return Collection::retrieveFiltered( Collection::resourceIdColumn(), id() );
}


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const Resource& entity )
{
  d << "[Resource: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "name = " <<
    
        entity.name()
      << ", "
    << "isVirtual = " <<
    
        entity.isVirtual()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool Resource::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    if ( d->isVirtual_changed )
      
      qb.setColumnValue( isVirtualColumn(), this->isVirtual() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool Resource::hasPendingChanges() const
{
  return false
  
    || d->name_changed
  
    || d->isVirtual_changed
  ;
}

// update existing data
bool Resource::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
    if ( d->isVirtual_changed ) {
      
      qb.setColumnValue( isVirtualColumn(), this->isVirtual() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool Resource::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<Resource>( column, value );
}


bool Resource::remove()
{
  invalidateCache();
  return Entity::remove<Resource>( idColumn(), id() );
}

bool Resource::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void Resource::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( name() );
        
  }
}

void Resource::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void Resource::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class Collection::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , parentId( 0 )
    
      , resourceId( 0 )
    
      , remoteId()
    
      , remoteRevision()
    
      , name()
    
      , cachePolicyLocalParts()
    
      , queryString()
    
      , queryAttributes()
    
      , queryCollections()
    
      , cachePolicyCheckInterval( -1 )
    
      , cachePolicyCacheTimeout( -1 )
    
      , enabled( true )
    
      , referenced( false )
    
      , cachePolicyInherit( true )
    
      , cachePolicySyncOnDemand( false )
    
      , isVirtual( false )
    
      , syncPref( Tristate::Undefined )
    
      , displayPref( Tristate::Undefined )
    
      , indexPref( Tristate::Undefined )
    
      , remoteId_changed( false )
    
      , remoteRevision_changed( false )
    
      , name_changed( false )
    
      , parentId_changed( false )
    
      , resourceId_changed( false )
    
      , enabled_changed( false )
    
      , syncPref_changed( false )
    
      , displayPref_changed( false )
    
      , indexPref_changed( false )
    
      , referenced_changed( false )
    
      , cachePolicyInherit_changed( false )
    
      , cachePolicyCheckInterval_changed( false )
    
      , cachePolicyCacheTimeout_changed( false )
    
      , cachePolicySyncOnDemand_changed( false )
    
      , cachePolicyLocalParts_changed( false )
    
      , queryString_changed( false )
    
      , queryAttributes_changed( false )
    
      , queryCollections_changed( false )
    
      , isVirtual_changed( false )
    
    {}

    
    qint64 parentId;
    
    qint64 resourceId;
    
    QString remoteId;
    
    QString remoteRevision;
    
    QString name;
    
    QString cachePolicyLocalParts;
    
    QString queryString;
    
    QString queryAttributes;
    
    QString queryCollections;
    
    int cachePolicyCheckInterval;
    
    int cachePolicyCacheTimeout;
    
    bool enabled : 1;
    
    bool referenced : 1;
    
    bool cachePolicyInherit : 1;
    
    bool cachePolicySyncOnDemand : 1;
    
    bool isVirtual : 1;
    Tristate syncPref;
    Tristate displayPref;
    Tristate indexPref;
    
    bool remoteId_changed : 1;
    
    bool remoteRevision_changed : 1;
    
    bool name_changed : 1;
    
    bool parentId_changed : 1;
    
    bool resourceId_changed : 1;
    
    bool enabled_changed : 1;
    
    bool syncPref_changed : 1;
    
    bool displayPref_changed : 1;
    
    bool indexPref_changed : 1;
    
    bool referenced_changed : 1;
    
    bool cachePolicyInherit_changed : 1;
    
    bool cachePolicyCheckInterval_changed : 1;
    
    bool cachePolicyCacheTimeout_changed : 1;
    
    bool cachePolicySyncOnDemand_changed : 1;
    
    bool cachePolicyLocalParts_changed : 1;
    
    bool queryString_changed : 1;
    
    bool queryAttributes_changed : 1;
    
    bool queryCollections_changed : 1;
    
    bool isVirtual_changed : 1;
    

    static void addToCache( const Collection & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, Collection > idCache;
    
    static QHash<QString, Collection > nameCache;
    
};


// static members
QAtomicInt Collection::Private::cacheEnabled(0);
QMutex Collection::Private::cacheMutex;

QHash<qint64, Collection > Collection::Private::idCache;

QHash<QString, Collection > Collection::Private::nameCache;



void Collection::Private::addToCache( const Collection & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.name(), entry );
      
}


// constructor
Collection::Collection() : Entity(),
  d( new Private )
{
}

Collection::Collection(
  const QString &remoteId, const QString &remoteRevision, const QString &name, qint64 parentId, qint64 resourceId, bool enabled, Collection::Tristate syncPref, Collection::Tristate displayPref, Collection::Tristate indexPref, bool referenced, bool cachePolicyInherit, int cachePolicyCheckInterval, int cachePolicyCacheTimeout, bool cachePolicySyncOnDemand, const QString &cachePolicyLocalParts, const QString &queryString, const QString &queryAttributes, const QString &queryCollections, bool isVirtual
) :
  Entity(),
  d( new Private )
{

  d->remoteId = remoteId;
  d->remoteId_changed = true;

  d->remoteRevision = remoteRevision;
  d->remoteRevision_changed = true;

  d->name = name;
  d->name_changed = true;

  d->parentId = parentId;
  d->parentId_changed = true;

  d->resourceId = resourceId;
  d->resourceId_changed = true;

  d->enabled = enabled;
  d->enabled_changed = true;

  d->syncPref = syncPref;
  d->syncPref_changed = true;

  d->displayPref = displayPref;
  d->displayPref_changed = true;

  d->indexPref = indexPref;
  d->indexPref_changed = true;

  d->referenced = referenced;
  d->referenced_changed = true;

  d->cachePolicyInherit = cachePolicyInherit;
  d->cachePolicyInherit_changed = true;

  d->cachePolicyCheckInterval = cachePolicyCheckInterval;
  d->cachePolicyCheckInterval_changed = true;

  d->cachePolicyCacheTimeout = cachePolicyCacheTimeout;
  d->cachePolicyCacheTimeout_changed = true;

  d->cachePolicySyncOnDemand = cachePolicySyncOnDemand;
  d->cachePolicySyncOnDemand_changed = true;

  d->cachePolicyLocalParts = cachePolicyLocalParts;
  d->cachePolicyLocalParts_changed = true;

  d->queryString = queryString;
  d->queryString_changed = true;

  d->queryAttributes = queryAttributes;
  d->queryAttributes_changed = true;

  d->queryCollections = queryCollections;
  d->queryCollections_changed = true;

  d->isVirtual = isVirtual;
  d->isVirtual_changed = true;

}

Collection::Collection(
  qint64 id, const QString &remoteId, const QString &remoteRevision, const QString &name, qint64 parentId, qint64 resourceId, bool enabled, Collection::Tristate syncPref, Collection::Tristate displayPref, Collection::Tristate indexPref, bool referenced, bool cachePolicyInherit, int cachePolicyCheckInterval, int cachePolicyCacheTimeout, bool cachePolicySyncOnDemand, const QString &cachePolicyLocalParts, const QString &queryString, const QString &queryAttributes, const QString &queryCollections, bool isVirtual
) :
  Entity( id ),
  d( new Private )
{

  d->remoteId = remoteId;
  d->remoteId_changed = true;

  d->remoteRevision = remoteRevision;
  d->remoteRevision_changed = true;

  d->name = name;
  d->name_changed = true;

  d->parentId = parentId;
  d->parentId_changed = true;

  d->resourceId = resourceId;
  d->resourceId_changed = true;

  d->enabled = enabled;
  d->enabled_changed = true;

  d->syncPref = syncPref;
  d->syncPref_changed = true;

  d->displayPref = displayPref;
  d->displayPref_changed = true;

  d->indexPref = indexPref;
  d->indexPref_changed = true;

  d->referenced = referenced;
  d->referenced_changed = true;

  d->cachePolicyInherit = cachePolicyInherit;
  d->cachePolicyInherit_changed = true;

  d->cachePolicyCheckInterval = cachePolicyCheckInterval;
  d->cachePolicyCheckInterval_changed = true;

  d->cachePolicyCacheTimeout = cachePolicyCacheTimeout;
  d->cachePolicyCacheTimeout_changed = true;

  d->cachePolicySyncOnDemand = cachePolicySyncOnDemand;
  d->cachePolicySyncOnDemand_changed = true;

  d->cachePolicyLocalParts = cachePolicyLocalParts;
  d->cachePolicyLocalParts_changed = true;

  d->queryString = queryString;
  d->queryString_changed = true;

  d->queryAttributes = queryAttributes;
  d->queryAttributes_changed = true;

  d->queryCollections = queryCollections;
  d->queryCollections_changed = true;

  d->isVirtual = isVirtual;
  d->isVirtual_changed = true;

}
Collection::Collection( const Collection & other )
  : Entity( other ), d( other.d )
{
}

// destructor
Collection::~Collection() {}

// assignment operator
Collection& Collection::operator=( const Collection & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool Collection::operator==( const Collection & other ) const
{
  return id() == other.id();
}

// accessor methods
QString Collection::remoteId() const
{
  return d->remoteId;
}

void Collection::
setRemoteId( const QString &remoteId )

{
  d->remoteId = remoteId;
  d->remoteId_changed = true;
}

QString Collection::remoteRevision() const
{
  return d->remoteRevision;
}

void Collection::
setRemoteRevision( const QString &remoteRevision )

{
  d->remoteRevision = remoteRevision;
  d->remoteRevision_changed = true;
}

QString Collection::name() const
{
  return d->name;
}

void Collection::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}

qint64 Collection::parentId() const
{
  return d->parentId;
}

void Collection::
setParentId( qint64 parentId )

{
  d->parentId = parentId;
  d->parentId_changed = true;
}

qint64 Collection::resourceId() const
{
  return d->resourceId;
}

void Collection::
setResourceId( qint64 resourceId )

{
  d->resourceId = resourceId;
  d->resourceId_changed = true;
}

bool Collection::enabled() const
{
  return d->enabled;
}

void Collection::
setEnabled( bool enabled )

{
  d->enabled = enabled;
  d->enabled_changed = true;
}

Collection::Tristate Collection::syncPref() const
{
  return d->syncPref;
}

void Collection::
setSyncPref( Collection::Tristate syncPref )

{
  d->syncPref = syncPref;
  d->syncPref_changed = true;
}

Collection::Tristate Collection::displayPref() const
{
  return d->displayPref;
}

void Collection::
setDisplayPref( Collection::Tristate displayPref )

{
  d->displayPref = displayPref;
  d->displayPref_changed = true;
}

Collection::Tristate Collection::indexPref() const
{
  return d->indexPref;
}

void Collection::
setIndexPref( Collection::Tristate indexPref )

{
  d->indexPref = indexPref;
  d->indexPref_changed = true;
}

bool Collection::referenced() const
{
  return d->referenced;
}

void Collection::
setReferenced( bool referenced )

{
  d->referenced = referenced;
  d->referenced_changed = true;
}

bool Collection::cachePolicyInherit() const
{
  return d->cachePolicyInherit;
}

void Collection::
setCachePolicyInherit( bool cachePolicyInherit )

{
  d->cachePolicyInherit = cachePolicyInherit;
  d->cachePolicyInherit_changed = true;
}

int Collection::cachePolicyCheckInterval() const
{
  return d->cachePolicyCheckInterval;
}

void Collection::
setCachePolicyCheckInterval( int cachePolicyCheckInterval )

{
  d->cachePolicyCheckInterval = cachePolicyCheckInterval;
  d->cachePolicyCheckInterval_changed = true;
}

int Collection::cachePolicyCacheTimeout() const
{
  return d->cachePolicyCacheTimeout;
}

void Collection::
setCachePolicyCacheTimeout( int cachePolicyCacheTimeout )

{
  d->cachePolicyCacheTimeout = cachePolicyCacheTimeout;
  d->cachePolicyCacheTimeout_changed = true;
}

bool Collection::cachePolicySyncOnDemand() const
{
  return d->cachePolicySyncOnDemand;
}

void Collection::
setCachePolicySyncOnDemand( bool cachePolicySyncOnDemand )

{
  d->cachePolicySyncOnDemand = cachePolicySyncOnDemand;
  d->cachePolicySyncOnDemand_changed = true;
}

QString Collection::cachePolicyLocalParts() const
{
  return d->cachePolicyLocalParts;
}

void Collection::
setCachePolicyLocalParts( const QString &cachePolicyLocalParts )

{
  d->cachePolicyLocalParts = cachePolicyLocalParts;
  d->cachePolicyLocalParts_changed = true;
}

QString Collection::queryString() const
{
  return d->queryString;
}

void Collection::
setQueryString( const QString &queryString )

{
  d->queryString = queryString;
  d->queryString_changed = true;
}

QString Collection::queryAttributes() const
{
  return d->queryAttributes;
}

void Collection::
setQueryAttributes( const QString &queryAttributes )

{
  d->queryAttributes = queryAttributes;
  d->queryAttributes_changed = true;
}

QString Collection::queryCollections() const
{
  return d->queryCollections;
}

void Collection::
setQueryCollections( const QString &queryCollections )

{
  d->queryCollections = queryCollections;
  d->queryCollections_changed = true;
}

bool Collection::isVirtual() const
{
  return d->isVirtual;
}

void Collection::
setIsVirtual( bool isVirtual )

{
  d->isVirtual = isVirtual;
  d->isVirtual_changed = true;
}



// SQL table information
QString Collection::tableName()
{
  static const QString tableName = QStringLiteral( "CollectionTable" );
  return tableName;
}

QStringList Collection::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << remoteIdColumn()
  
    << remoteRevisionColumn()
  
    << nameColumn()
  
    << parentIdColumn()
  
    << resourceIdColumn()
  
    << enabledColumn()
  
    << syncPrefColumn()
  
    << displayPrefColumn()
  
    << indexPrefColumn()
  
    << referencedColumn()
  
    << cachePolicyInheritColumn()
  
    << cachePolicyCheckIntervalColumn()
  
    << cachePolicyCacheTimeoutColumn()
  
    << cachePolicySyncOnDemandColumn()
  
    << cachePolicyLocalPartsColumn()
  
    << queryStringColumn()
  
    << queryAttributesColumn()
  
    << queryCollectionsColumn()
  
    << isVirtualColumn()
  
  ;
  return columns;
}

QStringList Collection::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << remoteIdFullColumnName()
  
    << remoteRevisionFullColumnName()
  
    << nameFullColumnName()
  
    << parentIdFullColumnName()
  
    << resourceIdFullColumnName()
  
    << enabledFullColumnName()
  
    << syncPrefFullColumnName()
  
    << displayPrefFullColumnName()
  
    << indexPrefFullColumnName()
  
    << referencedFullColumnName()
  
    << cachePolicyInheritFullColumnName()
  
    << cachePolicyCheckIntervalFullColumnName()
  
    << cachePolicyCacheTimeoutFullColumnName()
  
    << cachePolicySyncOnDemandFullColumnName()
  
    << cachePolicyLocalPartsFullColumnName()
  
    << queryStringFullColumnName()
  
    << queryAttributesFullColumnName()
  
    << queryCollectionsFullColumnName()
  
    << isVirtualFullColumnName()
  
  ;
  return columns;
}


QString Collection::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString Collection::idFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.id" );
  return column;
}

QString Collection::remoteIdColumn()
{
  static const QString column = QStringLiteral( "remoteId" );
  return column;
}

QString Collection::remoteIdFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.remoteId" );
  return column;
}

QString Collection::remoteRevisionColumn()
{
  static const QString column = QStringLiteral( "remoteRevision" );
  return column;
}

QString Collection::remoteRevisionFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.remoteRevision" );
  return column;
}

QString Collection::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString Collection::nameFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.name" );
  return column;
}

QString Collection::parentIdColumn()
{
  static const QString column = QStringLiteral( "parentId" );
  return column;
}

QString Collection::parentIdFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.parentId" );
  return column;
}

QString Collection::resourceIdColumn()
{
  static const QString column = QStringLiteral( "resourceId" );
  return column;
}

QString Collection::resourceIdFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.resourceId" );
  return column;
}

QString Collection::enabledColumn()
{
  static const QString column = QStringLiteral( "enabled" );
  return column;
}

QString Collection::enabledFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.enabled" );
  return column;
}

QString Collection::syncPrefColumn()
{
  static const QString column = QStringLiteral( "syncPref" );
  return column;
}

QString Collection::syncPrefFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.syncPref" );
  return column;
}

QString Collection::displayPrefColumn()
{
  static const QString column = QStringLiteral( "displayPref" );
  return column;
}

QString Collection::displayPrefFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.displayPref" );
  return column;
}

QString Collection::indexPrefColumn()
{
  static const QString column = QStringLiteral( "indexPref" );
  return column;
}

QString Collection::indexPrefFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.indexPref" );
  return column;
}

QString Collection::referencedColumn()
{
  static const QString column = QStringLiteral( "referenced" );
  return column;
}

QString Collection::referencedFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.referenced" );
  return column;
}

QString Collection::cachePolicyInheritColumn()
{
  static const QString column = QStringLiteral( "cachePolicyInherit" );
  return column;
}

QString Collection::cachePolicyInheritFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.cachePolicyInherit" );
  return column;
}

QString Collection::cachePolicyCheckIntervalColumn()
{
  static const QString column = QStringLiteral( "cachePolicyCheckInterval" );
  return column;
}

QString Collection::cachePolicyCheckIntervalFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.cachePolicyCheckInterval" );
  return column;
}

QString Collection::cachePolicyCacheTimeoutColumn()
{
  static const QString column = QStringLiteral( "cachePolicyCacheTimeout" );
  return column;
}

QString Collection::cachePolicyCacheTimeoutFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.cachePolicyCacheTimeout" );
  return column;
}

QString Collection::cachePolicySyncOnDemandColumn()
{
  static const QString column = QStringLiteral( "cachePolicySyncOnDemand" );
  return column;
}

QString Collection::cachePolicySyncOnDemandFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.cachePolicySyncOnDemand" );
  return column;
}

QString Collection::cachePolicyLocalPartsColumn()
{
  static const QString column = QStringLiteral( "cachePolicyLocalParts" );
  return column;
}

QString Collection::cachePolicyLocalPartsFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.cachePolicyLocalParts" );
  return column;
}

QString Collection::queryStringColumn()
{
  static const QString column = QStringLiteral( "queryString" );
  return column;
}

QString Collection::queryStringFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.queryString" );
  return column;
}

QString Collection::queryAttributesColumn()
{
  static const QString column = QStringLiteral( "queryAttributes" );
  return column;
}

QString Collection::queryAttributesFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.queryAttributes" );
  return column;
}

QString Collection::queryCollectionsColumn()
{
  static const QString column = QStringLiteral( "queryCollections" );
  return column;
}

QString Collection::queryCollectionsFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.queryCollections" );
  return column;
}

QString Collection::isVirtualColumn()
{
  static const QString column = QStringLiteral( "isVirtual" );
  return column;
}

QString Collection::isVirtualFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionTable.isVirtual" );
  return column;
}



// count records
int Collection::count( const QString &column, const QVariant &value )
{
  return Entity::count<Collection>( column, value );
}

// check existence

bool Collection::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool Collection::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< Collection > Collection::extractResult( QSqlQuery & query )
{
  QVector<Collection> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( Collection(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            ,
        (query.isNull(2)) ?
          QString() :
          
          Utils::variantToString( query.value( 2 ) )
            ,
        (query.isNull(3)) ?
          QString() :
          
          Utils::variantToString( query.value( 3 ) )
            ,
        (query.isNull(4)) ?
          qint64() :
          
          query.value( 4 ).value<qint64>()
            ,
        (query.isNull(5)) ?
          qint64() :
          
          query.value( 5 ).value<qint64>()
            ,
        (query.isNull(6)) ?
          bool() :
          
          query.value( 6 ).value<bool>()
            ,
        (query.isNull(7)) ?
          Collection::Tristate() :
          
            static_cast<Tristate>(query.value( 7 ).value<int>())
            ,
        (query.isNull(8)) ?
          Collection::Tristate() :
          
            static_cast<Tristate>(query.value( 8 ).value<int>())
            ,
        (query.isNull(9)) ?
          Collection::Tristate() :
          
            static_cast<Tristate>(query.value( 9 ).value<int>())
            ,
        (query.isNull(10)) ?
          bool() :
          
          query.value( 10 ).value<bool>()
            ,
        (query.isNull(11)) ?
          bool() :
          
          query.value( 11 ).value<bool>()
            ,
        (query.isNull(12)) ?
          int() :
          
          query.value( 12 ).value<int>()
            ,
        (query.isNull(13)) ?
          int() :
          
          query.value( 13 ).value<int>()
            ,
        (query.isNull(14)) ?
          bool() :
          
          query.value( 14 ).value<bool>()
            ,
        (query.isNull(15)) ?
          QString() :
          
          Utils::variantToString( query.value( 15 ) )
            ,
        (query.isNull(16)) ?
          QString() :
          
          Utils::variantToString( query.value( 16 ) )
            ,
        (query.isNull(17)) ?
          QString() :
          
          Utils::variantToString( query.value( 17 ) )
            ,
        (query.isNull(18)) ?
          QString() :
          
          Utils::variantToString( query.value( 18 ) )
            ,
        (query.isNull(19)) ?
          bool() :
          
          query.value( 19 ).value<bool>()
            
    ) );
  }
  return rv;
}

// data retrieval
Collection Collection::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, Collection>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Collection();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return Collection();
  }
  if ( !qb.query().next() ) {
    return Collection();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value3 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value4 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const qint64 value5 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const qint64 value6 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const bool value7 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const Collection::Tristate value8 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate() :
        
          static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      
    const Collection::Tristate value9 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate() :
        
          static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      
    const Collection::Tristate value10 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate() :
        
          static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      
    const bool value11 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const bool value12 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const int value13 =
    
        (qb.query().isNull(valueIndex)) ?
        int() :
        
          qb.query().value( valueIndex ).value<int>()
          
        ; ++valueIndex;
      
    const int value14 =
    
        (qb.query().isNull(valueIndex)) ?
        int() :
        
          qb.query().value( valueIndex ).value<int>()
          
        ; ++valueIndex;
      
    const bool value15 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const QString value16 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value17 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value18 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value19 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const bool value20 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      Collection rv(
  
    value1,
    value2,
    value3,
    value4,
    value5,
    value6,
    value7,
    value8,
    value9,
    value10,
    value11,
    value12,
    value13,
    value14,
    value15,
    value16,
    value17,
    value18,
    value19,
    value20
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

Collection Collection::retrieveByName( const QString &name )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, Collection>::const_iterator it = Private::nameCache.constFind(name);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Collection();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nameColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with name"
      << name << "from table" << tableName()
      << qb.query().lastError().text();
    return Collection();
  }
  if ( !qb.query().next() ) {
    return Collection();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value3 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value4 =
    name;
      
    const qint64 value5 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const qint64 value6 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const bool value7 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const Collection::Tristate value8 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate() :
        
          static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      
    const Collection::Tristate value9 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate() :
        
          static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      
    const Collection::Tristate value10 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate() :
        
          static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      
    const bool value11 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const bool value12 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const int value13 =
    
        (qb.query().isNull(valueIndex)) ?
        int() :
        
          qb.query().value( valueIndex ).value<int>()
          
        ; ++valueIndex;
      
    const int value14 =
    
        (qb.query().isNull(valueIndex)) ?
        int() :
        
          qb.query().value( valueIndex ).value<int>()
          
        ; ++valueIndex;
      
    const bool value15 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const QString value16 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value17 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value18 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value19 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const bool value20 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      Collection rv(
  
    value1,
    value2,
    value3,
    value4,
    value5,
    value6,
    value7,
    value8,
    value9,
    value10,
    value11,
    value12,
    value13,
    value14,
    value15,
    value16,
    value17,
    value18,
    value19,
    value20
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

Collection Collection::retrieveByNameOrCreate( const QString &name)
{
  static QMutex lock;
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return Collection();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByName(name);
}


QVector<Collection> Collection::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Collection>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<Collection>();
  }
  return extractResult( qb.query() );
}

QVector<Collection> Collection::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Collection>();

  SelectQueryBuilder<Collection> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<Collection>();
  }
  return qb.result();
}

// data retrieval for referenced tables
Collection Collection::parent() const
{
  return Collection::retrieveById( parentId() );
}

void Collection::
    setParent
    ( const Collection &value )
{
  d->parentId = value.id();
  d->parentId_changed = true;
}
Resource Collection::resource() const
{
  return Resource::retrieveById( resourceId() );
}

void Collection::
    setResource
    ( const Resource &value )
{
  d->resourceId = value.id();
  d->resourceId_changed = true;
}


// data retrieval for inverse referenced tables

QVector<Collection> Collection::children() const
{
  return Collection::retrieveFiltered( Collection::parentIdColumn(), id() );
}

QVector<PimItem> Collection::items() const
{
  return PimItem::retrieveFiltered( PimItem::collectionIdColumn(), id() );
}

QVector<CollectionAttribute> Collection::attributes() const
{
  return CollectionAttribute::retrieveFiltered( CollectionAttribute::collectionIdColumn(), id() );
}


// data retrieval for n:m relations
QVector<MimeType> Collection::mimeTypes() const
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<MimeType>();

  QueryBuilder qb( MimeType::tableName(), QueryBuilder::Select );
  static const QStringList columns = QStringList()
  
    << MimeType::idFullColumnName()
  
    << MimeType::nameFullColumnName()
  
  ;
  qb.addColumns(columns);
  qb.addJoin( QueryBuilder::InnerJoin, CollectionMimeTypeRelation::tableName(),
              CollectionMimeTypeRelation::rightFullColumnName(),
              MimeType::idFullColumnName() );
  qb.addValueCondition( CollectionMimeTypeRelation::leftFullColumnName(), Query::Equals, id() );

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table CollectionMimeTypeRelation"
      << qb.query().lastError().text();
    return QVector<MimeType>();
  }

  return MimeType::extractResult( qb.query() );
}

// manipulate n:m relations
bool Collection::relatesToMimeType( const MimeType & value ) const
{
  return Entity::relatesTo<CollectionMimeTypeRelation>( id(), value.id() );
}

bool Collection::relatesToMimeType( qint64 leftId, qint64 rightId )
{
  return Entity::relatesTo<CollectionMimeTypeRelation>( leftId, rightId );
}

bool Collection::addMimeType( const MimeType & value ) const
{
  return Entity::addToRelation<CollectionMimeTypeRelation>( id(), value.id() );
}

bool Collection::addMimeType( qint64 leftId, qint64 rightId )
{
  return Entity::addToRelation<CollectionMimeTypeRelation>( leftId, rightId );
}

bool Collection::removeMimeType( const MimeType & value ) const
{
  return Entity::removeFromRelation<CollectionMimeTypeRelation>( id(), value.id() );
}

bool Collection::removeMimeType( qint64 leftId, qint64 rightId )
{
  return Entity::removeFromRelation<CollectionMimeTypeRelation>( leftId, rightId );
}

bool Collection::clearMimeTypes() const
{
  return Entity::clearRelation<CollectionMimeTypeRelation>( id() );
}

bool Collection::clearMimeTypes( qint64 id )
{
  return Entity::clearRelation<CollectionMimeTypeRelation>( id );
}



// data retrieval for n:m relations
QVector<PimItem> Collection::pimItems() const
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<PimItem>();

  QueryBuilder qb( PimItem::tableName(), QueryBuilder::Select );
  static const QStringList columns = QStringList()
  
    << PimItem::idFullColumnName()
  
    << PimItem::revFullColumnName()
  
    << PimItem::remoteIdFullColumnName()
  
    << PimItem::remoteRevisionFullColumnName()
  
    << PimItem::gidFullColumnName()
  
    << PimItem::collectionIdFullColumnName()
  
    << PimItem::mimeTypeIdFullColumnName()
  
    << PimItem::datetimeFullColumnName()
  
    << PimItem::atimeFullColumnName()
  
    << PimItem::dirtyFullColumnName()
  
    << PimItem::sizeFullColumnName()
  
  ;
  qb.addColumns(columns);
  qb.addJoin( QueryBuilder::InnerJoin, CollectionPimItemRelation::tableName(),
              CollectionPimItemRelation::rightFullColumnName(),
              PimItem::idFullColumnName() );
  qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, id() );

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table CollectionPimItemRelation"
      << qb.query().lastError().text();
    return QVector<PimItem>();
  }

  return PimItem::extractResult( qb.query() );
}

// manipulate n:m relations
bool Collection::relatesToPimItem( const PimItem & value ) const
{
  return Entity::relatesTo<CollectionPimItemRelation>( id(), value.id() );
}

bool Collection::relatesToPimItem( qint64 leftId, qint64 rightId )
{
  return Entity::relatesTo<CollectionPimItemRelation>( leftId, rightId );
}

bool Collection::addPimItem( const PimItem & value ) const
{
  return Entity::addToRelation<CollectionPimItemRelation>( id(), value.id() );
}

bool Collection::addPimItem( qint64 leftId, qint64 rightId )
{
  return Entity::addToRelation<CollectionPimItemRelation>( leftId, rightId );
}

bool Collection::removePimItem( const PimItem & value ) const
{
  return Entity::removeFromRelation<CollectionPimItemRelation>( id(), value.id() );
}

bool Collection::removePimItem( qint64 leftId, qint64 rightId )
{
  return Entity::removeFromRelation<CollectionPimItemRelation>( leftId, rightId );
}

bool Collection::clearPimItems() const
{
  return Entity::clearRelation<CollectionPimItemRelation>( id() );
}

bool Collection::clearPimItems( qint64 id )
{
  return Entity::clearRelation<CollectionPimItemRelation>( id );
}



#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const Collection& entity )
{
  d << "[Collection: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "remoteId = " <<
    
        entity.remoteId()
      << ", "
    << "remoteRevision = " <<
    
        entity.remoteRevision()
      << ", "
    << "name = " <<
    
        entity.name()
      << ", "
    << "parentId = " <<
    
        entity.parentId()
      << ", "
    << "resourceId = " <<
    
        entity.resourceId()
      << ", "
    << "enabled = " <<
    
        entity.enabled()
      << ", "
    << "syncPref = " <<
    
        static_cast<int>(entity.syncPref())
      << ", "
    << "displayPref = " <<
    
        static_cast<int>(entity.displayPref())
      << ", "
    << "indexPref = " <<
    
        static_cast<int>(entity.indexPref())
      << ", "
    << "referenced = " <<
    
        entity.referenced()
      << ", "
    << "cachePolicyInherit = " <<
    
        entity.cachePolicyInherit()
      << ", "
    << "cachePolicyCheckInterval = " <<
    
        entity.cachePolicyCheckInterval()
      << ", "
    << "cachePolicyCacheTimeout = " <<
    
        entity.cachePolicyCacheTimeout()
      << ", "
    << "cachePolicySyncOnDemand = " <<
    
        entity.cachePolicySyncOnDemand()
      << ", "
    << "cachePolicyLocalParts = " <<
    
        entity.cachePolicyLocalParts()
      << ", "
    << "queryString = " <<
    
        entity.queryString()
      << ", "
    << "queryAttributes = " <<
    
        entity.queryAttributes()
      << ", "
    << "queryCollections = " <<
    
        entity.queryCollections()
      << ", "
    << "isVirtual = " <<
    
        entity.isVirtual()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool Collection::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->remoteId_changed )
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        
    if ( d->remoteRevision_changed )
      
      qb.setColumnValue( remoteRevisionColumn(), this->remoteRevision() );
        
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    if ( d->parentId_changed  && d->parentId > 0 )
      qb.setColumnValue( parentIdColumn(), this->parentId() );
    
    if ( d->resourceId_changed  && d->resourceId > 0 )
      qb.setColumnValue( resourceIdColumn(), this->resourceId() );
    
    if ( d->enabled_changed )
      
      qb.setColumnValue( enabledColumn(), this->enabled() );
        
    if ( d->syncPref_changed )
      
      qb.setColumnValue( syncPrefColumn(), static_cast<int>(this->syncPref()) );
        
    if ( d->displayPref_changed )
      
      qb.setColumnValue( displayPrefColumn(), static_cast<int>(this->displayPref()) );
        
    if ( d->indexPref_changed )
      
      qb.setColumnValue( indexPrefColumn(), static_cast<int>(this->indexPref()) );
        
    if ( d->referenced_changed )
      
      qb.setColumnValue( referencedColumn(), this->referenced() );
        
    if ( d->cachePolicyInherit_changed )
      
      qb.setColumnValue( cachePolicyInheritColumn(), this->cachePolicyInherit() );
        
    if ( d->cachePolicyCheckInterval_changed )
      
      qb.setColumnValue( cachePolicyCheckIntervalColumn(), this->cachePolicyCheckInterval() );
        
    if ( d->cachePolicyCacheTimeout_changed )
      
      qb.setColumnValue( cachePolicyCacheTimeoutColumn(), this->cachePolicyCacheTimeout() );
        
    if ( d->cachePolicySyncOnDemand_changed )
      
      qb.setColumnValue( cachePolicySyncOnDemandColumn(), this->cachePolicySyncOnDemand() );
        
    if ( d->cachePolicyLocalParts_changed )
      
      qb.setColumnValue( cachePolicyLocalPartsColumn(), this->cachePolicyLocalParts() );
        
    if ( d->queryString_changed )
      
      qb.setColumnValue( queryStringColumn(), this->queryString() );
        
    if ( d->queryAttributes_changed )
      
      qb.setColumnValue( queryAttributesColumn(), this->queryAttributes() );
        
    if ( d->queryCollections_changed )
      
      qb.setColumnValue( queryCollectionsColumn(), this->queryCollections() );
        
    if ( d->isVirtual_changed )
      
      qb.setColumnValue( isVirtualColumn(), this->isVirtual() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool Collection::hasPendingChanges() const
{
  return false
  
    || d->remoteId_changed
  
    || d->remoteRevision_changed
  
    || d->name_changed
  
    || d->parentId_changed
  
    || d->resourceId_changed
  
    || d->enabled_changed
  
    || d->syncPref_changed
  
    || d->displayPref_changed
  
    || d->indexPref_changed
  
    || d->referenced_changed
  
    || d->cachePolicyInherit_changed
  
    || d->cachePolicyCheckInterval_changed
  
    || d->cachePolicyCacheTimeout_changed
  
    || d->cachePolicySyncOnDemand_changed
  
    || d->cachePolicyLocalParts_changed
  
    || d->queryString_changed
  
    || d->queryAttributes_changed
  
    || d->queryCollections_changed
  
    || d->isVirtual_changed
  ;
}

// update existing data
bool Collection::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->remoteId_changed ) {
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        
    }
  
    if ( d->remoteRevision_changed ) {
      
      qb.setColumnValue( remoteRevisionColumn(), this->remoteRevision() );
        
    }
  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
    if ( d->parentId_changed ) {
      
      if ( d->parentId <= 0 )
        qb.setColumnValue( parentIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( parentIdColumn(), this->parentId() );
        
    }
  
    if ( d->resourceId_changed ) {
      
      if ( d->resourceId <= 0 )
        qb.setColumnValue( resourceIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( resourceIdColumn(), this->resourceId() );
        
    }
  
    if ( d->enabled_changed ) {
      
      qb.setColumnValue( enabledColumn(), this->enabled() );
        
    }
  
    if ( d->syncPref_changed ) {
      
      qb.setColumnValue( syncPrefColumn(), static_cast<int>(this->syncPref()) );
        
    }
  
    if ( d->displayPref_changed ) {
      
      qb.setColumnValue( displayPrefColumn(), static_cast<int>(this->displayPref()) );
        
    }
  
    if ( d->indexPref_changed ) {
      
      qb.setColumnValue( indexPrefColumn(), static_cast<int>(this->indexPref()) );
        
    }
  
    if ( d->referenced_changed ) {
      
      qb.setColumnValue( referencedColumn(), this->referenced() );
        
    }
  
    if ( d->cachePolicyInherit_changed ) {
      
      qb.setColumnValue( cachePolicyInheritColumn(), this->cachePolicyInherit() );
        
    }
  
    if ( d->cachePolicyCheckInterval_changed ) {
      
      qb.setColumnValue( cachePolicyCheckIntervalColumn(), this->cachePolicyCheckInterval() );
        
    }
  
    if ( d->cachePolicyCacheTimeout_changed ) {
      
      qb.setColumnValue( cachePolicyCacheTimeoutColumn(), this->cachePolicyCacheTimeout() );
        
    }
  
    if ( d->cachePolicySyncOnDemand_changed ) {
      
      qb.setColumnValue( cachePolicySyncOnDemandColumn(), this->cachePolicySyncOnDemand() );
        
    }
  
    if ( d->cachePolicyLocalParts_changed ) {
      
      qb.setColumnValue( cachePolicyLocalPartsColumn(), this->cachePolicyLocalParts() );
        
    }
  
    if ( d->queryString_changed ) {
      
      qb.setColumnValue( queryStringColumn(), this->queryString() );
        
    }
  
    if ( d->queryAttributes_changed ) {
      
      qb.setColumnValue( queryAttributesColumn(), this->queryAttributes() );
        
    }
  
    if ( d->queryCollections_changed ) {
      
      qb.setColumnValue( queryCollectionsColumn(), this->queryCollections() );
        
    }
  
    if ( d->isVirtual_changed ) {
      
      qb.setColumnValue( isVirtualColumn(), this->isVirtual() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool Collection::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<Collection>( column, value );
}


bool Collection::remove()
{
  invalidateCache();
  return Entity::remove<Collection>( idColumn(), id() );
}

bool Collection::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void Collection::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( name() );
        
  }
}

void Collection::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void Collection::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class MimeType::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , name()
    
      , name_changed( false )
    
    {}

    
    QString name;
    
    bool name_changed : 1;
    

    static void addToCache( const MimeType & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, MimeType > idCache;
    
    static QHash<QString, MimeType > nameCache;
    
};


// static members
QAtomicInt MimeType::Private::cacheEnabled(0);
QMutex MimeType::Private::cacheMutex;

QHash<qint64, MimeType > MimeType::Private::idCache;

QHash<QString, MimeType > MimeType::Private::nameCache;



void MimeType::Private::addToCache( const MimeType & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.name(), entry );
      
}


// constructor
MimeType::MimeType() : Entity(),
  d( new Private )
{
}

MimeType::MimeType(
  const QString &name
) :
  Entity(),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}

MimeType::MimeType(
  qint64 id, const QString &name
) :
  Entity( id ),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}
MimeType::MimeType( const MimeType & other )
  : Entity( other ), d( other.d )
{
}

// destructor
MimeType::~MimeType() {}

// assignment operator
MimeType& MimeType::operator=( const MimeType & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool MimeType::operator==( const MimeType & other ) const
{
  return id() == other.id();
}

// accessor methods
QString MimeType::name() const
{
  return d->name;
}

void MimeType::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}



// SQL table information
QString MimeType::tableName()
{
  static const QString tableName = QStringLiteral( "MimeTypeTable" );
  return tableName;
}

QStringList MimeType::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << nameColumn()
  
  ;
  return columns;
}

QStringList MimeType::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << nameFullColumnName()
  
  ;
  return columns;
}


QString MimeType::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString MimeType::idFullColumnName()
{
  static const QString column = QStringLiteral( "MimeTypeTable.id" );
  return column;
}

QString MimeType::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString MimeType::nameFullColumnName()
{
  static const QString column = QStringLiteral( "MimeTypeTable.name" );
  return column;
}



// count records
int MimeType::count( const QString &column, const QVariant &value )
{
  return Entity::count<MimeType>( column, value );
}

// check existence

bool MimeType::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool MimeType::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< MimeType > MimeType::extractResult( QSqlQuery & query )
{
  QVector<MimeType> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( MimeType(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            
    ) );
  }
  return rv;
}

// data retrieval
MimeType MimeType::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, MimeType>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return MimeType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return MimeType();
  }
  if ( !qb.query().next() ) {
    return MimeType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      MimeType rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

MimeType MimeType::retrieveByName( const QString &name )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, MimeType>::const_iterator it = Private::nameCache.constFind(name);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return MimeType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nameColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with name"
      << name << "from table" << tableName()
      << qb.query().lastError().text();
    return MimeType();
  }
  if ( !qb.query().next() ) {
    return MimeType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    name;
      MimeType rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

MimeType MimeType::retrieveByNameOrCreate( const QString &name)
{
  static QMutex lock;
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return MimeType();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByName(name);
}


QVector<MimeType> MimeType::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<MimeType>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<MimeType>();
  }
  return extractResult( qb.query() );
}

QVector<MimeType> MimeType::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<MimeType>();

  SelectQueryBuilder<MimeType> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<MimeType>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const MimeType& entity )
{
  d << "[MimeType: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "name = " <<
    
        entity.name()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool MimeType::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool MimeType::hasPendingChanges() const
{
  return false
  
    || d->name_changed
  ;
}

// update existing data
bool MimeType::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool MimeType::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<MimeType>( column, value );
}


bool MimeType::remove()
{
  invalidateCache();
  return Entity::remove<MimeType>( idColumn(), id() );
}

bool MimeType::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void MimeType::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( name() );
        
  }
}

void MimeType::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void MimeType::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class PimItem::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , collectionId( 0 )
    
      , mimeTypeId( 0 )
    
      , size( 0 )
    
      , remoteId()
    
      , remoteRevision()
    
      , gid()
    
    // on non-wince, QDateTime is one int
    
      , datetime()
    
      , atime()
    
      , rev( 0 )
    
      , dirty( false )
    
      , rev_changed( false )
    
      , remoteId_changed( false )
    
      , remoteRevision_changed( false )
    
      , gid_changed( false )
    
      , collectionId_changed( false )
    
      , mimeTypeId_changed( false )
    
      , datetime_changed( false )
    
      , atime_changed( false )
    
      , dirty_changed( false )
    
      , size_changed( false )
    
    {}

    
    qint64 collectionId;
    
    qint64 mimeTypeId;
    
    qint64 size;
    
    QString remoteId;
    
    QString remoteRevision;
    
    QString gid;
    
    // on non-wince, QDateTime is one int
    
    QDateTime datetime;
    
    QDateTime atime;
    
    int rev;
    
    bool dirty : 1;
    
    bool rev_changed : 1;
    
    bool remoteId_changed : 1;
    
    bool remoteRevision_changed : 1;
    
    bool gid_changed : 1;
    
    bool collectionId_changed : 1;
    
    bool mimeTypeId_changed : 1;
    
    bool datetime_changed : 1;
    
    bool atime_changed : 1;
    
    bool dirty_changed : 1;
    
    bool size_changed : 1;
    

    static void addToCache( const PimItem & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, PimItem > idCache;
    
};


// static members
QAtomicInt PimItem::Private::cacheEnabled(0);
QMutex PimItem::Private::cacheMutex;

QHash<qint64, PimItem > PimItem::Private::idCache;



void PimItem::Private::addToCache( const PimItem & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
}


// constructor
PimItem::PimItem() : Entity(),
  d( new Private )
{
}

PimItem::PimItem(
  int rev, const QString &remoteId, const QString &remoteRevision, const QString &gid, qint64 collectionId, qint64 mimeTypeId, const QDateTime &datetime, const QDateTime &atime, bool dirty, qint64 size
) :
  Entity(),
  d( new Private )
{

  d->rev = rev;
  d->rev_changed = true;

  d->remoteId = remoteId;
  d->remoteId_changed = true;

  d->remoteRevision = remoteRevision;
  d->remoteRevision_changed = true;

  d->gid = gid;
  d->gid_changed = true;

  d->collectionId = collectionId;
  d->collectionId_changed = true;

  d->mimeTypeId = mimeTypeId;
  d->mimeTypeId_changed = true;

  d->datetime = datetime;
  d->datetime_changed = true;

  d->atime = atime;
  d->atime_changed = true;

  d->dirty = dirty;
  d->dirty_changed = true;

  d->size = size;
  d->size_changed = true;

}

PimItem::PimItem(
  qint64 id, int rev, const QString &remoteId, const QString &remoteRevision, const QString &gid, qint64 collectionId, qint64 mimeTypeId, const QDateTime &datetime, const QDateTime &atime, bool dirty, qint64 size
) :
  Entity( id ),
  d( new Private )
{

  d->rev = rev;
  d->rev_changed = true;

  d->remoteId = remoteId;
  d->remoteId_changed = true;

  d->remoteRevision = remoteRevision;
  d->remoteRevision_changed = true;

  d->gid = gid;
  d->gid_changed = true;

  d->collectionId = collectionId;
  d->collectionId_changed = true;

  d->mimeTypeId = mimeTypeId;
  d->mimeTypeId_changed = true;

  d->datetime = datetime;
  d->datetime_changed = true;

  d->atime = atime;
  d->atime_changed = true;

  d->dirty = dirty;
  d->dirty_changed = true;

  d->size = size;
  d->size_changed = true;

}
PimItem::PimItem( const PimItem & other )
  : Entity( other ), d( other.d )
{
}

// destructor
PimItem::~PimItem() {}

// assignment operator
PimItem& PimItem::operator=( const PimItem & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool PimItem::operator==( const PimItem & other ) const
{
  return id() == other.id();
}

// accessor methods
int PimItem::rev() const
{
  return d->rev;
}

void PimItem::
setRev( int rev )

{
  d->rev = rev;
  d->rev_changed = true;
}

QString PimItem::remoteId() const
{
  return d->remoteId;
}

void PimItem::
setRemoteId( const QString &remoteId )

{
  d->remoteId = remoteId;
  d->remoteId_changed = true;
}

QString PimItem::remoteRevision() const
{
  return d->remoteRevision;
}

void PimItem::
setRemoteRevision( const QString &remoteRevision )

{
  d->remoteRevision = remoteRevision;
  d->remoteRevision_changed = true;
}

QString PimItem::gid() const
{
  return d->gid;
}

void PimItem::
setGid( const QString &gid )

{
  d->gid = gid;
  d->gid_changed = true;
}

qint64 PimItem::collectionId() const
{
  return d->collectionId;
}

void PimItem::
setCollectionId( qint64 collectionId )

{
  d->collectionId = collectionId;
  d->collectionId_changed = true;
}

qint64 PimItem::mimeTypeId() const
{
  return d->mimeTypeId;
}

void PimItem::
setMimeTypeId( qint64 mimeTypeId )

{
  d->mimeTypeId = mimeTypeId;
  d->mimeTypeId_changed = true;
}

QDateTime PimItem::datetime() const
{
  return d->datetime;
}

void PimItem::
setDatetime( const QDateTime &datetime )

{
  d->datetime = datetime;
  d->datetime_changed = true;
}

QDateTime PimItem::atime() const
{
  return d->atime;
}

void PimItem::
setAtime( const QDateTime &atime )

{
  d->atime = atime;
  d->atime_changed = true;
}

bool PimItem::dirty() const
{
  return d->dirty;
}

void PimItem::
setDirty( bool dirty )

{
  d->dirty = dirty;
  d->dirty_changed = true;
}

qint64 PimItem::size() const
{
  return d->size;
}

void PimItem::
setSize( qint64 size )

{
  d->size = size;
  d->size_changed = true;
}



// SQL table information
QString PimItem::tableName()
{
  static const QString tableName = QStringLiteral( "PimItemTable" );
  return tableName;
}

QStringList PimItem::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << revColumn()
  
    << remoteIdColumn()
  
    << remoteRevisionColumn()
  
    << gidColumn()
  
    << collectionIdColumn()
  
    << mimeTypeIdColumn()
  
    << datetimeColumn()
  
    << atimeColumn()
  
    << dirtyColumn()
  
    << sizeColumn()
  
  ;
  return columns;
}

QStringList PimItem::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << revFullColumnName()
  
    << remoteIdFullColumnName()
  
    << remoteRevisionFullColumnName()
  
    << gidFullColumnName()
  
    << collectionIdFullColumnName()
  
    << mimeTypeIdFullColumnName()
  
    << datetimeFullColumnName()
  
    << atimeFullColumnName()
  
    << dirtyFullColumnName()
  
    << sizeFullColumnName()
  
  ;
  return columns;
}


QString PimItem::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString PimItem::idFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.id" );
  return column;
}

QString PimItem::revColumn()
{
  static const QString column = QStringLiteral( "rev" );
  return column;
}

QString PimItem::revFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.rev" );
  return column;
}

QString PimItem::remoteIdColumn()
{
  static const QString column = QStringLiteral( "remoteId" );
  return column;
}

QString PimItem::remoteIdFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.remoteId" );
  return column;
}

QString PimItem::remoteRevisionColumn()
{
  static const QString column = QStringLiteral( "remoteRevision" );
  return column;
}

QString PimItem::remoteRevisionFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.remoteRevision" );
  return column;
}

QString PimItem::gidColumn()
{
  static const QString column = QStringLiteral( "gid" );
  return column;
}

QString PimItem::gidFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.gid" );
  return column;
}

QString PimItem::collectionIdColumn()
{
  static const QString column = QStringLiteral( "collectionId" );
  return column;
}

QString PimItem::collectionIdFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.collectionId" );
  return column;
}

QString PimItem::mimeTypeIdColumn()
{
  static const QString column = QStringLiteral( "mimeTypeId" );
  return column;
}

QString PimItem::mimeTypeIdFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.mimeTypeId" );
  return column;
}

QString PimItem::datetimeColumn()
{
  static const QString column = QStringLiteral( "datetime" );
  return column;
}

QString PimItem::datetimeFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.datetime" );
  return column;
}

QString PimItem::atimeColumn()
{
  static const QString column = QStringLiteral( "atime" );
  return column;
}

QString PimItem::atimeFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.atime" );
  return column;
}

QString PimItem::dirtyColumn()
{
  static const QString column = QStringLiteral( "dirty" );
  return column;
}

QString PimItem::dirtyFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.dirty" );
  return column;
}

QString PimItem::sizeColumn()
{
  static const QString column = QStringLiteral( "size" );
  return column;
}

QString PimItem::sizeFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTable.size" );
  return column;
}



// count records
int PimItem::count( const QString &column, const QVariant &value )
{
  return Entity::count<PimItem>( column, value );
}

// check existence

bool PimItem::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}



// result extraction
QVector< PimItem > PimItem::extractResult( QSqlQuery & query )
{
  QVector<PimItem> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( PimItem(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          int() :
          
          query.value( 1 ).value<int>()
            ,
        (query.isNull(2)) ?
          QString() :
          
          Utils::variantToString( query.value( 2 ) )
            ,
        (query.isNull(3)) ?
          QString() :
          
          Utils::variantToString( query.value( 3 ) )
            ,
        (query.isNull(4)) ?
          QString() :
          
          Utils::variantToString( query.value( 4 ) )
            ,
        (query.isNull(5)) ?
          qint64() :
          
          query.value( 5 ).value<qint64>()
            ,
        (query.isNull(6)) ?
          qint64() :
          
          query.value( 6 ).value<qint64>()
            ,
        (query.isNull(7)) ?
          QDateTime() :
          
          query.value( 7 ).value<QDateTime>()
            ,
        (query.isNull(8)) ?
          QDateTime() :
          
          query.value( 8 ).value<QDateTime>()
            ,
        (query.isNull(9)) ?
          bool() :
          
          query.value( 9 ).value<bool>()
            ,
        (query.isNull(10)) ?
          qint64() :
          
          query.value( 10 ).value<qint64>()
            
    ) );
  }
  return rv;
}

// data retrieval
PimItem PimItem::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, PimItem>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return PimItem();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return PimItem();
  }
  if ( !qb.query().next() ) {
    return PimItem();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const int value2 =
    
        (qb.query().isNull(valueIndex)) ?
        int() :
        
          qb.query().value( valueIndex ).value<int>()
          
        ; ++valueIndex;
      
    const QString value3 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value4 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value5 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const qint64 value6 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const qint64 value7 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QDateTime value8 =
    
        (qb.query().isNull(valueIndex)) ?
        QDateTime() :
        
          Utils::variantToDateTime(qb.query().value(valueIndex))
          
        ; ++valueIndex;
      
    const QDateTime value9 =
    
        (qb.query().isNull(valueIndex)) ?
        QDateTime() :
        
          Utils::variantToDateTime(qb.query().value(valueIndex))
          
        ; ++valueIndex;
      
    const bool value10 =
    
        (qb.query().isNull(valueIndex)) ?
        bool() :
        
          qb.query().value( valueIndex ).value<bool>()
          
        ; ++valueIndex;
      
    const qint64 value11 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      PimItem rv(
  
    value1,
    value2,
    value3,
    value4,
    value5,
    value6,
    value7,
    value8,
    value9,
    value10,
    value11
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}



QVector<PimItem> PimItem::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<PimItem>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<PimItem>();
  }
  return extractResult( qb.query() );
}

QVector<PimItem> PimItem::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<PimItem>();

  SelectQueryBuilder<PimItem> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<PimItem>();
  }
  return qb.result();
}

// data retrieval for referenced tables
Collection PimItem::collection() const
{
  return Collection::retrieveById( collectionId() );
}

void PimItem::
    setCollection
    ( const Collection &value )
{
  d->collectionId = value.id();
  d->collectionId_changed = true;
}
MimeType PimItem::mimeType() const
{
  return MimeType::retrieveById( mimeTypeId() );
}

void PimItem::
    setMimeType
    ( const MimeType &value )
{
  d->mimeTypeId = value.id();
  d->mimeTypeId_changed = true;
}


// data retrieval for inverse referenced tables

QVector<Part> PimItem::parts() const
{
  return Part::retrieveFiltered( Part::pimItemIdColumn(), id() );
}


// data retrieval for n:m relations
QVector<Flag> PimItem::flags() const
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Flag>();

  QueryBuilder qb( Flag::tableName(), QueryBuilder::Select );
  static const QStringList columns = QStringList()
  
    << Flag::idFullColumnName()
  
    << Flag::nameFullColumnName()
  
  ;
  qb.addColumns(columns);
  qb.addJoin( QueryBuilder::InnerJoin, PimItemFlagRelation::tableName(),
              PimItemFlagRelation::rightFullColumnName(),
              Flag::idFullColumnName() );
  qb.addValueCondition( PimItemFlagRelation::leftFullColumnName(), Query::Equals, id() );

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table PimItemFlagRelation"
      << qb.query().lastError().text();
    return QVector<Flag>();
  }

  return Flag::extractResult( qb.query() );
}

// manipulate n:m relations
bool PimItem::relatesToFlag( const Flag & value ) const
{
  return Entity::relatesTo<PimItemFlagRelation>( id(), value.id() );
}

bool PimItem::relatesToFlag( qint64 leftId, qint64 rightId )
{
  return Entity::relatesTo<PimItemFlagRelation>( leftId, rightId );
}

bool PimItem::addFlag( const Flag & value ) const
{
  return Entity::addToRelation<PimItemFlagRelation>( id(), value.id() );
}

bool PimItem::addFlag( qint64 leftId, qint64 rightId )
{
  return Entity::addToRelation<PimItemFlagRelation>( leftId, rightId );
}

bool PimItem::removeFlag( const Flag & value ) const
{
  return Entity::removeFromRelation<PimItemFlagRelation>( id(), value.id() );
}

bool PimItem::removeFlag( qint64 leftId, qint64 rightId )
{
  return Entity::removeFromRelation<PimItemFlagRelation>( leftId, rightId );
}

bool PimItem::clearFlags() const
{
  return Entity::clearRelation<PimItemFlagRelation>( id() );
}

bool PimItem::clearFlags( qint64 id )
{
  return Entity::clearRelation<PimItemFlagRelation>( id );
}



// data retrieval for n:m relations
QVector<Tag> PimItem::tags() const
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Tag>();

  QueryBuilder qb( Tag::tableName(), QueryBuilder::Select );
  static const QStringList columns = QStringList()
  
    << Tag::idFullColumnName()
  
    << Tag::gidFullColumnName()
  
    << Tag::parentIdFullColumnName()
  
    << Tag::typeIdFullColumnName()
  
  ;
  qb.addColumns(columns);
  qb.addJoin( QueryBuilder::InnerJoin, PimItemTagRelation::tableName(),
              PimItemTagRelation::rightFullColumnName(),
              Tag::idFullColumnName() );
  qb.addValueCondition( PimItemTagRelation::leftFullColumnName(), Query::Equals, id() );

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table PimItemTagRelation"
      << qb.query().lastError().text();
    return QVector<Tag>();
  }

  return Tag::extractResult( qb.query() );
}

// manipulate n:m relations
bool PimItem::relatesToTag( const Tag & value ) const
{
  return Entity::relatesTo<PimItemTagRelation>( id(), value.id() );
}

bool PimItem::relatesToTag( qint64 leftId, qint64 rightId )
{
  return Entity::relatesTo<PimItemTagRelation>( leftId, rightId );
}

bool PimItem::addTag( const Tag & value ) const
{
  return Entity::addToRelation<PimItemTagRelation>( id(), value.id() );
}

bool PimItem::addTag( qint64 leftId, qint64 rightId )
{
  return Entity::addToRelation<PimItemTagRelation>( leftId, rightId );
}

bool PimItem::removeTag( const Tag & value ) const
{
  return Entity::removeFromRelation<PimItemTagRelation>( id(), value.id() );
}

bool PimItem::removeTag( qint64 leftId, qint64 rightId )
{
  return Entity::removeFromRelation<PimItemTagRelation>( leftId, rightId );
}

bool PimItem::clearTags() const
{
  return Entity::clearRelation<PimItemTagRelation>( id() );
}

bool PimItem::clearTags( qint64 id )
{
  return Entity::clearRelation<PimItemTagRelation>( id );
}



#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const PimItem& entity )
{
  d << "[PimItem: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "rev = " <<
    
        entity.rev()
      << ", "
    << "remoteId = " <<
    
        entity.remoteId()
      << ", "
    << "remoteRevision = " <<
    
        entity.remoteRevision()
      << ", "
    << "gid = " <<
    
        entity.gid()
      << ", "
    << "collectionId = " <<
    
        entity.collectionId()
      << ", "
    << "mimeTypeId = " <<
    
        entity.mimeTypeId()
      << ", "
    << "datetime = " <<
    
        entity.datetime()
      << ", "
    << "atime = " <<
    
        entity.atime()
      << ", "
    << "dirty = " <<
    
        entity.dirty()
      << ", "
    << "size = " <<
    
        entity.size()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool PimItem::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->rev_changed )
      
      qb.setColumnValue( revColumn(), this->rev() );
        
    if ( d->remoteId_changed )
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        
    if ( d->remoteRevision_changed )
      
      qb.setColumnValue( remoteRevisionColumn(), this->remoteRevision() );
        
    if ( d->gid_changed )
      
      qb.setColumnValue( gidColumn(), this->gid() );
        
    if ( d->collectionId_changed  && d->collectionId > 0 )
      qb.setColumnValue( collectionIdColumn(), this->collectionId() );
    
    if ( d->mimeTypeId_changed  && d->mimeTypeId > 0 )
      qb.setColumnValue( mimeTypeIdColumn(), this->mimeTypeId() );
    
    if ( d->datetime_changed )
      
      qb.setColumnValue( datetimeColumn(), this->datetime() );
        
    if ( d->atime_changed )
      
      qb.setColumnValue( atimeColumn(), this->atime() );
        
    if ( d->dirty_changed )
      
      qb.setColumnValue( dirtyColumn(), this->dirty() );
        
    if ( d->size_changed )
      
      qb.setColumnValue( sizeColumn(), this->size() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool PimItem::hasPendingChanges() const
{
  return false
  
    || d->rev_changed
  
    || d->remoteId_changed
  
    || d->remoteRevision_changed
  
    || d->gid_changed
  
    || d->collectionId_changed
  
    || d->mimeTypeId_changed
  
    || d->datetime_changed
  
    || d->atime_changed
  
    || d->dirty_changed
  
    || d->size_changed
  ;
}

// update existing data
bool PimItem::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->rev_changed ) {
      
      qb.setColumnValue( revColumn(), this->rev() );
        
    }
  
    if ( d->remoteId_changed ) {
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        
    }
  
    if ( d->remoteRevision_changed ) {
      
      qb.setColumnValue( remoteRevisionColumn(), this->remoteRevision() );
        
    }
  
    if ( d->gid_changed ) {
      
      qb.setColumnValue( gidColumn(), this->gid() );
        
    }
  
    if ( d->collectionId_changed ) {
      
      if ( d->collectionId <= 0 )
        qb.setColumnValue( collectionIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( collectionIdColumn(), this->collectionId() );
        
    }
  
    if ( d->mimeTypeId_changed ) {
      
      if ( d->mimeTypeId <= 0 )
        qb.setColumnValue( mimeTypeIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( mimeTypeIdColumn(), this->mimeTypeId() );
        
    }
  
    if ( d->datetime_changed ) {
      
      qb.setColumnValue( datetimeColumn(), this->datetime() );
        
    }
  
    if ( d->atime_changed ) {
      
      qb.setColumnValue( atimeColumn(), this->atime() );
        
    }
  
    if ( d->dirty_changed ) {
      
      qb.setColumnValue( dirtyColumn(), this->dirty() );
        
    }
  
    if ( d->size_changed ) {
      
      qb.setColumnValue( sizeColumn(), this->size() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool PimItem::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<PimItem>( column, value );
}


bool PimItem::remove()
{
  invalidateCache();
  return Entity::remove<PimItem>( idColumn(), id() );
}

bool PimItem::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void PimItem::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
  }
}

void PimItem::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
  }
}

void PimItem::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class Flag::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , name()
    
      , name_changed( false )
    
    {}

    
    QString name;
    
    bool name_changed : 1;
    

    static void addToCache( const Flag & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, Flag > idCache;
    
    static QHash<QString, Flag > nameCache;
    
};


// static members
QAtomicInt Flag::Private::cacheEnabled(0);
QMutex Flag::Private::cacheMutex;

QHash<qint64, Flag > Flag::Private::idCache;

QHash<QString, Flag > Flag::Private::nameCache;



void Flag::Private::addToCache( const Flag & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.name(), entry );
      
}


// constructor
Flag::Flag() : Entity(),
  d( new Private )
{
}

Flag::Flag(
  const QString &name
) :
  Entity(),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}

Flag::Flag(
  qint64 id, const QString &name
) :
  Entity( id ),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}
Flag::Flag( const Flag & other )
  : Entity( other ), d( other.d )
{
}

// destructor
Flag::~Flag() {}

// assignment operator
Flag& Flag::operator=( const Flag & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool Flag::operator==( const Flag & other ) const
{
  return id() == other.id();
}

// accessor methods
QString Flag::name() const
{
  return d->name;
}

void Flag::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}



// SQL table information
QString Flag::tableName()
{
  static const QString tableName = QStringLiteral( "FlagTable" );
  return tableName;
}

QStringList Flag::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << nameColumn()
  
  ;
  return columns;
}

QStringList Flag::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << nameFullColumnName()
  
  ;
  return columns;
}


QString Flag::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString Flag::idFullColumnName()
{
  static const QString column = QStringLiteral( "FlagTable.id" );
  return column;
}

QString Flag::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString Flag::nameFullColumnName()
{
  static const QString column = QStringLiteral( "FlagTable.name" );
  return column;
}



// count records
int Flag::count( const QString &column, const QVariant &value )
{
  return Entity::count<Flag>( column, value );
}

// check existence

bool Flag::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool Flag::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< Flag > Flag::extractResult( QSqlQuery & query )
{
  QVector<Flag> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( Flag(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            
    ) );
  }
  return rv;
}

// data retrieval
Flag Flag::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, Flag>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Flag();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return Flag();
  }
  if ( !qb.query().next() ) {
    return Flag();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      Flag rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

Flag Flag::retrieveByName( const QString &name )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, Flag>::const_iterator it = Private::nameCache.constFind(name);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Flag();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nameColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with name"
      << name << "from table" << tableName()
      << qb.query().lastError().text();
    return Flag();
  }
  if ( !qb.query().next() ) {
    return Flag();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    name;
      Flag rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

Flag Flag::retrieveByNameOrCreate( const QString &name)
{
  static QMutex lock;
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return Flag();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByName(name);
}


QVector<Flag> Flag::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Flag>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<Flag>();
  }
  return extractResult( qb.query() );
}

QVector<Flag> Flag::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Flag>();

  SelectQueryBuilder<Flag> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<Flag>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const Flag& entity )
{
  d << "[Flag: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "name = " <<
    
        entity.name()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool Flag::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool Flag::hasPendingChanges() const
{
  return false
  
    || d->name_changed
  ;
}

// update existing data
bool Flag::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool Flag::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<Flag>( column, value );
}


bool Flag::remove()
{
  invalidateCache();
  return Entity::remove<Flag>( idColumn(), id() );
}

bool Flag::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void Flag::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( name() );
        
  }
}

void Flag::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void Flag::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class PartType::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , name()
    
      , ns()
    
      , name_changed( false )
    
      , ns_changed( false )
    
    {}

    
    QString name;
    
    QString ns;
    
    bool name_changed : 1;
    
    bool ns_changed : 1;
    

    static void addToCache( const PartType & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, PartType > idCache;
    
    static QHash<QString, PartType > nameCache;
    
};


// static members
QAtomicInt PartType::Private::cacheEnabled(0);
QMutex PartType::Private::cacheMutex;

QHash<qint64, PartType > PartType::Private::idCache;

QHash<QString, PartType > PartType::Private::nameCache;



void PartType::Private::addToCache( const PartType & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.ns() + QLatin1Char(':') + entry.name(), entry );
      
}


// constructor
PartType::PartType() : Entity(),
  d( new Private )
{
}

PartType::PartType(
  const QString &name, const QString &ns
) :
  Entity(),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

  d->ns = ns;
  d->ns_changed = true;

}

PartType::PartType(
  qint64 id, const QString &name, const QString &ns
) :
  Entity( id ),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

  d->ns = ns;
  d->ns_changed = true;

}
PartType::PartType( const PartType & other )
  : Entity( other ), d( other.d )
{
}

// destructor
PartType::~PartType() {}

// assignment operator
PartType& PartType::operator=( const PartType & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool PartType::operator==( const PartType & other ) const
{
  return id() == other.id();
}

// accessor methods
QString PartType::name() const
{
  return d->name;
}

void PartType::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}

QString PartType::ns() const
{
  return d->ns;
}

void PartType::
setNs( const QString &ns )

{
  d->ns = ns;
  d->ns_changed = true;
}



// SQL table information
QString PartType::tableName()
{
  static const QString tableName = QStringLiteral( "PartTypeTable" );
  return tableName;
}

QStringList PartType::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << nameColumn()
  
    << nsColumn()
  
  ;
  return columns;
}

QStringList PartType::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << nameFullColumnName()
  
    << nsFullColumnName()
  
  ;
  return columns;
}


QString PartType::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString PartType::idFullColumnName()
{
  static const QString column = QStringLiteral( "PartTypeTable.id" );
  return column;
}

QString PartType::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString PartType::nameFullColumnName()
{
  static const QString column = QStringLiteral( "PartTypeTable.name" );
  return column;
}

QString PartType::nsColumn()
{
  static const QString column = QStringLiteral( "ns" );
  return column;
}

QString PartType::nsFullColumnName()
{
  static const QString column = QStringLiteral( "PartTypeTable.ns" );
  return column;
}



// count records
int PartType::count( const QString &column, const QVariant &value )
{
  return Entity::count<PartType>( column, value );
}

// check existence

bool PartType::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool PartType::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< PartType > PartType::extractResult( QSqlQuery & query )
{
  QVector<PartType> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( PartType(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            ,
        (query.isNull(2)) ?
          QString() :
          
          Utils::variantToString( query.value( 2 ) )
            
    ) );
  }
  return rv;
}

// data retrieval
PartType PartType::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, PartType>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return PartType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return PartType();
  }
  if ( !qb.query().next() ) {
    return PartType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value3 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      PartType rv(
  
    value1,
    value2,
    value3
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

PartType PartType::retrieveByFQName( const QString & ns, const QString & name )
{
  const QString fqname = ns + QLatin1Char(':') + name;
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, PartType>::const_iterator it = Private::nameCache.constFind(fqname);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return PartType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nsColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nsColumn(), Query::Equals, ns );
  
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with ns"
      << ns << "from table" << tableName()
      << qb.query().lastError().text();
    return PartType();
  }
  if ( !qb.query().next() ) {
    return PartType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const QString value3 =
    ns;
      PartType rv(
  
    value1,
    value2,
    value3
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

PartType PartType::retrieveByFQNameOrCreate( const QString & ns, const QString & name )
{
  static QMutex lock;
  PartType rv = retrieveByFQName(ns, name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setNs(ns);
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return PartType();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByFQName(ns, name);
}


QVector<PartType> PartType::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<PartType>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<PartType>();
  }
  return extractResult( qb.query() );
}

QVector<PartType> PartType::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<PartType>();

  SelectQueryBuilder<PartType> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<PartType>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const PartType& entity )
{
  d << "[PartType: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "name = " <<
    
        entity.name()
      << ", "
    << "ns = " <<
    
        entity.ns()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool PartType::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    if ( d->ns_changed )
      
      qb.setColumnValue( nsColumn(), this->ns() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool PartType::hasPendingChanges() const
{
  return false
  
    || d->name_changed
  
    || d->ns_changed
  ;
}

// update existing data
bool PartType::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
    if ( d->ns_changed ) {
      
      qb.setColumnValue( nsColumn(), this->ns() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool PartType::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<PartType>( column, value );
}


bool PartType::remove()
{
  invalidateCache();
  return Entity::remove<PartType>( idColumn(), id() );
}

bool PartType::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void PartType::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( ns() + QLatin1Char(':') + name() );
        
  }
}

void PartType::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void PartType::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class Part::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , pimItemId( 0 )
    
      , partTypeId( 0 )
    
      , datasize( 0 )
    
      , data()
    
      , version( 0 )
    
      , storage( Internal )
    
      , pimItemId_changed( false )
    
      , partTypeId_changed( false )
    
      , data_changed( false )
    
      , datasize_changed( false )
    
      , version_changed( false )
    
      , storage_changed( false )
    
    {}

    
    qint64 pimItemId;
    
    qint64 partTypeId;
    
    qint64 datasize;
    
    QByteArray data;
    
    int version;
    Storage storage;
    
    bool pimItemId_changed : 1;
    
    bool partTypeId_changed : 1;
    
    bool data_changed : 1;
    
    bool datasize_changed : 1;
    
    bool version_changed : 1;
    
    bool storage_changed : 1;
    

    static void addToCache( const Part & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, Part > idCache;
    
};


// static members
QAtomicInt Part::Private::cacheEnabled(0);
QMutex Part::Private::cacheMutex;

QHash<qint64, Part > Part::Private::idCache;



void Part::Private::addToCache( const Part & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
}


// constructor
Part::Part() : Entity(),
  d( new Private )
{
}

Part::Part(
  qint64 pimItemId, qint64 partTypeId, const QByteArray &data, qint64 datasize, int version, Part::Storage storage
) :
  Entity(),
  d( new Private )
{

  d->pimItemId = pimItemId;
  d->pimItemId_changed = true;

  d->partTypeId = partTypeId;
  d->partTypeId_changed = true;

  d->data = data;
  d->data_changed = true;

  d->datasize = datasize;
  d->datasize_changed = true;

  d->version = version;
  d->version_changed = true;

  d->storage = storage;
  d->storage_changed = true;

}

Part::Part(
  qint64 id, qint64 pimItemId, qint64 partTypeId, const QByteArray &data, qint64 datasize, int version, Part::Storage storage
) :
  Entity( id ),
  d( new Private )
{

  d->pimItemId = pimItemId;
  d->pimItemId_changed = true;

  d->partTypeId = partTypeId;
  d->partTypeId_changed = true;

  d->data = data;
  d->data_changed = true;

  d->datasize = datasize;
  d->datasize_changed = true;

  d->version = version;
  d->version_changed = true;

  d->storage = storage;
  d->storage_changed = true;

}
Part::Part( const Part & other )
  : Entity( other ), d( other.d )
{
}

// destructor
Part::~Part() {}

// assignment operator
Part& Part::operator=( const Part & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool Part::operator==( const Part & other ) const
{
  return id() == other.id();
}

// accessor methods
qint64 Part::pimItemId() const
{
  return d->pimItemId;
}

void Part::
setPimItemId( qint64 pimItemId )

{
  d->pimItemId = pimItemId;
  d->pimItemId_changed = true;
}

qint64 Part::partTypeId() const
{
  return d->partTypeId;
}

void Part::
setPartTypeId( qint64 partTypeId )

{
  d->partTypeId = partTypeId;
  d->partTypeId_changed = true;
}

QByteArray Part::data() const
{
  return d->data;
}

void Part::
setData( const QByteArray &data )

{
  d->data = data;
  d->data_changed = true;
}

qint64 Part::datasize() const
{
  return d->datasize;
}

void Part::
setDatasize( qint64 datasize )

{
  d->datasize = datasize;
  d->datasize_changed = true;
}

int Part::version() const
{
  return d->version;
}

void Part::
setVersion( int version )

{
  d->version = version;
  d->version_changed = true;
}

Part::Storage Part::storage() const
{
  return d->storage;
}

void Part::
setStorage( Part::Storage storage )

{
  d->storage = storage;
  d->storage_changed = true;
}



// SQL table information
QString Part::tableName()
{
  static const QString tableName = QStringLiteral( "PartTable" );
  return tableName;
}

QStringList Part::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << pimItemIdColumn()
  
    << partTypeIdColumn()
  
    << dataColumn()
  
    << datasizeColumn()
  
    << versionColumn()
  
    << storageColumn()
  
  ;
  return columns;
}

QStringList Part::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << pimItemIdFullColumnName()
  
    << partTypeIdFullColumnName()
  
    << dataFullColumnName()
  
    << datasizeFullColumnName()
  
    << versionFullColumnName()
  
    << storageFullColumnName()
  
  ;
  return columns;
}


QString Part::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString Part::idFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.id" );
  return column;
}

QString Part::pimItemIdColumn()
{
  static const QString column = QStringLiteral( "pimItemId" );
  return column;
}

QString Part::pimItemIdFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.pimItemId" );
  return column;
}

QString Part::partTypeIdColumn()
{
  static const QString column = QStringLiteral( "partTypeId" );
  return column;
}

QString Part::partTypeIdFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.partTypeId" );
  return column;
}

QString Part::dataColumn()
{
  static const QString column = QStringLiteral( "data" );
  return column;
}

QString Part::dataFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.data" );
  return column;
}

QString Part::datasizeColumn()
{
  static const QString column = QStringLiteral( "datasize" );
  return column;
}

QString Part::datasizeFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.datasize" );
  return column;
}

QString Part::versionColumn()
{
  static const QString column = QStringLiteral( "version" );
  return column;
}

QString Part::versionFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.version" );
  return column;
}

QString Part::storageColumn()
{
  static const QString column = QStringLiteral( "storage" );
  return column;
}

QString Part::storageFullColumnName()
{
  static const QString column = QStringLiteral( "PartTable.storage" );
  return column;
}



// count records
int Part::count( const QString &column, const QVariant &value )
{
  return Entity::count<Part>( column, value );
}

// check existence

bool Part::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}



// result extraction
QVector< Part > Part::extractResult( QSqlQuery & query )
{
  QVector<Part> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( Part(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          qint64() :
          
          query.value( 1 ).value<qint64>()
            ,
        (query.isNull(2)) ?
          qint64() :
          
          query.value( 2 ).value<qint64>()
            ,
        (query.isNull(3)) ?
          QByteArray() :
          
          query.value( 3 ).value<QByteArray>()
            ,
        (query.isNull(4)) ?
          qint64() :
          
          query.value( 4 ).value<qint64>()
            ,
        (query.isNull(5)) ?
          int() :
          
          query.value( 5 ).value<int>()
            ,
        (query.isNull(6)) ?
          Part::Storage() :
          
            static_cast<Storage>(query.value( 6 ).value<int>())
            
    ) );
  }
  return rv;
}

// data retrieval
Part Part::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, Part>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Part();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return Part();
  }
  if ( !qb.query().next() ) {
    return Part();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const qint64 value2 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const qint64 value3 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QByteArray value4 =
    
        (qb.query().isNull(valueIndex)) ?
        QByteArray() :
        
          qb.query().value( valueIndex ).value<QByteArray>()
          
        ; ++valueIndex;
      
    const qint64 value5 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const int value6 =
    
        (qb.query().isNull(valueIndex)) ?
        int() :
        
          qb.query().value( valueIndex ).value<int>()
          
        ; ++valueIndex;
      
    const Part::Storage value7 =
    
        (qb.query().isNull(valueIndex)) ?
        Part::Storage() :
        
          static_cast<Storage>(qb.query().value( valueIndex ).value<int>())
          
        ; ++valueIndex;
      Part rv(
  
    value1,
    value2,
    value3,
    value4,
    value5,
    value6,
    value7
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}



QVector<Part> Part::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Part>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<Part>();
  }
  return extractResult( qb.query() );
}

QVector<Part> Part::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Part>();

  SelectQueryBuilder<Part> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<Part>();
  }
  return qb.result();
}

// data retrieval for referenced tables
PimItem Part::pimItem() const
{
  return PimItem::retrieveById( pimItemId() );
}

void Part::
    setPimItem
    ( const PimItem &value )
{
  d->pimItemId = value.id();
  d->pimItemId_changed = true;
}
PartType Part::partType() const
{
  return PartType::retrieveById( partTypeId() );
}

void Part::
    setPartType
    ( const PartType &value )
{
  d->partTypeId = value.id();
  d->partTypeId_changed = true;
}


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const Part& entity )
{
  d << "[Part: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "pimItemId = " <<
    
        entity.pimItemId()
      << ", "
    << "partTypeId = " <<
    
        entity.partTypeId()
      << ", "
    << "data = " <<
    
        entity.data()
      << ", "
    << "datasize = " <<
    
        entity.datasize()
      << ", "
    << "version = " <<
    
        entity.version()
      << ", "
    << "storage = " <<
    
        static_cast<int>(entity.storage())
      
    << "]";
  return d;
}
#endif

// inserting new data
bool Part::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->pimItemId_changed  && d->pimItemId > 0 )
      qb.setColumnValue( pimItemIdColumn(), this->pimItemId() );
    
    if ( d->partTypeId_changed  && d->partTypeId > 0 )
      qb.setColumnValue( partTypeIdColumn(), this->partTypeId() );
    
    if ( d->data_changed )
      
      qb.setColumnValue( dataColumn(), this->data() );
        
    if ( d->datasize_changed )
      
      qb.setColumnValue( datasizeColumn(), this->datasize() );
        
    if ( d->version_changed )
      
      qb.setColumnValue( versionColumn(), this->version() );
        
    if ( d->storage_changed )
      
      qb.setColumnValue( storageColumn(), static_cast<int>(this->storage()) );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool Part::hasPendingChanges() const
{
  return false
  
    || d->pimItemId_changed
  
    || d->partTypeId_changed
  
    || d->data_changed
  
    || d->datasize_changed
  
    || d->version_changed
  
    || d->storage_changed
  ;
}

// update existing data
bool Part::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->pimItemId_changed ) {
      
      if ( d->pimItemId <= 0 )
        qb.setColumnValue( pimItemIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( pimItemIdColumn(), this->pimItemId() );
        
    }
  
    if ( d->partTypeId_changed ) {
      
      if ( d->partTypeId <= 0 )
        qb.setColumnValue( partTypeIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( partTypeIdColumn(), this->partTypeId() );
        
    }
  
    if ( d->data_changed ) {
      
      qb.setColumnValue( dataColumn(), this->data() );
        
    }
  
    if ( d->datasize_changed ) {
      
      qb.setColumnValue( datasizeColumn(), this->datasize() );
        
    }
  
    if ( d->version_changed ) {
      
      qb.setColumnValue( versionColumn(), this->version() );
        
    }
  
    if ( d->storage_changed ) {
      
      qb.setColumnValue( storageColumn(), static_cast<int>(this->storage()) );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool Part::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<Part>( column, value );
}


bool Part::remove()
{
  invalidateCache();
  return Entity::remove<Part>( idColumn(), id() );
}

bool Part::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void Part::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
  }
}

void Part::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
  }
}

void Part::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class CollectionAttribute::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , collectionId( 0 )
    
      , type()
    
      , value()
    
      , collectionId_changed( false )
    
      , type_changed( false )
    
      , value_changed( false )
    
    {}

    
    qint64 collectionId;
    
    QByteArray type;
    
    QByteArray value;
    
    bool collectionId_changed : 1;
    
    bool type_changed : 1;
    
    bool value_changed : 1;
    

    static void addToCache( const CollectionAttribute & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, CollectionAttribute > idCache;
    
};


// static members
QAtomicInt CollectionAttribute::Private::cacheEnabled(0);
QMutex CollectionAttribute::Private::cacheMutex;

QHash<qint64, CollectionAttribute > CollectionAttribute::Private::idCache;



void CollectionAttribute::Private::addToCache( const CollectionAttribute & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
}


// constructor
CollectionAttribute::CollectionAttribute() : Entity(),
  d( new Private )
{
}

CollectionAttribute::CollectionAttribute(
  qint64 collectionId, const QByteArray &type, const QByteArray &value
) :
  Entity(),
  d( new Private )
{

  d->collectionId = collectionId;
  d->collectionId_changed = true;

  d->type = type;
  d->type_changed = true;

  d->value = value;
  d->value_changed = true;

}

CollectionAttribute::CollectionAttribute(
  qint64 id, qint64 collectionId, const QByteArray &type, const QByteArray &value
) :
  Entity( id ),
  d( new Private )
{

  d->collectionId = collectionId;
  d->collectionId_changed = true;

  d->type = type;
  d->type_changed = true;

  d->value = value;
  d->value_changed = true;

}
CollectionAttribute::CollectionAttribute( const CollectionAttribute & other )
  : Entity( other ), d( other.d )
{
}

// destructor
CollectionAttribute::~CollectionAttribute() {}

// assignment operator
CollectionAttribute& CollectionAttribute::operator=( const CollectionAttribute & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool CollectionAttribute::operator==( const CollectionAttribute & other ) const
{
  return id() == other.id();
}

// accessor methods
qint64 CollectionAttribute::collectionId() const
{
  return d->collectionId;
}

void CollectionAttribute::
setCollectionId( qint64 collectionId )

{
  d->collectionId = collectionId;
  d->collectionId_changed = true;
}

QByteArray CollectionAttribute::type() const
{
  return d->type;
}

void CollectionAttribute::
setType( const QByteArray &type )

{
  d->type = type;
  d->type_changed = true;
}

QByteArray CollectionAttribute::value() const
{
  return d->value;
}

void CollectionAttribute::
setValue( const QByteArray &value )

{
  d->value = value;
  d->value_changed = true;
}



// SQL table information
QString CollectionAttribute::tableName()
{
  static const QString tableName = QStringLiteral( "CollectionAttributeTable" );
  return tableName;
}

QStringList CollectionAttribute::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << collectionIdColumn()
  
    << typeColumn()
  
    << valueColumn()
  
  ;
  return columns;
}

QStringList CollectionAttribute::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << collectionIdFullColumnName()
  
    << typeFullColumnName()
  
    << valueFullColumnName()
  
  ;
  return columns;
}


QString CollectionAttribute::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString CollectionAttribute::idFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionAttributeTable.id" );
  return column;
}

QString CollectionAttribute::collectionIdColumn()
{
  static const QString column = QStringLiteral( "collectionId" );
  return column;
}

QString CollectionAttribute::collectionIdFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionAttributeTable.collectionId" );
  return column;
}

QString CollectionAttribute::typeColumn()
{
  static const QString column = QStringLiteral( "type" );
  return column;
}

QString CollectionAttribute::typeFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionAttributeTable.type" );
  return column;
}

QString CollectionAttribute::valueColumn()
{
  static const QString column = QStringLiteral( "value" );
  return column;
}

QString CollectionAttribute::valueFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionAttributeTable.value" );
  return column;
}



// count records
int CollectionAttribute::count( const QString &column, const QVariant &value )
{
  return Entity::count<CollectionAttribute>( column, value );
}

// check existence

bool CollectionAttribute::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}



// result extraction
QVector< CollectionAttribute > CollectionAttribute::extractResult( QSqlQuery & query )
{
  QVector<CollectionAttribute> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( CollectionAttribute(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          qint64() :
          
          query.value( 1 ).value<qint64>()
            ,
        (query.isNull(2)) ?
          QByteArray() :
          
          query.value( 2 ).value<QByteArray>()
            ,
        (query.isNull(3)) ?
          QByteArray() :
          
          query.value( 3 ).value<QByteArray>()
            
    ) );
  }
  return rv;
}

// data retrieval
CollectionAttribute CollectionAttribute::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, CollectionAttribute>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return CollectionAttribute();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return CollectionAttribute();
  }
  if ( !qb.query().next() ) {
    return CollectionAttribute();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const qint64 value2 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QByteArray value3 =
    
        (qb.query().isNull(valueIndex)) ?
        QByteArray() :
        
          qb.query().value( valueIndex ).value<QByteArray>()
          
        ; ++valueIndex;
      
    const QByteArray value4 =
    
        (qb.query().isNull(valueIndex)) ?
        QByteArray() :
        
          qb.query().value( valueIndex ).value<QByteArray>()
          
        ; ++valueIndex;
      CollectionAttribute rv(
  
    value1,
    value2,
    value3,
    value4
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}



QVector<CollectionAttribute> CollectionAttribute::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<CollectionAttribute>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<CollectionAttribute>();
  }
  return extractResult( qb.query() );
}

QVector<CollectionAttribute> CollectionAttribute::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<CollectionAttribute>();

  SelectQueryBuilder<CollectionAttribute> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<CollectionAttribute>();
  }
  return qb.result();
}

// data retrieval for referenced tables
Collection CollectionAttribute::collection() const
{
  return Collection::retrieveById( collectionId() );
}

void CollectionAttribute::
    setCollection
    ( const Collection &value )
{
  d->collectionId = value.id();
  d->collectionId_changed = true;
}


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const CollectionAttribute& entity )
{
  d << "[CollectionAttribute: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "collectionId = " <<
    
        entity.collectionId()
      << ", "
    << "type = " <<
    
        entity.type()
      << ", "
    << "value = " <<
    
        entity.value()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool CollectionAttribute::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->collectionId_changed  && d->collectionId > 0 )
      qb.setColumnValue( collectionIdColumn(), this->collectionId() );
    
    if ( d->type_changed )
      
      qb.setColumnValue( typeColumn(), this->type() );
        
    if ( d->value_changed )
      
      qb.setColumnValue( valueColumn(), this->value() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool CollectionAttribute::hasPendingChanges() const
{
  return false
  
    || d->collectionId_changed
  
    || d->type_changed
  
    || d->value_changed
  ;
}

// update existing data
bool CollectionAttribute::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->collectionId_changed ) {
      
      if ( d->collectionId <= 0 )
        qb.setColumnValue( collectionIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( collectionIdColumn(), this->collectionId() );
        
    }
  
    if ( d->type_changed ) {
      
      qb.setColumnValue( typeColumn(), this->type() );
        
    }
  
    if ( d->value_changed ) {
      
      qb.setColumnValue( valueColumn(), this->value() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool CollectionAttribute::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<CollectionAttribute>( column, value );
}


bool CollectionAttribute::remove()
{
  invalidateCache();
  return Entity::remove<CollectionAttribute>( idColumn(), id() );
}

bool CollectionAttribute::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void CollectionAttribute::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
  }
}

void CollectionAttribute::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
  }
}

void CollectionAttribute::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class TagType::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , name()
    
      , name_changed( false )
    
    {}

    
    QString name;
    
    bool name_changed : 1;
    

    static void addToCache( const TagType & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, TagType > idCache;
    
    static QHash<QString, TagType > nameCache;
    
};


// static members
QAtomicInt TagType::Private::cacheEnabled(0);
QMutex TagType::Private::cacheMutex;

QHash<qint64, TagType > TagType::Private::idCache;

QHash<QString, TagType > TagType::Private::nameCache;



void TagType::Private::addToCache( const TagType & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.name(), entry );
      
}


// constructor
TagType::TagType() : Entity(),
  d( new Private )
{
}

TagType::TagType(
  const QString &name
) :
  Entity(),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}

TagType::TagType(
  qint64 id, const QString &name
) :
  Entity( id ),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}
TagType::TagType( const TagType & other )
  : Entity( other ), d( other.d )
{
}

// destructor
TagType::~TagType() {}

// assignment operator
TagType& TagType::operator=( const TagType & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool TagType::operator==( const TagType & other ) const
{
  return id() == other.id();
}

// accessor methods
QString TagType::name() const
{
  return d->name;
}

void TagType::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}



// SQL table information
QString TagType::tableName()
{
  static const QString tableName = QStringLiteral( "TagTypeTable" );
  return tableName;
}

QStringList TagType::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << nameColumn()
  
  ;
  return columns;
}

QStringList TagType::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << nameFullColumnName()
  
  ;
  return columns;
}


QString TagType::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString TagType::idFullColumnName()
{
  static const QString column = QStringLiteral( "TagTypeTable.id" );
  return column;
}

QString TagType::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString TagType::nameFullColumnName()
{
  static const QString column = QStringLiteral( "TagTypeTable.name" );
  return column;
}



// count records
int TagType::count( const QString &column, const QVariant &value )
{
  return Entity::count<TagType>( column, value );
}

// check existence

bool TagType::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool TagType::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< TagType > TagType::extractResult( QSqlQuery & query )
{
  QVector<TagType> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( TagType(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            
    ) );
  }
  return rv;
}

// data retrieval
TagType TagType::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, TagType>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return TagType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return TagType();
  }
  if ( !qb.query().next() ) {
    return TagType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      TagType rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

TagType TagType::retrieveByName( const QString &name )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, TagType>::const_iterator it = Private::nameCache.constFind(name);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return TagType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nameColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with name"
      << name << "from table" << tableName()
      << qb.query().lastError().text();
    return TagType();
  }
  if ( !qb.query().next() ) {
    return TagType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    name;
      TagType rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

TagType TagType::retrieveByNameOrCreate( const QString &name)
{
  static QMutex lock;
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return TagType();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByName(name);
}


QVector<TagType> TagType::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<TagType>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<TagType>();
  }
  return extractResult( qb.query() );
}

QVector<TagType> TagType::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<TagType>();

  SelectQueryBuilder<TagType> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<TagType>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const TagType& entity )
{
  d << "[TagType: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "name = " <<
    
        entity.name()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool TagType::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool TagType::hasPendingChanges() const
{
  return false
  
    || d->name_changed
  ;
}

// update existing data
bool TagType::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool TagType::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<TagType>( column, value );
}


bool TagType::remove()
{
  invalidateCache();
  return Entity::remove<TagType>( idColumn(), id() );
}

bool TagType::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void TagType::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( name() );
        
  }
}

void TagType::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void TagType::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class Tag::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , parentId( 0 )
    
      , typeId( 1 )
    
      , gid()
    
      , gid_changed( false )
    
      , parentId_changed( false )
    
      , typeId_changed( false )
    
    {}

    
    qint64 parentId;
    
    qint64 typeId;
    
    QString gid;
    
    bool gid_changed : 1;
    
    bool parentId_changed : 1;
    
    bool typeId_changed : 1;
    

    static void addToCache( const Tag & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, Tag > idCache;
    
};


// static members
QAtomicInt Tag::Private::cacheEnabled(0);
QMutex Tag::Private::cacheMutex;

QHash<qint64, Tag > Tag::Private::idCache;



void Tag::Private::addToCache( const Tag & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
}


// constructor
Tag::Tag() : Entity(),
  d( new Private )
{
}

Tag::Tag(
  const QString &gid, qint64 parentId, qint64 typeId
) :
  Entity(),
  d( new Private )
{

  d->gid = gid;
  d->gid_changed = true;

  d->parentId = parentId;
  d->parentId_changed = true;

  d->typeId = typeId;
  d->typeId_changed = true;

}

Tag::Tag(
  qint64 id, const QString &gid, qint64 parentId, qint64 typeId
) :
  Entity( id ),
  d( new Private )
{

  d->gid = gid;
  d->gid_changed = true;

  d->parentId = parentId;
  d->parentId_changed = true;

  d->typeId = typeId;
  d->typeId_changed = true;

}
Tag::Tag( const Tag & other )
  : Entity( other ), d( other.d )
{
}

// destructor
Tag::~Tag() {}

// assignment operator
Tag& Tag::operator=( const Tag & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool Tag::operator==( const Tag & other ) const
{
  return id() == other.id();
}

// accessor methods
QString Tag::gid() const
{
  return d->gid;
}

void Tag::
setGid( const QString &gid )

{
  d->gid = gid;
  d->gid_changed = true;
}

qint64 Tag::parentId() const
{
  return d->parentId;
}

void Tag::
setParentId( qint64 parentId )

{
  d->parentId = parentId;
  d->parentId_changed = true;
}

qint64 Tag::typeId() const
{
  return d->typeId;
}

void Tag::
setTypeId( qint64 typeId )

{
  d->typeId = typeId;
  d->typeId_changed = true;
}



// SQL table information
QString Tag::tableName()
{
  static const QString tableName = QStringLiteral( "TagTable" );
  return tableName;
}

QStringList Tag::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << gidColumn()
  
    << parentIdColumn()
  
    << typeIdColumn()
  
  ;
  return columns;
}

QStringList Tag::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << gidFullColumnName()
  
    << parentIdFullColumnName()
  
    << typeIdFullColumnName()
  
  ;
  return columns;
}


QString Tag::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString Tag::idFullColumnName()
{
  static const QString column = QStringLiteral( "TagTable.id" );
  return column;
}

QString Tag::gidColumn()
{
  static const QString column = QStringLiteral( "gid" );
  return column;
}

QString Tag::gidFullColumnName()
{
  static const QString column = QStringLiteral( "TagTable.gid" );
  return column;
}

QString Tag::parentIdColumn()
{
  static const QString column = QStringLiteral( "parentId" );
  return column;
}

QString Tag::parentIdFullColumnName()
{
  static const QString column = QStringLiteral( "TagTable.parentId" );
  return column;
}

QString Tag::typeIdColumn()
{
  static const QString column = QStringLiteral( "typeId" );
  return column;
}

QString Tag::typeIdFullColumnName()
{
  static const QString column = QStringLiteral( "TagTable.typeId" );
  return column;
}



// count records
int Tag::count( const QString &column, const QVariant &value )
{
  return Entity::count<Tag>( column, value );
}

// check existence

bool Tag::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}



// result extraction
QVector< Tag > Tag::extractResult( QSqlQuery & query )
{
  QVector<Tag> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( Tag(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            ,
        (query.isNull(2)) ?
          qint64() :
          
          query.value( 2 ).value<qint64>()
            ,
        (query.isNull(3)) ?
          qint64() :
          
          query.value( 3 ).value<qint64>()
            
    ) );
  }
  return rv;
}

// data retrieval
Tag Tag::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, Tag>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return Tag();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return Tag();
  }
  if ( !qb.query().next() ) {
    return Tag();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      
    const qint64 value3 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const qint64 value4 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      Tag rv(
  
    value1,
    value2,
    value3,
    value4
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}



QVector<Tag> Tag::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Tag>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<Tag>();
  }
  return extractResult( qb.query() );
}

QVector<Tag> Tag::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Tag>();

  SelectQueryBuilder<Tag> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<Tag>();
  }
  return qb.result();
}

// data retrieval for referenced tables
Tag Tag::parent() const
{
  return Tag::retrieveById( parentId() );
}

void Tag::
    setParent
    ( const Tag &value )
{
  d->parentId = value.id();
  d->parentId_changed = true;
}
TagType Tag::tagType() const
{
  return TagType::retrieveById( typeId() );
}

void Tag::
    setTagType
    ( const TagType &value )
{
  d->typeId = value.id();
  d->typeId_changed = true;
}


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const Tag& entity )
{
  d << "[Tag: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "gid = " <<
    
        entity.gid()
      << ", "
    << "parentId = " <<
    
        entity.parentId()
      << ", "
    << "typeId = " <<
    
        entity.typeId()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool Tag::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->gid_changed )
      
      qb.setColumnValue( gidColumn(), this->gid() );
        
    if ( d->parentId_changed  && d->parentId > 0 )
      qb.setColumnValue( parentIdColumn(), this->parentId() );
    
    if ( d->typeId_changed  && d->typeId > 0 )
      qb.setColumnValue( typeIdColumn(), this->typeId() );
    

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool Tag::hasPendingChanges() const
{
  return false
  
    || d->gid_changed
  
    || d->parentId_changed
  
    || d->typeId_changed
  ;
}

// update existing data
bool Tag::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->gid_changed ) {
      
      qb.setColumnValue( gidColumn(), this->gid() );
        
    }
  
    if ( d->parentId_changed ) {
      
      if ( d->parentId <= 0 )
        qb.setColumnValue( parentIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( parentIdColumn(), this->parentId() );
        
    }
  
    if ( d->typeId_changed ) {
      
      if ( d->typeId <= 0 )
        qb.setColumnValue( typeIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( typeIdColumn(), this->typeId() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool Tag::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<Tag>( column, value );
}


bool Tag::remove()
{
  invalidateCache();
  return Entity::remove<Tag>( idColumn(), id() );
}

bool Tag::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void Tag::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
  }
}

void Tag::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
  }
}

void Tag::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class TagAttribute::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , tagId( 0 )
    
      , type()
    
      , value()
    
      , tagId_changed( false )
    
      , type_changed( false )
    
      , value_changed( false )
    
    {}

    
    qint64 tagId;
    
    QByteArray type;
    
    QByteArray value;
    
    bool tagId_changed : 1;
    
    bool type_changed : 1;
    
    bool value_changed : 1;
    

    static void addToCache( const TagAttribute & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, TagAttribute > idCache;
    
};


// static members
QAtomicInt TagAttribute::Private::cacheEnabled(0);
QMutex TagAttribute::Private::cacheMutex;

QHash<qint64, TagAttribute > TagAttribute::Private::idCache;



void TagAttribute::Private::addToCache( const TagAttribute & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
}


// constructor
TagAttribute::TagAttribute() : Entity(),
  d( new Private )
{
}

TagAttribute::TagAttribute(
  qint64 tagId, const QByteArray &type, const QByteArray &value
) :
  Entity(),
  d( new Private )
{

  d->tagId = tagId;
  d->tagId_changed = true;

  d->type = type;
  d->type_changed = true;

  d->value = value;
  d->value_changed = true;

}

TagAttribute::TagAttribute(
  qint64 id, qint64 tagId, const QByteArray &type, const QByteArray &value
) :
  Entity( id ),
  d( new Private )
{

  d->tagId = tagId;
  d->tagId_changed = true;

  d->type = type;
  d->type_changed = true;

  d->value = value;
  d->value_changed = true;

}
TagAttribute::TagAttribute( const TagAttribute & other )
  : Entity( other ), d( other.d )
{
}

// destructor
TagAttribute::~TagAttribute() {}

// assignment operator
TagAttribute& TagAttribute::operator=( const TagAttribute & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool TagAttribute::operator==( const TagAttribute & other ) const
{
  return id() == other.id();
}

// accessor methods
qint64 TagAttribute::tagId() const
{
  return d->tagId;
}

void TagAttribute::
setTagId( qint64 tagId )

{
  d->tagId = tagId;
  d->tagId_changed = true;
}

QByteArray TagAttribute::type() const
{
  return d->type;
}

void TagAttribute::
setType( const QByteArray &type )

{
  d->type = type;
  d->type_changed = true;
}

QByteArray TagAttribute::value() const
{
  return d->value;
}

void TagAttribute::
setValue( const QByteArray &value )

{
  d->value = value;
  d->value_changed = true;
}



// SQL table information
QString TagAttribute::tableName()
{
  static const QString tableName = QStringLiteral( "TagAttributeTable" );
  return tableName;
}

QStringList TagAttribute::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << tagIdColumn()
  
    << typeColumn()
  
    << valueColumn()
  
  ;
  return columns;
}

QStringList TagAttribute::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << tagIdFullColumnName()
  
    << typeFullColumnName()
  
    << valueFullColumnName()
  
  ;
  return columns;
}


QString TagAttribute::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString TagAttribute::idFullColumnName()
{
  static const QString column = QStringLiteral( "TagAttributeTable.id" );
  return column;
}

QString TagAttribute::tagIdColumn()
{
  static const QString column = QStringLiteral( "tagId" );
  return column;
}

QString TagAttribute::tagIdFullColumnName()
{
  static const QString column = QStringLiteral( "TagAttributeTable.tagId" );
  return column;
}

QString TagAttribute::typeColumn()
{
  static const QString column = QStringLiteral( "type" );
  return column;
}

QString TagAttribute::typeFullColumnName()
{
  static const QString column = QStringLiteral( "TagAttributeTable.type" );
  return column;
}

QString TagAttribute::valueColumn()
{
  static const QString column = QStringLiteral( "value" );
  return column;
}

QString TagAttribute::valueFullColumnName()
{
  static const QString column = QStringLiteral( "TagAttributeTable.value" );
  return column;
}



// count records
int TagAttribute::count( const QString &column, const QVariant &value )
{
  return Entity::count<TagAttribute>( column, value );
}

// check existence

bool TagAttribute::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}



// result extraction
QVector< TagAttribute > TagAttribute::extractResult( QSqlQuery & query )
{
  QVector<TagAttribute> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( TagAttribute(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          qint64() :
          
          query.value( 1 ).value<qint64>()
            ,
        (query.isNull(2)) ?
          QByteArray() :
          
          query.value( 2 ).value<QByteArray>()
            ,
        (query.isNull(3)) ?
          QByteArray() :
          
          query.value( 3 ).value<QByteArray>()
            
    ) );
  }
  return rv;
}

// data retrieval
TagAttribute TagAttribute::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, TagAttribute>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return TagAttribute();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return TagAttribute();
  }
  if ( !qb.query().next() ) {
    return TagAttribute();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const qint64 value2 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QByteArray value3 =
    
        (qb.query().isNull(valueIndex)) ?
        QByteArray() :
        
          qb.query().value( valueIndex ).value<QByteArray>()
          
        ; ++valueIndex;
      
    const QByteArray value4 =
    
        (qb.query().isNull(valueIndex)) ?
        QByteArray() :
        
          qb.query().value( valueIndex ).value<QByteArray>()
          
        ; ++valueIndex;
      TagAttribute rv(
  
    value1,
    value2,
    value3,
    value4
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}



QVector<TagAttribute> TagAttribute::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<TagAttribute>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<TagAttribute>();
  }
  return extractResult( qb.query() );
}

QVector<TagAttribute> TagAttribute::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<TagAttribute>();

  SelectQueryBuilder<TagAttribute> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<TagAttribute>();
  }
  return qb.result();
}

// data retrieval for referenced tables
Tag TagAttribute::tag() const
{
  return Tag::retrieveById( tagId() );
}

void TagAttribute::
    setTag
    ( const Tag &value )
{
  d->tagId = value.id();
  d->tagId_changed = true;
}


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const TagAttribute& entity )
{
  d << "[TagAttribute: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "tagId = " <<
    
        entity.tagId()
      << ", "
    << "type = " <<
    
        entity.type()
      << ", "
    << "value = " <<
    
        entity.value()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool TagAttribute::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->tagId_changed  && d->tagId > 0 )
      qb.setColumnValue( tagIdColumn(), this->tagId() );
    
    if ( d->type_changed )
      
      qb.setColumnValue( typeColumn(), this->type() );
        
    if ( d->value_changed )
      
      qb.setColumnValue( valueColumn(), this->value() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool TagAttribute::hasPendingChanges() const
{
  return false
  
    || d->tagId_changed
  
    || d->type_changed
  
    || d->value_changed
  ;
}

// update existing data
bool TagAttribute::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->tagId_changed ) {
      
      if ( d->tagId <= 0 )
        qb.setColumnValue( tagIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( tagIdColumn(), this->tagId() );
        
    }
  
    if ( d->type_changed ) {
      
      qb.setColumnValue( typeColumn(), this->type() );
        
    }
  
    if ( d->value_changed ) {
      
      qb.setColumnValue( valueColumn(), this->value() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool TagAttribute::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<TagAttribute>( column, value );
}


bool TagAttribute::remove()
{
  invalidateCache();
  return Entity::remove<TagAttribute>( idColumn(), id() );
}

bool TagAttribute::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void TagAttribute::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
  }
}

void TagAttribute::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
  }
}

void TagAttribute::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class TagRemoteIdResourceRelation::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , tagId( 0 )
    
      , resourceId( 0 )
    
      , remoteId()
    
      , tagId_changed( false )
    
      , resourceId_changed( false )
    
      , remoteId_changed( false )
    
    {}

    
    qint64 tagId;
    
    qint64 resourceId;
    
    QString remoteId;
    
    bool tagId_changed : 1;
    
    bool resourceId_changed : 1;
    
    bool remoteId_changed : 1;
    

    static void addToCache( const TagRemoteIdResourceRelation & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
};


// static members
QAtomicInt TagRemoteIdResourceRelation::Private::cacheEnabled(0);
QMutex TagRemoteIdResourceRelation::Private::cacheMutex;



void TagRemoteIdResourceRelation::Private::addToCache( const TagRemoteIdResourceRelation & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
}


// constructor
TagRemoteIdResourceRelation::TagRemoteIdResourceRelation() : Entity(),
  d( new Private )
{
}

TagRemoteIdResourceRelation::TagRemoteIdResourceRelation(
  qint64 tagId, qint64 resourceId, const QString &remoteId
) :
  Entity(),
  d( new Private )
{

  d->tagId = tagId;
  d->tagId_changed = true;

  d->resourceId = resourceId;
  d->resourceId_changed = true;

  d->remoteId = remoteId;
  d->remoteId_changed = true;

}

TagRemoteIdResourceRelation::TagRemoteIdResourceRelation( const TagRemoteIdResourceRelation & other )
  : Entity( other ), d( other.d )
{
}

// destructor
TagRemoteIdResourceRelation::~TagRemoteIdResourceRelation() {}

// assignment operator
TagRemoteIdResourceRelation& TagRemoteIdResourceRelation::operator=( const TagRemoteIdResourceRelation & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool TagRemoteIdResourceRelation::operator==( const TagRemoteIdResourceRelation & other ) const
{
  return id() == other.id();
}

// accessor methods
qint64 TagRemoteIdResourceRelation::tagId() const
{
  return d->tagId;
}

void TagRemoteIdResourceRelation::
setTagId( qint64 tagId )

{
  d->tagId = tagId;
  d->tagId_changed = true;
}

qint64 TagRemoteIdResourceRelation::resourceId() const
{
  return d->resourceId;
}

void TagRemoteIdResourceRelation::
setResourceId( qint64 resourceId )

{
  d->resourceId = resourceId;
  d->resourceId_changed = true;
}

QString TagRemoteIdResourceRelation::remoteId() const
{
  return d->remoteId;
}

void TagRemoteIdResourceRelation::
setRemoteId( const QString &remoteId )

{
  d->remoteId = remoteId;
  d->remoteId_changed = true;
}



// SQL table information
QString TagRemoteIdResourceRelation::tableName()
{
  static const QString tableName = QStringLiteral( "TagRemoteIdResourceRelationTable" );
  return tableName;
}

QStringList TagRemoteIdResourceRelation::columnNames()
{
  static const QStringList columns = QStringList()
  
    << tagIdColumn()
  
    << resourceIdColumn()
  
    << remoteIdColumn()
  
  ;
  return columns;
}

QStringList TagRemoteIdResourceRelation::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << tagIdFullColumnName()
  
    << resourceIdFullColumnName()
  
    << remoteIdFullColumnName()
  
  ;
  return columns;
}


QString TagRemoteIdResourceRelation::tagIdColumn()
{
  static const QString column = QStringLiteral( "tagId" );
  return column;
}

QString TagRemoteIdResourceRelation::tagIdFullColumnName()
{
  static const QString column = QStringLiteral( "TagRemoteIdResourceRelationTable.tagId" );
  return column;
}

QString TagRemoteIdResourceRelation::resourceIdColumn()
{
  static const QString column = QStringLiteral( "resourceId" );
  return column;
}

QString TagRemoteIdResourceRelation::resourceIdFullColumnName()
{
  static const QString column = QStringLiteral( "TagRemoteIdResourceRelationTable.resourceId" );
  return column;
}

QString TagRemoteIdResourceRelation::remoteIdColumn()
{
  static const QString column = QStringLiteral( "remoteId" );
  return column;
}

QString TagRemoteIdResourceRelation::remoteIdFullColumnName()
{
  static const QString column = QStringLiteral( "TagRemoteIdResourceRelationTable.remoteId" );
  return column;
}



// count records
int TagRemoteIdResourceRelation::count( const QString &column, const QVariant &value )
{
  return Entity::count<TagRemoteIdResourceRelation>( column, value );
}

// check existence



// result extraction
QVector< TagRemoteIdResourceRelation > TagRemoteIdResourceRelation::extractResult( QSqlQuery & query )
{
  QVector<TagRemoteIdResourceRelation> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( TagRemoteIdResourceRelation(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          qint64() :
          
          query.value( 1 ).value<qint64>()
            ,
        (query.isNull(2)) ?
          QString() :
          
          Utils::variantToString( query.value( 2 ) )
            
    ) );
  }
  return rv;
}

// data retrieval


QVector<TagRemoteIdResourceRelation> TagRemoteIdResourceRelation::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<TagRemoteIdResourceRelation>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<TagRemoteIdResourceRelation>();
  }
  return extractResult( qb.query() );
}

QVector<TagRemoteIdResourceRelation> TagRemoteIdResourceRelation::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<TagRemoteIdResourceRelation>();

  SelectQueryBuilder<TagRemoteIdResourceRelation> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<TagRemoteIdResourceRelation>();
  }
  return qb.result();
}

// data retrieval for referenced tables
Tag TagRemoteIdResourceRelation::tag() const
{
  return Tag::retrieveById( tagId() );
}

void TagRemoteIdResourceRelation::
    setTag
    ( const Tag &value )
{
  d->tagId = value.id();
  d->tagId_changed = true;
}
Resource TagRemoteIdResourceRelation::resource() const
{
  return Resource::retrieveById( resourceId() );
}

void TagRemoteIdResourceRelation::
    setResource
    ( const Resource &value )
{
  d->resourceId = value.id();
  d->resourceId_changed = true;
}


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const TagRemoteIdResourceRelation& entity )
{
  d << "[TagRemoteIdResourceRelation: "
  
    << "tagId = " <<
    
        entity.tagId()
      << ", "
    << "resourceId = " <<
    
        entity.resourceId()
      << ", "
    << "remoteId = " <<
    
        entity.remoteId()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool TagRemoteIdResourceRelation::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
  qb.setIdentificationColumn(QLatin1String(""));
  
    if ( d->tagId_changed  && d->tagId > 0 )
      qb.setColumnValue( tagIdColumn(), this->tagId() );
    
    if ( d->resourceId_changed  && d->resourceId > 0 )
      qb.setColumnValue( resourceIdColumn(), this->resourceId() );
    
    if ( d->remoteId_changed )
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool TagRemoteIdResourceRelation::hasPendingChanges() const
{
  return false
  
    || d->tagId_changed
  
    || d->resourceId_changed
  
    || d->remoteId_changed
  ;
}

// update existing data
bool TagRemoteIdResourceRelation::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->tagId_changed ) {
      
      if ( d->tagId <= 0 )
        qb.setColumnValue( tagIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( tagIdColumn(), this->tagId() );
        
    }
  
    if ( d->resourceId_changed ) {
      
      if ( d->resourceId <= 0 )
        qb.setColumnValue( resourceIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( resourceIdColumn(), this->resourceId() );
        
    }
  
    if ( d->remoteId_changed ) {
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        
    }
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool TagRemoteIdResourceRelation::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<TagRemoteIdResourceRelation>( column, value );
}



// cache stuff
void TagRemoteIdResourceRelation::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
  }
}

void TagRemoteIdResourceRelation::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
  }
}

void TagRemoteIdResourceRelation::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class RelationType::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , name()
    
      , name_changed( false )
    
    {}

    
    QString name;
    
    bool name_changed : 1;
    

    static void addToCache( const RelationType & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
    static QHash<qint64, RelationType > idCache;
    
    static QHash<QString, RelationType > nameCache;
    
};


// static members
QAtomicInt RelationType::Private::cacheEnabled(0);
QMutex RelationType::Private::cacheMutex;

QHash<qint64, RelationType > RelationType::Private::idCache;

QHash<QString, RelationType > RelationType::Private::nameCache;



void RelationType::Private::addToCache( const RelationType & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
  idCache.insert( entry.id(), entry );
  
  nameCache.insert( entry.name(), entry );
      
}


// constructor
RelationType::RelationType() : Entity(),
  d( new Private )
{
}

RelationType::RelationType(
  const QString &name
) :
  Entity(),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}

RelationType::RelationType(
  qint64 id, const QString &name
) :
  Entity( id ),
  d( new Private )
{

  d->name = name;
  d->name_changed = true;

}
RelationType::RelationType( const RelationType & other )
  : Entity( other ), d( other.d )
{
}

// destructor
RelationType::~RelationType() {}

// assignment operator
RelationType& RelationType::operator=( const RelationType & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool RelationType::operator==( const RelationType & other ) const
{
  return id() == other.id();
}

// accessor methods
QString RelationType::name() const
{
  return d->name;
}

void RelationType::
setName( const QString &name )

{
  d->name = name;
  d->name_changed = true;
}



// SQL table information
QString RelationType::tableName()
{
  static const QString tableName = QStringLiteral( "RelationTypeTable" );
  return tableName;
}

QStringList RelationType::columnNames()
{
  static const QStringList columns = QStringList()
  
    << idColumn()
  
    << nameColumn()
  
  ;
  return columns;
}

QStringList RelationType::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << idFullColumnName()
  
    << nameFullColumnName()
  
  ;
  return columns;
}


QString RelationType::idColumn()
{
  static const QString column = QStringLiteral( "id" );
  return column;
}

QString RelationType::idFullColumnName()
{
  static const QString column = QStringLiteral( "RelationTypeTable.id" );
  return column;
}

QString RelationType::nameColumn()
{
  static const QString column = QStringLiteral( "name" );
  return column;
}

QString RelationType::nameFullColumnName()
{
  static const QString column = QStringLiteral( "RelationTypeTable.name" );
  return column;
}



// count records
int RelationType::count( const QString &column, const QVariant &value )
{
  return Entity::count<RelationType>( column, value );
}

// check existence

bool RelationType::exists( qint64 id )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::idCache.contains( id ) ) {
      return true;
    }
  }
  return count( idColumn(), id ) > 0;
}

bool RelationType::exists( const QString &name )
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    if ( Private::nameCache.contains( name ) ) {
      return true;
    }
  }
  return count( nameColumn(), name ) > 0;
}



// result extraction
QVector< RelationType > RelationType::extractResult( QSqlQuery & query )
{
  QVector<RelationType> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( RelationType(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          QString() :
          
          Utils::variantToString( query.value( 1 ) )
            
    ) );
  }
  return rv;
}

// data retrieval
RelationType RelationType::retrieveById( qint64 id )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<qint64, RelationType>::const_iterator it = Private::idCache.constFind(id);
    if ( it != Private::idCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return RelationType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), idColumn());
  qb.addColumns( columns );
  qb.addValueCondition( idColumn(), Query::Equals, id );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with id"
      << id << "from table" << tableName()
      << qb.query().lastError().text();
    return RelationType();
  }
  if ( !qb.query().next() ) {
    return RelationType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    id;
      
    const QString value2 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
          Utils::variantToString( qb.query().value( valueIndex ) )
          
        ; ++valueIndex;
      RelationType rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

RelationType RelationType::retrieveByName( const QString &name )
{
  
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    QHash<QString, RelationType>::const_iterator it = Private::nameCache.constFind(name);
    if ( it != Private::nameCache.constEnd() ) {
      return it.value();
    }
  }
  
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return RelationType();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  static const QStringList columns = removeEntry(columnNames(), nameColumn());
  qb.addColumns( columns );
  qb.addValueCondition( nameColumn(), Query::Equals, name );
  
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of record with name"
      << name << "from table" << tableName()
      << qb.query().lastError().text();
    return RelationType();
  }
  if ( !qb.query().next() ) {
    return RelationType();
  }

  
  int valueIndex = 0;
  
    const qint64 value1 =
    
        (qb.query().isNull(valueIndex)) ?
        qint64() :
        
          qb.query().value( valueIndex ).value<qint64>()
          
        ; ++valueIndex;
      
    const QString value2 =
    name;
      RelationType rv(
  
    value1,
    value2
  );
  if ( Private::cacheEnabled ) {
    Private::addToCache( rv );
  }
  return rv;

}

RelationType RelationType::retrieveByNameOrCreate( const QString &name)
{
  static QMutex lock;
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  if (lock.tryLock()) {
    rv.setName(name);
    if (!rv.insert()) {
      lock.unlock();
      return RelationType();
    }

    if (Private::cacheEnabled) {
      Private::addToCache(rv);
    }
    lock.unlock();
    return rv;
  }

  lock.lock();
  lock.unlock();
  return retrieveByName(name);
}


QVector<RelationType> RelationType::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<RelationType>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<RelationType>();
  }
  return extractResult( qb.query() );
}

QVector<RelationType> RelationType::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<RelationType>();

  SelectQueryBuilder<RelationType> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<RelationType>();
  }
  return qb.result();
}

// data retrieval for referenced tables


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const RelationType& entity )
{
  d << "[RelationType: "
  
    << "id = " <<
    
        entity.id()
      << ", "
    << "name = " <<
    
        entity.name()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool RelationType::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
    if ( d->name_changed )
      
      qb.setColumnValue( nameColumn(), this->name() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool RelationType::hasPendingChanges() const
{
  return false
  
    || d->name_changed
  ;
}

// update existing data
bool RelationType::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->name_changed ) {
      
      qb.setColumnValue( nameColumn(), this->name() );
        
    }
  
  qb.addValueCondition( idColumn(), Query::Equals, id() );
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool RelationType::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<RelationType>( column, value );
}


bool RelationType::remove()
{
  invalidateCache();
  return Entity::remove<RelationType>( idColumn(), id() );
}

bool RelationType::remove( qint64 id )
{
  return remove( idColumn(), id );
}


// cache stuff
void RelationType::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.remove( id() );
    
    Private::nameCache.remove( name() );
        
  }
}

void RelationType::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
    Private::idCache.clear();
    
    Private::nameCache.clear();
    
  }
}

void RelationType::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// private class
class Relation::Private : public QSharedData
{
  public:
    Private() : QSharedData()
    
      , leftId( 0 )
    
      , rightId( 0 )
    
      , typeId( 1 )
    
      , remoteId()
    
      , leftId_changed( false )
    
      , rightId_changed( false )
    
      , typeId_changed( false )
    
      , remoteId_changed( false )
    
    {}

    
    qint64 leftId;
    
    qint64 rightId;
    
    qint64 typeId;
    
    QString remoteId;
    
    bool leftId_changed : 1;
    
    bool rightId_changed : 1;
    
    bool typeId_changed : 1;
    
    bool remoteId_changed : 1;
    

    static void addToCache( const Relation & entry );

    // cache
    static QAtomicInt cacheEnabled;
    static QMutex cacheMutex;
    
};


// static members
QAtomicInt Relation::Private::cacheEnabled(0);
QMutex Relation::Private::cacheMutex;



void Relation::Private::addToCache( const Relation & entry )
{
  Q_ASSERT( cacheEnabled );
  Q_UNUSED( entry ); 
  QMutexLocker lock(&cacheMutex);
  
}


// constructor
Relation::Relation() : Entity(),
  d( new Private )
{
}

Relation::Relation(
  qint64 leftId, qint64 rightId, qint64 typeId, const QString &remoteId
) :
  Entity(),
  d( new Private )
{

  d->leftId = leftId;
  d->leftId_changed = true;

  d->rightId = rightId;
  d->rightId_changed = true;

  d->typeId = typeId;
  d->typeId_changed = true;

  d->remoteId = remoteId;
  d->remoteId_changed = true;

}

Relation::Relation( const Relation & other )
  : Entity( other ), d( other.d )
{
}

// destructor
Relation::~Relation() {}

// assignment operator
Relation& Relation::operator=( const Relation & other )
{
  if ( this != &other ) {
    d = other.d;
    setId( other.id() );
  }
  return *this;
}

// comparisson operator
bool Relation::operator==( const Relation & other ) const
{
  return id() == other.id();
}

// accessor methods
qint64 Relation::leftId() const
{
  return d->leftId;
}

void Relation::
setLeftId( qint64 leftId )

{
  d->leftId = leftId;
  d->leftId_changed = true;
}

qint64 Relation::rightId() const
{
  return d->rightId;
}

void Relation::
setRightId( qint64 rightId )

{
  d->rightId = rightId;
  d->rightId_changed = true;
}

qint64 Relation::typeId() const
{
  return d->typeId;
}

void Relation::
setTypeId( qint64 typeId )

{
  d->typeId = typeId;
  d->typeId_changed = true;
}

QString Relation::remoteId() const
{
  return d->remoteId;
}

void Relation::
setRemoteId( const QString &remoteId )

{
  d->remoteId = remoteId;
  d->remoteId_changed = true;
}



// SQL table information
QString Relation::tableName()
{
  static const QString tableName = QStringLiteral( "RelationTable" );
  return tableName;
}

QStringList Relation::columnNames()
{
  static const QStringList columns = QStringList()
  
    << leftIdColumn()
  
    << rightIdColumn()
  
    << typeIdColumn()
  
    << remoteIdColumn()
  
  ;
  return columns;
}

QStringList Relation::fullColumnNames()
{
  static const QStringList columns = QStringList()
  
    << leftIdFullColumnName()
  
    << rightIdFullColumnName()
  
    << typeIdFullColumnName()
  
    << remoteIdFullColumnName()
  
  ;
  return columns;
}


QString Relation::leftIdColumn()
{
  static const QString column = QStringLiteral( "leftId" );
  return column;
}

QString Relation::leftIdFullColumnName()
{
  static const QString column = QStringLiteral( "RelationTable.leftId" );
  return column;
}

QString Relation::rightIdColumn()
{
  static const QString column = QStringLiteral( "rightId" );
  return column;
}

QString Relation::rightIdFullColumnName()
{
  static const QString column = QStringLiteral( "RelationTable.rightId" );
  return column;
}

QString Relation::typeIdColumn()
{
  static const QString column = QStringLiteral( "typeId" );
  return column;
}

QString Relation::typeIdFullColumnName()
{
  static const QString column = QStringLiteral( "RelationTable.typeId" );
  return column;
}

QString Relation::remoteIdColumn()
{
  static const QString column = QStringLiteral( "remoteId" );
  return column;
}

QString Relation::remoteIdFullColumnName()
{
  static const QString column = QStringLiteral( "RelationTable.remoteId" );
  return column;
}



// count records
int Relation::count( const QString &column, const QVariant &value )
{
  return Entity::count<Relation>( column, value );
}

// check existence



// result extraction
QVector< Relation > Relation::extractResult( QSqlQuery & query )
{
  QVector<Relation> rv;
  if (query.driver()->hasFeature(QSqlDriver::QuerySize)) {
    rv.reserve(query.size());
  }
  while ( query.next() ) {
    rv.append( Relation(
      
        (query.isNull(0)) ?
          qint64() :
          
          query.value( 0 ).value<qint64>()
            ,
        (query.isNull(1)) ?
          qint64() :
          
          query.value( 1 ).value<qint64>()
            ,
        (query.isNull(2)) ?
          qint64() :
          
          query.value( 2 ).value<qint64>()
            ,
        (query.isNull(3)) ?
          QString() :
          
          Utils::variantToString( query.value( 3 ) )
            
    ) );
  }
  return rv;
}

// data retrieval


QVector<Relation> Relation::retrieveAll()
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Relation>();

  QueryBuilder qb( tableName(), QueryBuilder::Select );
  qb.addColumns( columnNames() );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName()
      << qb.query().lastError().text() << qb.query().lastQuery();
    return QVector<Relation>();
  }
  return extractResult( qb.query() );
}

QVector<Relation> Relation::retrieveFiltered( const QString &key, const QVariant &value )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return QVector<Relation>();

  SelectQueryBuilder<Relation> qb;
  if ( value.isNull() )
    qb.addValueCondition( key, Query::Is, QVariant() );
  else
    qb.addValueCondition( key, Query::Equals, value );
  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName()
      << "filtered by" << key << "=" << value
      << qb.query().lastError().text();
    return QVector<Relation>();
  }
  return qb.result();
}

// data retrieval for referenced tables
PimItem Relation::left() const
{
  return PimItem::retrieveById( leftId() );
}

void Relation::
    setLeft
    ( const PimItem &value )
{
  d->leftId = value.id();
  d->leftId_changed = true;
}
PimItem Relation::right() const
{
  return PimItem::retrieveById( rightId() );
}

void Relation::
    setRight
    ( const PimItem &value )
{
  d->rightId = value.id();
  d->rightId_changed = true;
}
RelationType Relation::relationType() const
{
  return RelationType::retrieveById( typeId() );
}

void Relation::
    setRelationType
    ( const RelationType &value )
{
  d->typeId = value.id();
  d->typeId_changed = true;
}


// data retrieval for inverse referenced tables


#ifndef QT_NO_DEBUG_STREAM
// debug stream operator
QDebug & operator<<( QDebug& d, const Relation& entity )
{
  d << "[Relation: "
  
    << "leftId = " <<
    
        entity.leftId()
      << ", "
    << "rightId = " <<
    
        entity.rightId()
      << ", "
    << "typeId = " <<
    
        entity.typeId()
      << ", "
    << "remoteId = " <<
    
        entity.remoteId()
      
    << "]";
  return d;
}
#endif

// inserting new data
bool Relation::insert( qint64* insertId )
{
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Insert );
  
  qb.setIdentificationColumn(QLatin1String(""));
  
    if ( d->leftId_changed  && d->leftId > 0 )
      qb.setColumnValue( leftIdColumn(), this->leftId() );
    
    if ( d->rightId_changed  && d->rightId > 0 )
      qb.setColumnValue( rightIdColumn(), this->rightId() );
    
    if ( d->typeId_changed  && d->typeId > 0 )
      qb.setColumnValue( typeIdColumn(), this->typeId() );
    
    if ( d->remoteId_changed )
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName()
      << qb.query().lastError().text();
    return false;
  }

  setId( qb.insertId() );
  if ( insertId )
    *insertId = id();
  return true;
}

bool Relation::hasPendingChanges() const
{
  return false
  
    || d->leftId_changed
  
    || d->rightId_changed
  
    || d->typeId_changed
  
    || d->remoteId_changed
  ;
}

// update existing data
bool Relation::update()
{
  invalidateCache();
  QSqlDatabase db = DataStore::self()->database();
  if ( !db.isOpen() )
    return false;

  QueryBuilder qb( tableName(), QueryBuilder::Update );

  
    if ( d->leftId_changed ) {
      
      if ( d->leftId <= 0 )
        qb.setColumnValue( leftIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( leftIdColumn(), this->leftId() );
        
    }
  
    if ( d->rightId_changed ) {
      
      if ( d->rightId <= 0 )
        qb.setColumnValue( rightIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( rightIdColumn(), this->rightId() );
        
    }
  
    if ( d->typeId_changed ) {
      
      if ( d->typeId <= 0 )
        qb.setColumnValue( typeIdColumn(), QVariant() );
      else
      
      qb.setColumnValue( typeIdColumn(), this->typeId() );
        
    }
  
    if ( d->remoteId_changed ) {
      
      qb.setColumnValue( remoteIdColumn(), this->remoteId() );
        
    }
  

  if ( !qb.exec() ) {
    qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id()
             << " in table" << tableName() << qb.query().lastError().text();
    return false;
  }
  return true;
}

// delete records
bool Relation::remove( const QString &column, const QVariant &value )
{
  invalidateCompleteCache();
  return Entity::remove<Relation>( column, value );
}



// cache stuff
void Relation::invalidateCache() const
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
  }
}

void Relation::invalidateCompleteCache()
{
  if ( Private::cacheEnabled ) {
    QMutexLocker lock(&Private::cacheMutex);
    
  }
}

void Relation::enableCache( bool enable )
{
  Private::cacheEnabled = enable;
}



// SQL table information
QString PimItemFlagRelation::tableName()
{
  static const QString table = QStringLiteral( "PimItemFlagRelation" );
  return table;
}

QString PimItemFlagRelation::leftColumn()
{
  static const QString column = QStringLiteral( "PimItem_id" );
  return column;
}

QString PimItemFlagRelation::leftFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemFlagRelation.PimItem_id" );
  return column;
}

QString PimItemFlagRelation::rightColumn()
{
  static const QString column = QStringLiteral( "Flag_id" );
  return column;
}

QString PimItemFlagRelation::rightFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemFlagRelation.Flag_id" );
  return column;
}


// SQL table information
QString PimItemTagRelation::tableName()
{
  static const QString table = QStringLiteral( "PimItemTagRelation" );
  return table;
}

QString PimItemTagRelation::leftColumn()
{
  static const QString column = QStringLiteral( "PimItem_id" );
  return column;
}

QString PimItemTagRelation::leftFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTagRelation.PimItem_id" );
  return column;
}

QString PimItemTagRelation::rightColumn()
{
  static const QString column = QStringLiteral( "Tag_id" );
  return column;
}

QString PimItemTagRelation::rightFullColumnName()
{
  static const QString column = QStringLiteral( "PimItemTagRelation.Tag_id" );
  return column;
}


// SQL table information
QString CollectionMimeTypeRelation::tableName()
{
  static const QString table = QStringLiteral( "CollectionMimeTypeRelation" );
  return table;
}

QString CollectionMimeTypeRelation::leftColumn()
{
  static const QString column = QStringLiteral( "Collection_id" );
  return column;
}

QString CollectionMimeTypeRelation::leftFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionMimeTypeRelation.Collection_id" );
  return column;
}

QString CollectionMimeTypeRelation::rightColumn()
{
  static const QString column = QStringLiteral( "MimeType_id" );
  return column;
}

QString CollectionMimeTypeRelation::rightFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionMimeTypeRelation.MimeType_id" );
  return column;
}


// SQL table information
QString CollectionPimItemRelation::tableName()
{
  static const QString table = QStringLiteral( "CollectionPimItemRelation" );
  return table;
}

QString CollectionPimItemRelation::leftColumn()
{
  static const QString column = QStringLiteral( "Collection_id" );
  return column;
}

QString CollectionPimItemRelation::leftFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionPimItemRelation.Collection_id" );
  return column;
}

QString CollectionPimItemRelation::rightColumn()
{
  static const QString column = QStringLiteral( "PimItem_id" );
  return column;
}

QString CollectionPimItemRelation::rightFullColumnName()
{
  static const QString column = QStringLiteral( "CollectionPimItemRelation.PimItem_id" );
  return column;
}


QVector<QString> Akonadi::Server::allDatabaseTables()
{
  static const QVector<QString> allTables = QVector<QString>()
  
    << QStringLiteral( "SchemaVersionTable" )
  
    << QStringLiteral( "ResourceTable" )
  
    << QStringLiteral( "CollectionTable" )
  
    << QStringLiteral( "MimeTypeTable" )
  
    << QStringLiteral( "PimItemTable" )
  
    << QStringLiteral( "FlagTable" )
  
    << QStringLiteral( "PartTypeTable" )
  
    << QStringLiteral( "PartTable" )
  
    << QStringLiteral( "CollectionAttributeTable" )
  
    << QStringLiteral( "TagTypeTable" )
  
    << QStringLiteral( "TagTable" )
  
    << QStringLiteral( "TagAttributeTable" )
  
    << QStringLiteral( "TagRemoteIdResourceRelationTable" )
  
    << QStringLiteral( "RelationTypeTable" )
  
    << QStringLiteral( "RelationTable" )
  
    << QStringLiteral( "PimItemFlagRelation" )
  
    << QStringLiteral( "PimItemTagRelation" )
  
    << QStringLiteral( "CollectionMimeTypeRelation" )
  
    << QStringLiteral( "CollectionPimItemRelation" )
  
  ;
  return allTables;
}

