Akonadi

handlerhelper.cpp
1 /***************************************************************************
2  * Copyright (C) 2006 by Tobias Koenig <[email protected]> *
3  * *
4  * This program is free software; you can redistribute it and/or modify *
5  * it under the terms of the GNU Library General Public License as *
6  * published by the Free Software Foundation; either version 2 of the *
7  * License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU Library General Public *
15  * License along with this program; if not, write to the *
16  * Free Software Foundation, Inc., *
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18  ***************************************************************************/
19 
20 #include "handlerhelper.h"
21 #include "storage/countquerybuilder.h"
22 #include "storage/datastore.h"
23 #include "storage/selectquerybuilder.h"
24 #include "storage/collectionstatistics.h"
25 #include "storage/queryhelper.h"
26 #include "storage/collectionqueryhelper.h"
27 #include "commandcontext.h"
28 #include "handler.h"
29 #include "connection.h"
30 #include "utils.h"
31 #include "akonadi.h"
32 
33 #include <private/imapset_p.h>
34 #include <private/scope_p.h>
35 #include <private/protocol_p.h>
36 
37 using namespace Akonadi;
38 using namespace Akonadi::Server;
39 
41 {
42  // id is a number
43  bool ok = false;
44  qint64 collectionId = id.toLongLong(&ok);
45  if (ok) {
46  return Collection::retrieveById(collectionId);
47  }
48 
49  // id is a path
50  QString path = QString::fromUtf8(id); // ### should be UTF-7 for real IMAP compatibility
51 
52  const QStringList pathParts = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
53  Collection col;
54  for (const QString &part : pathParts) {
56  qb.addValueCondition(Collection::nameColumn(), Query::Equals, part);
57  if (col.isValid()) {
58  qb.addValueCondition(Collection::parentIdColumn(), Query::Equals, col.id());
59  } else {
60  qb.addValueCondition(Collection::parentIdColumn(), Query::Is, QVariant());
61  }
62  if (!qb.exec()) {
63  return Collection();
64  }
65  Collection::List list = qb.result();
66  if (list.count() != 1) {
67  return Collection();
68  }
69  col = list.first();
70  }
71  return col;
72 }
73 
75 {
76  QStringList parts;
77  Collection current = col;
78  while (current.isValid()) {
79  parts.prepend(current.name());
80  current = current.parent();
81  }
82  return parts.join(QLatin1Char('/'));
83 }
84 
85 Protocol::CachePolicy HandlerHelper::cachePolicyResponse(const Collection &col)
86 {
87  Protocol::CachePolicy cachePolicy;
88  cachePolicy.setInherit(col.cachePolicyInherit());
89  cachePolicy.setCacheTimeout(col.cachePolicyCacheTimeout());
90  cachePolicy.setCheckInterval(col.cachePolicyCheckInterval());
91  if (!col.cachePolicyLocalParts().isEmpty()) {
92  cachePolicy.setLocalParts(col.cachePolicyLocalParts().split(QLatin1Char(' ')));
93  }
94  cachePolicy.setSyncOnDemand(col.cachePolicySyncOnDemand());
95  return cachePolicy;
96 }
97 
98 Protocol::FetchCollectionsResponse HandlerHelper::fetchCollectionsResponse(
99  AkonadiServer &akonadi, const Collection &col)
100 {
101  QStringList mimeTypes;
102  mimeTypes.reserve(col.mimeTypes().size());
103  Q_FOREACH (const MimeType &mt, col.mimeTypes()) {
104  mimeTypes << mt.name();
105  }
106 
107  return fetchCollectionsResponse(akonadi, col, col.attributes(), false, 0, QStack<Collection>(),
108  QStack<CollectionAttribute::List>(), mimeTypes);
109 }
110 
111 Protocol::FetchCollectionsResponse HandlerHelper::fetchCollectionsResponse(
112  AkonadiServer &akonadi,
113  const Collection &col,
114  const CollectionAttribute::List &attrs,
115  bool includeStatistics,
116  int ancestorDepth,
117  const QStack<Collection> &ancestors,
118  const QStack<CollectionAttribute::List> &ancestorAttributes,
119  const QStringList &mimeTypes)
120 {
121  Protocol::FetchCollectionsResponse response;
122  response.setId(col.id());
123  response.setParentId(col.parentId());
124  response.setName(col.name());
125  response.setMimeTypes(mimeTypes);
126  response.setRemoteId(col.remoteId());
127  response.setRemoteRevision(col.remoteRevision());
128  response.setResource(col.resource().name());
129  response.setIsVirtual(col.isVirtual());
130 
131  if (includeStatistics) {
132  const auto stats = akonadi.collectionStatistics().statistics(col);
133  if (stats.count > -1) {
134  Protocol::FetchCollectionStatsResponse statsResponse;
135  statsResponse.setCount(stats.count);
136  statsResponse.setUnseen(stats.count - stats.read);
137  statsResponse.setSize(stats.size);
138  response.setStatistics(statsResponse);
139  }
140  }
141 
142  if (!col.queryString().isEmpty()) {
143  response.setSearchQuery(col.queryString());
144  QVector<qint64> searchCols;
145  const QStringList searchColIds = col.queryCollections().split(QLatin1Char(' '));
146  searchCols.reserve(searchColIds.size());
147  for (const QString &searchColId : searchColIds) {
148  searchCols << searchColId.toLongLong();
149  }
150  response.setSearchCollections(searchCols);
151  }
152 
153  Protocol::CachePolicy cachePolicy = cachePolicyResponse(col);
154  response.setCachePolicy(cachePolicy);
155 
156  if (ancestorDepth) {
157  QVector<Protocol::Ancestor> ancestorList
158  = HandlerHelper::ancestorsResponse(ancestorDepth, ancestors, ancestorAttributes);
159  response.setAncestors(ancestorList);
160  }
161 
162  response.setEnabled(col.enabled());
163  response.setDisplayPref(static_cast<Tristate>(col.displayPref()));
164  response.setSyncPref(static_cast<Tristate>(col.syncPref()));
165  response.setIndexPref(static_cast<Tristate>(col.indexPref()));
166 
168  for (const CollectionAttribute &attr : attrs) {
169  ra.insert(attr.type(), attr.value());
170  }
171  response.setAttributes(ra);
172 
173  return response;
174 }
175 
177  const QStack<Collection> &_ancestors,
178  const QStack<CollectionAttribute::List> &_ancestorsAttributes)
179 {
181  if (ancestorDepth > 0) {
182  QStack<Collection> ancestors(_ancestors);
183  QStack<CollectionAttribute::List> ancestorAttributes(_ancestorsAttributes);
184  for (int i = 0; i < ancestorDepth; ++i) {
185  if (ancestors.isEmpty()) {
186  Protocol::Ancestor ancestor;
187  ancestor.setId(0);
188  rv << ancestor;
189  break;
190  }
191  const Collection c = ancestors.pop();
192  Protocol::Ancestor a;
193  a.setId(c.id());
194  a.setRemoteId(c.remoteId());
195  a.setName(c.name());
196  if (!ancestorAttributes.isEmpty()) {
198  Q_FOREACH (const CollectionAttribute &attr, ancestorAttributes.pop()) {
199  attrs.insert(attr.type(), attr.value());
200  }
201  a.setAttributes(attrs);
202  }
203 
204  rv << a;
205  }
206  }
207 
208  return rv;
209 }
210 
211 
212 Protocol::FetchTagsResponse HandlerHelper::fetchTagsResponse(const Tag &tag,
213  const Protocol::TagFetchScope &tagFetchScope,
214  Connection *connection)
215 {
216  Protocol::FetchTagsResponse response;
217  response.setId(tag.id());
218  if (tagFetchScope.fetchIdOnly()) {
219  return response;
220  }
221 
222  response.setType(tag.tagType().name().toUtf8());
223  // FIXME FIXME FIXME Terrible hack to workaround limitations of the generated entities code:
224  // The invalid parent is represented in code by -1 but in the DB it is stored as NULL, which
225  // gets converted to 0 by our entities code.
226  if (tag.parentId() == 0) {
227  response.setParentId(-1);
228  } else {
229  response.setParentId(tag.parentId());
230  }
231  response.setGid(tag.gid().toUtf8());
232  if (tagFetchScope.fetchRemoteID() && connection) {
233  // Fail silently if retrieving tag RID is not allowed in current context
234  if (connection->context().resource().isValid()) {
236  qb.addColumn(TagRemoteIdResourceRelation::remoteIdColumn());
237  qb.addValueCondition(TagRemoteIdResourceRelation::resourceIdColumn(),
238  Query::Equals,
239  connection->context().resource().id());
240  qb.addValueCondition(TagRemoteIdResourceRelation::tagIdColumn(),
241  Query::Equals,
242  tag.id());
243  if (!qb.exec()) {
244  throw HandlerException("Unable to query Tag Remote ID");
245  }
246  QSqlQuery query = qb.query();
247  // RID may not be available
248  if (query.next()) {
249  response.setRemoteId(Utils::variantToByteArray(query.value(0)));
250  }
251  query.finish();
252  }
253  }
254 
255  if (tagFetchScope.fetchAllAttributes() || !tagFetchScope.attributes().isEmpty()) {
257  qb.addColumns({ TagAttribute::typeFullColumnName(),
258  TagAttribute::valueFullColumnName() });
259  Query::Condition cond(Query::And);
260  cond.addValueCondition(TagAttribute::tagIdFullColumnName(), Query::Equals, tag.id());
261  if (!tagFetchScope.fetchAllAttributes() && !tagFetchScope.attributes().isEmpty()) {
262  QVariantList types;
263  const auto scope = tagFetchScope.attributes();
264  std::transform(scope.cbegin(), scope.cend(), std::back_inserter(types),
265  [](const QByteArray &ba) { return QVariant(ba); });
266  cond.addValueCondition(TagAttribute::typeFullColumnName(), Query::In, types);
267  }
268  qb.addCondition(cond);
269  if (!qb.exec()) {
270  throw HandlerException("Unable to query Tag Attributes");
271  }
272  QSqlQuery query = qb.query();
273  Protocol::Attributes attributes;
274  while (query.next()) {
275  attributes.insert(Utils::variantToByteArray(query.value(0)),
276  Utils::variantToByteArray(query.value(1)));
277  }
278  query.finish();
279  response.setAttributes(attributes);
280  }
281 
282  return response;
283 }
284 
285 Protocol::FetchRelationsResponse HandlerHelper::fetchRelationsResponse(const Relation &relation)
286 {
287  Protocol::FetchRelationsResponse resp;
288  resp.setLeft(relation.leftId());
289  resp.setLeftMimeType(relation.left().mimeType().name().toUtf8());
290  resp.setRight(relation.rightId());
291  resp.setRightMimeType(relation.right().mimeType().name().toUtf8());
292  resp.setType(relation.relationType().name().toUtf8());
293  return resp;
294 }
295 
297 {
298  Flag::List flagList;
299  flagList.reserve(flagNames.size());
300  for (const QByteArray &flagName : flagNames) {
301  const Flag flag = Flag::retrieveByNameOrCreate(QString::fromUtf8(flagName));
302  if (!flag.isValid()) {
303  throw HandlerException("Unable to create flag");
304  }
305  flagList.append(flag);
306  }
307  return flagList;
308 }
309 
311 {
312  if (tags.isEmpty()) {
313  return Tag::List();
314  }
316  QueryHelper::setToQuery(tags, Tag::idFullColumnName(), qb);
317  if (!qb.exec()) {
318  throw HandlerException("Unable to resolve tags");
319  }
320  const Tag::List result = qb.result();
321  if (result.isEmpty()) {
322  throw HandlerException("No tags found");
323  }
324  return result;
325 }
326 
327 Tag::List HandlerHelper::resolveTagsByGID(const QStringList &tagsGIDs)
328 {
329  Tag::List tagList;
330  if (tagsGIDs.isEmpty()) {
331  return tagList;
332  }
333 
334  for (const QString &tagGID : tagsGIDs) {
335  Tag::List tags = Tag::retrieveFiltered(Tag::gidColumn(), tagGID);
336  Tag tag;
337  if (tags.isEmpty()) {
338  tag.setGid(tagGID);
339  tag.setParentId(0);
340 
341  const TagType type = TagType::retrieveByNameOrCreate(QStringLiteral("PLAIN"));
342  if (!type.isValid()) {
343  throw HandlerException("Unable to create tag type");
344  }
345  tag.setTagType(type);
346  if (!tag.insert()) {
347  throw HandlerException("Unable to create tag");
348  }
349  } else if (tags.count() == 1) {
350  tag = tags[0];
351  } else {
352  // Should not happen
353  throw HandlerException("Tag GID is not unique");
354  }
355 
356  tagList.append(tag);
357  }
358 
359  return tagList;
360 }
361 
362 Tag::List HandlerHelper::resolveTagsByRID(const QStringList &tagsRIDs, const CommandContext &context)
363 {
364  Tag::List tags;
365  if (tagsRIDs.isEmpty()) {
366  return tags;
367  }
368 
369  if (!context.resource().isValid()) {
370  throw HandlerException("Tags can be resolved by their RID only in resource context");
371  }
372 
373  tags.reserve(tagsRIDs.size());
374  for (const QString &tagRID : tagsRIDs) {
376  Query::Condition cond;
377  cond.addColumnCondition(Tag::idFullColumnName(), Query::Equals, TagRemoteIdResourceRelation::tagIdFullColumnName());
378  cond.addValueCondition(TagRemoteIdResourceRelation::resourceIdFullColumnName(), Query::Equals, context.resource().id());
380  qb.addValueCondition(TagRemoteIdResourceRelation::remoteIdFullColumnName(), Query::Equals, tagRID);
381  if (!qb.exec()) {
382  throw HandlerException("Unable to resolve tags");
383  }
384 
385  Tag tag;
386  Tag::List results = qb.result();
387  if (results.isEmpty()) {
388  // If the tag does not exist, we create a new one with GID matching RID
389  Tag::List tags = resolveTagsByGID(QStringList() << tagRID);
390  if (tags.count() != 1) {
391  throw HandlerException("Unable to resolve tag");
392  }
393  tag = tags[0];
395  rel.setRemoteId(tagRID);
396  rel.setTagId(tag.id());
397  rel.setResourceId(context.resource().id());
398  if (!rel.insert()) {
399  throw HandlerException("Unable to create tag");
400  }
401  } else if (results.count() == 1) {
402  tag = results[0];
403  } else {
404  throw HandlerException("Tag RID is not unique within this resource context");
405  }
406 
407  tags.append(tag);
408  }
409 
410  return tags;
411 }
412 
413 Collection HandlerHelper::collectionFromScope(const Scope &scope, const CommandContext &context)
414 {
415  if (scope.scope() == Scope::Invalid || scope.scope() == Scope::Gid) {
416  throw HandlerException("Invalid collection scope");
417  }
418 
420  CollectionQueryHelper::scopeToQuery(scope, context, qb);
421  if (!qb.exec()) {
422  throw HandlerException("Failed to execute SQL query");
423  }
424 
425  const Collection::List c = qb.result();
426  if (c.isEmpty()) {
427  return Collection();
428  } else if (c.count() == 1) {
429  return c.at(0);
430  } else {
431  throw HandlerException("Query returned more than one reslut");
432  }
433 }
434 
435 Tag::List HandlerHelper::tagsFromScope(const Scope &scope, const CommandContext &context)
436 {
437  if (scope.scope() == Scope::Invalid || scope.scope() == Scope::HierarchicalRid) {
438  throw HandlerException("Invalid tag scope");
439  }
440 
441  if (scope.scope() == Scope::Uid) {
442  return resolveTagsByUID(scope.uidSet());
443  } else if (scope.scope() == Scope::Gid) {
444  return resolveTagsByGID(scope.gidSet());
445  } else if (scope.scope() == Scope::Rid) {
446  return resolveTagsByRID(scope.ridSet(), context);
447  }
448 
449  Q_ASSERT(false);
450  return Tag::List();
451 }
static Collection collectionFromIdOrName(const QByteArray &id)
Returns the collection identified by the given id or path.
void addValueCondition(const QString &column, Query::CompareOperator op, const QVariant &value, ConditionType type=WhereCondition)
Add a WHERE or HAVING condition which compares a column with a given value.
void setParentId(qint64 parentId)
Sets the value of the parentId column of this record.
Definition: entities.cpp:8381
QSqlQuery & query()
Returns the query, only valid after exec().
void append(const T &value)
int cachePolicyCheckInterval() const
Returns the value of the cachePolicyCheckInterval column of this record.
Definition: entities.cpp:1533
QString remoteId() const
Returns the value of the remoteId column of this record.
int size() const const
static Flag::List resolveFlags(const QSet< QByteArray > &flagNames)
Converts a bytearray list of flag names into flag records.
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
void reserve(int alloc)
Resource resource() const
Retrieve the Resource record referred to by the resourceId column of this record. ...
Representation of a record in the MimeType table.
Definition: entities.h:1011
int cachePolicyCacheTimeout() const
Returns the value of the cachePolicyCacheTimeout column of this record.
Definition: entities.cpp:1546
PimItem right() const
Retrieve the PimItem record referred to by the rightId column of this record.
void setId(Id identifier)
Sets the unique identifier of the collection.
Definition: collection.cpp:107
Collection::Tristate indexPref() const
Returns the value of the indexPref column of this record.
Definition: entities.cpp:1507
qint64 rightId() const
Returns the value of the rightId column of this record.
Definition: entities.cpp:10637
void addJoin(JoinType joinType, const QString &table, const Query::Condition &condition)
Join a table to the query.
T & first()
QString join(const QString &separator) const const
qint64 parentId() const
Returns the value of the parentId column of this record.
Definition: entities.cpp:1442
TagType tagType() const
Retrieve the TagType record referred to by the typeId column of this record.
Definition: entities.cpp:8666
QString name() const
Returns the value of the name column of this record.
Definition: entities.cpp:3309
int size() const const
Representation of a record in the Flag table.
Definition: entities.h:1595
static QString tableName()
Returns the name of the SQL table.
Definition: entities.cpp:9033
PimItem left() const
Retrieve the PimItem record referred to by the leftId column of this record.
void addColumn(const QString &col)
Adds the given column to a select query.
void setTagId(qint64 tagId)
Sets the value of the tagId column of this record.
Definition: entities.cpp:9587
QString gid() const
Returns the value of the gid column of this record.
QString fromUtf8(const char *str, int size)
qint64 leftId() const
Returns the value of the leftId column of this record.
Definition: entities.cpp:10624
QVariant value(int index) const const
RelationType relationType() const
Retrieve the RelationType record referred to by the typeId column of this record. ...
Definition: entities.cpp:10865
bool isEmpty() const const
bool isEmpty() const const
bool next()
QVector< T > result()
Returns the result of this SELECT query.
static Protocol::FetchCollectionsResponse fetchCollectionsResponse(AkonadiServer &akonadi, const Collection &col)
Returns the protocol representation of the given collection.
QVector< CollectionAttribute > attributes() const
Retrieve a list of all CollectionAttribute records referring to this record in their column collectio...
void addColumnCondition(const QString &column, CompareOperator op, const QString &column2)
Add a WHERE condition which compares a column with another column.
Definition: query.cpp:35
static Tag::List resolveTagsByUID(const ImapSet &tags)
Converts a imap set of tags into tag records.
NOTE: only supported for SELECT queries.
Definition: querybuilder.h:64
void scopeToQuery(const Scope &scope, const CommandContext &context, QueryBuilder &qb)
Add conditions to qb for the given collection operation scope scope.
void setGid(const QString &gid)
Sets the value of the gid column of this record.
QByteArray type() const
Returns the value of the type column of this record.
Definition: entities.cpp:7212
void reserve(int size)
Representation of a record in the CollectionAttribute table.
Definition: entities.h:2254
static TagType retrieveByNameOrCreate(const QString &name)
Returns the record with name name.
Definition: entities.cpp:8028
bool cachePolicySyncOnDemand() const
Returns the value of the cachePolicySyncOnDemand column of this record.
Definition: entities.cpp:1559
Representation of a record in the TagRemoteIdResourceRelation table.
Definition: entities.h:3098
void setToQuery(const ImapSet &set, const QString &column, QueryBuilder &qb)
Add conditions to qb for the given uid set set applied to column.
Definition: queryhelper.cpp:29
Representation of a record in the TagType table.
Definition: entities.h:2470
qint64 parentId() const
Returns the value of the parentId column of this record.
Definition: entities.cpp:8375
const T & at(int i) const const
static QString pathForCollection(const Collection &col)
Returns the full path for the given collection.
Helper class for creating and executing database SELECT queries.
QByteArray value() const
Returns the value of the value column of this record.
Definition: entities.cpp:7225
QString name() const
Returns the value of the name column of this record.
Definition: entities.cpp:7790
QString queryString() const
Returns the value of the queryString column of this record.
Definition: entities.cpp:1585
static QVector< Protocol::Ancestor > ancestorsResponse(int ancestorDepth, const QStack< Collection > &ancestors, const QStack< CollectionAttribute::List > &_ancestorsAttributes=QStack< CollectionAttribute::List >())
Returns the protocol representation of a collection ancestor chain.
QString name() const
Returns the value of the name column of this record.
void finish()
void setResourceId(qint64 resourceId)
Sets the value of the resourceId column of this record.
Definition: entities.cpp:9600
bool isEmpty() const const
bool insert(qint64 *insertId=nullptr)
Inserts this record into the DataStore.
Definition: entities.cpp:8711
Helper integration between Akonadi and Qt.
Representation of a record in the Tag table.
Definition: entities.h:2653
static Collection retrieveById(qint64 id)
Returns the record with id id.
Definition: entities.cpp:2037
bool cachePolicyInherit() const
Returns the value of the cachePolicyInherit column of this record.
Definition: entities.cpp:1520
Collection::Tristate syncPref() const
Returns the value of the syncPref column of this record.
Definition: entities.cpp:1481
int count(const T &value) const const
Representation of a record in the Relation table.
Definition: entities.h:3492
QString queryCollections() const
Returns the value of the queryCollections column of this record.
Definition: entities.cpp:1611
static Tag::List retrieveFiltered(const QString &key, const QVariant &value)
Retrieve all records with value value in column key.
Definition: entities.cpp:8631
QString name() const
Returns the value of the name column of this record.
Definition: entities.cpp:10064
bool enabled() const
Returns the value of the enabled column of this record.
QString remoteRevision() const
Returns the value of the remoteRevision column of this record.
void setRemoteId(const QString &remoteId)
Sets the value of the remoteId column of this record.
Definition: entities.cpp:9613
void prepend(const T &value)
static Flag retrieveByNameOrCreate(const QString &name)
Returns the record with name name.
Definition: entities.cpp:5385
QMap::iterator insert(const Key &key, const T &value)
Represents a WHERE condition tree.
Definition: query.h:77
Collection::Tristate displayPref() const
Returns the value of the displayPref column of this record.
Definition: entities.cpp:1494
void addColumns(const QStringList &cols)
Adds the given columns to a select query.
bool isVirtual() const
Returns the value of the isVirtual column of this record.
static QString tableName()
Returns the name of the SQL table.
Definition: entities.cpp:9623
bool insert(qint64 *insertId=nullptr)
Inserts this record into the DataStore.
Definition: entities.cpp:9821
QString name() const
Returns the value of the name column of this record.
Definition: entities.cpp:545
void setTagType(const TagType &value)
Set the TagType record referred to by the typeId column of this record.
Definition: entities.cpp:8673
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:45
MimeType mimeType() const
Retrieve the MimeType record referred to by the mimeTypeId column of this record. ...
Definition: entities.cpp:4567
Collection parent() const
Retrieve the Collection record referred to by the parentId column of this record. ...
Definition: entities.cpp:2552
QString cachePolicyLocalParts() const
Returns the value of the cachePolicyLocalParts column of this record.
Definition: entities.cpp:1572
void addCondition(const Query::Condition &condition, ConditionType type=WhereCondition)
Add a WHERE condition.
QVector< Tag > List
List of Tag records.
Definition: entities.h:2659
bool exec()
Executes the query, returns true on success.
An Connection represents one connection of a client to the server.
Definition: connection.h:53
static Protocol::CachePolicy cachePolicyResponse(const Collection &col)
Returns the protocol representation of the cache policy of the given Collection object.
void addValueCondition(const QString &column, CompareOperator op, const QVariant &value)
Add a WHERE condition which compares a column with a given value.
Definition: query.cpp:25
Representation of a record in the Collection table.
Definition: entities.h:451
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue May 26 2020 22:46:20 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.