
/*
 * 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>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDriver>
#include <QVariant>
#include <QHash>
#include <QMutex>
#include <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() // NOLINT(readability-redundant-member-init)
    
      , version_changed(false)
    
      , generation_changed(false)
    
    {}

    
    int version = 0;
    
    int generation = 0;
    
    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()
    : d(new Private)
{
}

SchemaVersion::SchemaVersion(
  int version, int generation
) : 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;
}

// comparison 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 = {
        versionColumn()
        ,generationColumn()
        
    };
    return columns;
}

QStringList SchemaVersion::fullColumnNames()
{
    static const QStringList columns = {
        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>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , 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()
    : d(new Private)
{
}

Resource::Resource(
  const QString &name, bool isVirtual
) : 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;
}

// comparison 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 = {
        idColumn()
        ,nameColumn()
        ,isVirtualColumn()
        
    };
    return columns;
}

QStringList Resource::fullColumnNames()
{
    static const QStringList columns = {
        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>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
Resource Resource::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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(false) :
        
            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);
        auto 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(false) :
        
            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;
  QMutexLocker locker(&lock);
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

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

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , enabled(true)
    
      , cachePolicyInherit(true)
    
      , cachePolicySyncOnDemand(false)
    
      , isVirtual(false)
    
      , 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)
    
      , 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 = 0;
    
    qint64 resourceId = 0;
    
    QString remoteId;
    
    QString remoteRevision;
    
    QString name;
    
    QString cachePolicyLocalParts;
    
    QString queryString;
    
    QString queryAttributes;
    
    QString queryCollections;
    
    int cachePolicyCheckInterval = -1;
    
    int cachePolicyCacheTimeout = -1;
    
    bool enabled : 1;
    
    bool cachePolicyInherit : 1;
    
    bool cachePolicySyncOnDemand : 1;
    
    bool isVirtual : 1;
    Tristate syncPref = Tristate::Undefined;
    Tristate displayPref = Tristate::Undefined;
    Tristate indexPref = Tristate::Undefined;
    
    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 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()
    : 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 cachePolicyInherit, int cachePolicyCheckInterval, int cachePolicyCacheTimeout, bool cachePolicySyncOnDemand, const QString &cachePolicyLocalParts, const QString &queryString, const QString &queryAttributes, const QString &queryCollections, bool isVirtual
) : 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->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 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->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;
}

// comparison 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::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 = {
        idColumn()
        ,remoteIdColumn()
        ,remoteRevisionColumn()
        ,nameColumn()
        ,parentIdColumn()
        ,resourceIdColumn()
        ,enabledColumn()
        ,syncPrefColumn()
        ,displayPrefColumn()
        ,indexPrefColumn()
        ,cachePolicyInheritColumn()
        ,cachePolicyCheckIntervalColumn()
        ,cachePolicyCacheTimeoutColumn()
        ,cachePolicySyncOnDemandColumn()
        ,cachePolicyLocalPartsColumn()
        ,queryStringColumn()
        ,queryAttributesColumn()
        ,queryCollectionsColumn()
        ,isVirtualColumn()
        
    };
    return columns;
}

QStringList Collection::fullColumnNames()
{
    static const QStringList columns = {
        idFullColumnName()
        ,remoteIdFullColumnName()
        ,remoteRevisionFullColumnName()
        ,nameFullColumnName()
        ,parentIdFullColumnName()
        ,resourceIdFullColumnName()
        ,enabledFullColumnName()
        ,syncPrefFullColumnName()
        ,displayPrefFullColumnName()
        ,indexPrefFullColumnName()
        ,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::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)
                ? int()
                : query.value(11).value<int>()),
              (query.isNull(12)
                ? int()
                : query.value(12).value<int>()),
              (query.isNull(13)
                ? bool()
                : query.value(13).value<bool>()),
              (query.isNull(14)
                ? QString()
                : Utils::variantToString(query.value(14))),
              (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)
                ? bool()
                : query.value(18).value<bool>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
Collection Collection::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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(true) :
        
            qb.query().value( valueIndex ).value<bool>()
          
           ; ++valueIndex;
      
    const Collection::Tristate &value8 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate(Tristate::Undefined) :
        
            static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
           ; ++valueIndex;
      
    const Collection::Tristate &value9 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate(Tristate::Undefined) :
        
            static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
           ; ++valueIndex;
      
    const Collection::Tristate &value10 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate(Tristate::Undefined) :
        
            static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
           ; ++valueIndex;
      
    const bool &value11 =
    
        (qb.query().isNull(valueIndex)) ?
        bool(true) :
        
            qb.query().value( valueIndex ).value<bool>()
          
           ; ++valueIndex;
      
    const int &value12 =
    
        (qb.query().isNull(valueIndex)) ?
        int(-1) :
        
            qb.query().value( valueIndex ).value<int>()
          
           ; ++valueIndex;
      
    const int &value13 =
    
        (qb.query().isNull(valueIndex)) ?
        int(-1) :
        
            qb.query().value( valueIndex ).value<int>()
          
           ; ++valueIndex;
      
    const bool &value14 =
    
        (qb.query().isNull(valueIndex)) ?
        bool(false) :
        
            qb.query().value( valueIndex ).value<bool>()
          
           ; ++valueIndex;
      
    const QString &value15 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
            Utils::variantToString(qb.query().value( valueIndex))
          
           ; ++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 bool &value19 =
    
        (qb.query().isNull(valueIndex)) ?
        bool(false) :
        
            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
    );
    if (Private::cacheEnabled) {
        Private::addToCache(rv);
    }
    return rv;

}

Collection Collection::retrieveByName(const QString &name)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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(true) :
        
            qb.query().value( valueIndex ).value<bool>()
          
           ; ++valueIndex;
      
    const Collection::Tristate &value8 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate(Tristate::Undefined) :
        
            static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
           ; ++valueIndex;
      
    const Collection::Tristate &value9 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate(Tristate::Undefined) :
        
            static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
           ; ++valueIndex;
      
    const Collection::Tristate &value10 =
    
        (qb.query().isNull(valueIndex)) ?
        Collection::Tristate(Tristate::Undefined) :
        
            static_cast<Tristate>(qb.query().value( valueIndex ).value<int>())
          
           ; ++valueIndex;
      
    const bool &value11 =
    
        (qb.query().isNull(valueIndex)) ?
        bool(true) :
        
            qb.query().value( valueIndex ).value<bool>()
          
           ; ++valueIndex;
      
    const int &value12 =
    
        (qb.query().isNull(valueIndex)) ?
        int(-1) :
        
            qb.query().value( valueIndex ).value<int>()
          
           ; ++valueIndex;
      
    const int &value13 =
    
        (qb.query().isNull(valueIndex)) ?
        int(-1) :
        
            qb.query().value( valueIndex ).value<int>()
          
           ; ++valueIndex;
      
    const bool &value14 =
    
        (qb.query().isNull(valueIndex)) ?
        bool(false) :
        
            qb.query().value( valueIndex ).value<bool>()
          
           ; ++valueIndex;
      
    const QString &value15 =
    
        (qb.query().isNull(valueIndex)) ?
        QString() :
        
            Utils::variantToString(qb.query().value( valueIndex))
          
           ; ++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 bool &value19 =
    
        (qb.query().isNull(valueIndex)) ?
        bool(false) :
        
            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
    );
    if (Private::cacheEnabled) {
        Private::addToCache(rv);
    }
    return rv;

}

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

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

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 {};
    }

    QueryBuilder qb(MimeType::tableName(), QueryBuilder::Select);
    static const QStringList columns = {
    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 {};
    }

    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 {};
    }

    QueryBuilder qb(PimItem::tableName(), QueryBuilder::Select);
    static const QStringList columns = {
    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 {};
    }

    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())
        << ", "
        << "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->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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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->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->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() // NOLINT(readability-redundant-member-init)
    
      , 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()
    : d(new Private)
{
}

MimeType::MimeType(
  const QString &name
) : 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;
}

// comparison 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 = {
        idColumn()
        ,nameColumn()
        
    };
    return columns;
}

QStringList MimeType::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
MimeType MimeType::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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);
        auto 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;
  QMutexLocker locker(&lock);
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

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

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , 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 = 0;
    
    qint64 mimeTypeId = 0;
    
    qint64 size = 0;
    
    QString remoteId;
    
    QString remoteRevision;
    
    QString gid;
    
    // on non-wince, QDateTime is one int
    
    QDateTime datetime;
    
    QDateTime atime;
    
    int rev = 0;
    
    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()
    : 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
) : 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;
}

// comparison 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 = {
        idColumn()
        ,revColumn()
        ,remoteIdColumn()
        ,remoteRevisionColumn()
        ,gidColumn()
        ,collectionIdColumn()
        ,mimeTypeIdColumn()
        ,datetimeColumn()
        ,atimeColumn()
        ,dirtyColumn()
        ,sizeColumn()
        
    };
    return columns;
}

QStringList PimItem::fullColumnNames()
{
    static const QStringList columns = {
        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>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
PimItem PimItem::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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(0) :
        
            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(QDateTime::currentDateTimeUtc()) :
        
            Utils::variantToDateTime(qb.query().value(valueIndex))
          
           ; ++valueIndex;
      
    const QDateTime &value9 =
    
        (qb.query().isNull(valueIndex)) ?
        QDateTime(QDateTime::currentDateTimeUtc()) :
        
            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(0) :
        
            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 {};
    }

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 {};
    }

    QueryBuilder qb(Flag::tableName(), QueryBuilder::Select);
    static const QStringList columns = {
    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 {};
    }

    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 {};
    }

    QueryBuilder qb(Tag::tableName(), QueryBuilder::Select);
    static const QStringList columns = {
    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 {};
    }

    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , 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()
    : d(new Private)
{
}

Flag::Flag(
  const QString &name
) : 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;
}

// comparison 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 = {
        idColumn()
        ,nameColumn()
        
    };
    return columns;
}

QStringList Flag::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
Flag Flag::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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);
        auto 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;
  QMutexLocker locker(&lock);
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

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

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , 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()
    : d(new Private)
{
}

PartType::PartType(
  const QString &name, const QString &ns
) : 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;
}

// comparison 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 = {
        idColumn()
        ,nameColumn()
        ,nsColumn()
        
    };
    return columns;
}

QStringList PartType::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
PartType PartType::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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);
        auto 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;
  QMutexLocker locker(&lock);
  PartType rv = retrieveByFQName(ns, name);
  if (rv.isValid()) {
    return rv;
  }

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

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , pimItemId_changed(false)
    
      , partTypeId_changed(false)
    
      , data_changed(false)
    
      , datasize_changed(false)
    
      , version_changed(false)
    
      , storage_changed(false)
    
    {}

    
    qint64 pimItemId = 0;
    
    qint64 partTypeId = 0;
    
    qint64 datasize = 0;
    
    QByteArray data;
    
    int version = 0;
    Storage storage = Internal;
    
    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()
    : d(new Private)
{
}

Part::Part(
  qint64 pimItemId, qint64 partTypeId, const QByteArray &data, qint64 datasize, int version, Part::Storage storage
) : 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;
}

// comparison 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 = {
        idColumn()
        ,pimItemIdColumn()
        ,partTypeIdColumn()
        ,dataColumn()
        ,datasizeColumn()
        ,versionColumn()
        ,storageColumn()
        
    };
    return columns;
}

QStringList Part::fullColumnNames()
{
    static const QStringList columns = {
        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>()))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
Part Part::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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(0) :
        
            qb.query().value( valueIndex ).value<int>()
          
           ; ++valueIndex;
      
    const Part::Storage &value7 =
    
        (qb.query().isNull(valueIndex)) ?
        Part::Storage(Internal) :
        
            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 {};
    }

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , collectionId_changed(false)
    
      , type_changed(false)
    
      , value_changed(false)
    
    {}

    
    qint64 collectionId = 0;
    
    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()
    : d(new Private)
{
}

CollectionAttribute::CollectionAttribute(
  qint64 collectionId, const QByteArray &type, const QByteArray &value
) : 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;
}

// comparison 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 = {
        idColumn()
        ,collectionIdColumn()
        ,typeColumn()
        ,valueColumn()
        
    };
    return columns;
}

QStringList CollectionAttribute::fullColumnNames()
{
    static const QStringList columns = {
        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>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
CollectionAttribute CollectionAttribute::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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 {};
    }

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , 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()
    : d(new Private)
{
}

TagType::TagType(
  const QString &name
) : 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;
}

// comparison 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 = {
        idColumn()
        ,nameColumn()
        
    };
    return columns;
}

QStringList TagType::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
TagType TagType::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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);
        auto 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;
  QMutexLocker locker(&lock);
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  rv.setName(name);
  if (!rv.insert()) {
    return TagType();
  }

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , gid_changed(false)
    
      , parentId_changed(false)
    
      , typeId_changed(false)
    
    {}

    
    qint64 parentId = 0;
    
    qint64 typeId = 1;
    
    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()
    : d(new Private)
{
}

Tag::Tag(
  const QString &gid, qint64 parentId, qint64 typeId
) : 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;
}

// comparison 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 = {
        idColumn()
        ,gidColumn()
        ,parentIdColumn()
        ,typeIdColumn()
        
    };
    return columns;
}

QStringList Tag::fullColumnNames()
{
    static const QStringList columns = {
        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>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
Tag Tag::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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(1) :
        
            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 {};
    }

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , tagId_changed(false)
    
      , type_changed(false)
    
      , value_changed(false)
    
    {}

    
    qint64 tagId = 0;
    
    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()
    : d(new Private)
{
}

TagAttribute::TagAttribute(
  qint64 tagId, const QByteArray &type, const QByteArray &value
) : 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;
}

// comparison 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 = {
        idColumn()
        ,tagIdColumn()
        ,typeColumn()
        ,valueColumn()
        
    };
    return columns;
}

QStringList TagAttribute::fullColumnNames()
{
    static const QStringList columns = {
        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>())
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
TagAttribute TagAttribute::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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 {};
    }

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , tagId_changed(false)
    
      , resourceId_changed(false)
    
      , remoteId_changed(false)
    
    {}

    
    qint64 tagId = 0;
    
    qint64 resourceId = 0;
    
    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()
    : d(new Private)
{
}

TagRemoteIdResourceRelation::TagRemoteIdResourceRelation(
  qint64 tagId, qint64 resourceId, const QString &remoteId
) : 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;
}

// comparison 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 = {
        tagIdColumn()
        ,resourceIdColumn()
        ,remoteIdColumn()
        
    };
    return columns;
}

QStringList TagRemoteIdResourceRelation::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , 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()
    : d(new Private)
{
}

RelationType::RelationType(
  const QString &name
) : 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;
}

// comparison 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 = {
        idColumn()
        ,nameColumn()
        
    };
    return columns;
}

QStringList RelationType::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval
RelationType RelationType::retrieveById(qint64 id)
{
    
    if (Private::cacheEnabled) {
        QMutexLocker lock(&Private::cacheMutex);
        auto 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);
        auto 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;
  QMutexLocker locker(&lock);
  auto rv = retrieveByName(name);
  if (rv.isValid()) {
    return rv;
  }

  rv.setName(name);
  if (!rv.insert()) {
    return RelationType();
  }

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


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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() // NOLINT(readability-redundant-member-init)
    
      , leftId_changed(false)
    
      , rightId_changed(false)
    
      , typeId_changed(false)
    
      , remoteId_changed(false)
    
    {}

    
    qint64 leftId = 0;
    
    qint64 rightId = 0;
    
    qint64 typeId = 1;
    
    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()
    : d(new Private)
{
}

Relation::Relation(
  qint64 leftId, qint64 rightId, qint64 typeId, const QString &remoteId
) : 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;
}

// comparison 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 = {
        leftIdColumn()
        ,rightIdColumn()
        ,typeIdColumn()
        ,remoteIdColumn()
        
    };
    return columns;
}

QStringList Relation::fullColumnNames()
{
    static const QStringList columns = {
        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)))
            ));
    }
    query.finish();
    return rv;
}

// data retrieval


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

    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 {};
    }
    return extractResult(qb.query());
}

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

    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 {};
    }
    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 // NOLINT(readability-simplify-boolean-expr)
      
        || 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 = {
    
        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;
}

