File indexing completed on 2024-06-23 05:07:03

0001 /*
0002     SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "tagfetchhelper.h"
0008 #include "connection.h"
0009 #include "handler.h"
0010 #include "storage/querybuilder.h"
0011 #include "storage/tagqueryhelper.h"
0012 #include "utils.h"
0013 
0014 using namespace Akonadi;
0015 using namespace Akonadi::Server;
0016 
0017 TagFetchHelper::TagFetchHelper(Connection *connection, const Scope &scope, const Protocol::TagFetchScope &fetchScope)
0018     : mConnection(connection)
0019     , mScope(scope)
0020     , mFetchScope(fetchScope)
0021 {
0022 }
0023 
0024 QSqlQuery TagFetchHelper::buildAttributeQuery() const
0025 {
0026     QueryBuilder qb(TagAttribute::tableName());
0027     qb.addColumn(TagAttribute::tagIdFullColumnName());
0028     qb.addColumn(TagAttribute::typeFullColumnName());
0029     qb.addColumn(TagAttribute::valueFullColumnName());
0030     qb.addSortColumn(TagAttribute::tagIdFullColumnName(), Query::Descending);
0031     qb.addJoin(QueryBuilder::InnerJoin, Tag::tableName(), TagAttribute::tagIdFullColumnName(), Tag::idFullColumnName());
0032     TagQueryHelper::scopeToQuery(mScope, mConnection->context(), qb);
0033 
0034     if (!qb.exec()) {
0035         throw HandlerException("Unable to list tag attributes");
0036     }
0037 
0038     qb.query().next();
0039     return qb.query();
0040 }
0041 
0042 QSqlQuery TagFetchHelper::buildAttributeQuery(qint64 id, const Protocol::TagFetchScope &fetchScope)
0043 {
0044     QueryBuilder qb(TagAttribute::tableName());
0045     qb.addColumn(TagAttribute::tagIdColumn());
0046     qb.addColumn(TagAttribute::typeColumn());
0047     qb.addColumn(TagAttribute::valueColumn());
0048     qb.addSortColumn(TagAttribute::tagIdColumn(), Query::Descending);
0049 
0050     qb.addValueCondition(TagAttribute::tagIdColumn(), Query::Equals, id);
0051     if (!fetchScope.fetchAllAttributes() && !fetchScope.attributes().isEmpty()) {
0052         QVariantList typeNames;
0053         const auto attrs = fetchScope.attributes();
0054         std::transform(attrs.cbegin(), attrs.cend(), std::back_inserter(typeNames), [](const QByteArray &ba) {
0055             return QVariant(ba);
0056         });
0057         qb.addValueCondition(TagAttribute::typeColumn(), Query::In, typeNames);
0058     }
0059 
0060     if (!qb.exec()) {
0061         throw HandlerException("Unable to list tag attributes");
0062     }
0063 
0064     qb.query().next();
0065     return qb.query();
0066 }
0067 
0068 QSqlQuery TagFetchHelper::buildTagQuery()
0069 {
0070     QueryBuilder qb(Tag::tableName());
0071     qb.addColumn(Tag::idFullColumnName());
0072     qb.addColumn(Tag::gidFullColumnName());
0073     qb.addColumn(Tag::parentIdFullColumnName());
0074 
0075     qb.addJoin(QueryBuilder::InnerJoin, TagType::tableName(), Tag::typeIdFullColumnName(), TagType::idFullColumnName());
0076     qb.addColumn(TagType::nameFullColumnName());
0077 
0078     // Expose tag's remote ID only to resources
0079     if (mFetchScope.fetchRemoteID() && mConnection->context().resource().isValid()) {
0080         qb.addColumn(TagRemoteIdResourceRelation::remoteIdFullColumnName());
0081         Query::Condition joinCondition;
0082         joinCondition.addValueCondition(TagRemoteIdResourceRelation::resourceIdFullColumnName(), Query::Equals, mConnection->context().resource().id());
0083         joinCondition.addColumnCondition(TagRemoteIdResourceRelation::tagIdFullColumnName(), Query::Equals, Tag::idFullColumnName());
0084         qb.addJoin(QueryBuilder::LeftJoin, TagRemoteIdResourceRelation::tableName(), joinCondition);
0085     }
0086 
0087     qb.addSortColumn(Tag::idFullColumnName(), Query::Descending);
0088     TagQueryHelper::scopeToQuery(mScope, mConnection->context(), qb);
0089     if (!qb.exec()) {
0090         throw HandlerException("Unable to list tags");
0091     }
0092 
0093     qb.query().next();
0094     return qb.query();
0095 }
0096 
0097 QMap<QByteArray, QByteArray> TagFetchHelper::fetchTagAttributes(qint64 tagId, const Protocol::TagFetchScope &fetchScope)
0098 {
0099     QMap<QByteArray, QByteArray> attributes;
0100 
0101     QSqlQuery attributeQuery = buildAttributeQuery(tagId, fetchScope);
0102     while (attributeQuery.isValid()) {
0103         attributes.insert(Utils::variantToByteArray(attributeQuery.value(1)), Utils::variantToByteArray(attributeQuery.value(2)));
0104         attributeQuery.next();
0105     }
0106     attributeQuery.finish();
0107     return attributes;
0108 }
0109 
0110 bool TagFetchHelper::fetchTags()
0111 {
0112     QSqlQuery tagQuery = buildTagQuery();
0113     QSqlQuery attributeQuery;
0114     if (!mFetchScope.fetchIdOnly()) {
0115         attributeQuery = buildAttributeQuery();
0116     }
0117 
0118     while (tagQuery.isValid()) {
0119         const qint64 tagId = tagQuery.value(0).toLongLong();
0120         Protocol::FetchTagsResponse response;
0121         response.setId(tagId);
0122         if (!mFetchScope.fetchIdOnly()) {
0123             response.setGid(Utils::variantToByteArray(tagQuery.value(1)));
0124             if (tagQuery.value(2).isNull()) {
0125                 // client indicates invalid or null parent as ID -1
0126                 response.setParentId(-1);
0127             } else {
0128                 response.setParentId(tagQuery.value(2).toLongLong());
0129             }
0130             response.setType(Utils::variantToByteArray(tagQuery.value(3)));
0131             if (mFetchScope.fetchRemoteID() && mConnection->context().resource().isValid()) {
0132                 response.setRemoteId(Utils::variantToByteArray(tagQuery.value(4)));
0133             }
0134 
0135             QMap<QByteArray, QByteArray> tagAttributes;
0136             while (attributeQuery.isValid()) {
0137                 const qint64 id = attributeQuery.value(0).toLongLong();
0138                 if (id > tagId) {
0139                     attributeQuery.next();
0140                     continue;
0141                 } else if (id < tagId) {
0142                     break;
0143                 }
0144 
0145                 tagAttributes.insert(Utils::variantToByteArray(attributeQuery.value(1)), Utils::variantToByteArray(attributeQuery.value(2)));
0146                 attributeQuery.next();
0147             }
0148 
0149             response.setAttributes(tagAttributes);
0150         }
0151 
0152         mConnection->sendResponse(std::move(response));
0153 
0154         tagQuery.next();
0155     }
0156     attributeQuery.finish();
0157     tagQuery.finish();
0158 
0159     return true;
0160 }