New: Introduce runQuery variants that accept a custom QSparqlConnection
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.git] / src / engine / abstractrequest.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 // Needs to go on top due to clash between GIO and "signals" keyword.
43 #include <tracker-sparql.h>
44
45 #include "abstractrequest.h"
46
47 #include <dao/sparqlconnectionmanager.h>
48 #include <engine/engine.h>
49
50 #include <ontologies/nco.h>
51 #include <QtSparql>
52
53 ///////////////////////////////////////////////////////////////////////////////////////////////////
54
55 CUBI_USE_NAMESPACE
56 CUBI_USE_NAMESPACE_RESOURCES
57
58 ///////////////////////////////////////////////////////////////////////////////////////////////////
59
60 static QString
61 makePrefix(QContactTrackerEngine *engine)
62 {
63     QString prefix;
64
65     if (0 != engine) {
66         prefix = engine->managerUri();
67     }
68
69     // strip common URI prefix
70     static const QString commonPrefix = QLatin1String("qtcontacts:tracker:");
71
72     if (prefix.startsWith(commonPrefix)) {
73         prefix = prefix.mid(commonPrefix.length());
74     }
75
76     return prefix;
77 }
78
79 static QContactTrackerEngine::DebugFlag
80 queryDebugFlag(const QSparqlQuery &query)
81 {
82     switch(query.type()) {
83     case QSparqlQuery::InsertStatement:
84     case QSparqlQuery::DeleteStatement:
85         return QContactTrackerEngine::ShowUpdates;
86
87     default:
88         break;
89     }
90
91     return QContactTrackerEngine::ShowSelects;
92 }
93
94 QContactManager::Error
95 QTrackerAbstractRequest::translateError(const QSparqlError& error)
96 {
97     switch (error.number()) {
98     case TRACKER_SPARQL_ERROR_NO_SPACE:
99             return QContactManager::OutOfMemoryError;
100
101     case TRACKER_SPARQL_ERROR_UNSUPPORTED:
102         return QContactManager::NotSupportedError;
103
104     default:
105         return QContactManager::UnspecifiedError;
106     }
107 }
108
109 ///////////////////////////////////////////////////////////////////////////////////////////////////
110
111 QTrackerAbstractRequest::QTrackerAbstractRequest(QContactTrackerEngine *engine, QObject *parent)
112     : QObject(parent)
113     , m_engine(engine)
114     , m_lastError(QContactManager::NoError)
115     , m_logger(makePrefix(engine))
116     , m_canceled(false)
117 {
118     if (0 == m_engine) {
119         qctFail("No engine passed to request worker");
120     }
121
122     m_logger.setShowLocation(::qctLogger().showLocation());
123 }
124
125 QTrackerAbstractRequest::~QTrackerAbstractRequest()
126 {
127 }
128
129 void
130 QTrackerAbstractRequest::cancel()
131 {
132     m_canceled = true;
133     engine()->cancelRequest(engine()->request(this).data());
134 }
135
136 bool
137 QTrackerAbstractRequest::canceled() const
138 {
139     return m_canceled;
140 }
141
142 void
143 QTrackerAbstractRequest::emitResult()
144 {
145     emitResult(m_lastError);
146 }
147
148 void
149 QTrackerAbstractRequest::emitResultLater()
150 {
151     if (not canceled()) {
152         QMetaObject::invokeMethod(this, "emitResult", Qt::QueuedConnection);
153     }
154 }
155
156 QSparqlResult *
157 QTrackerAbstractRequest::runQuery(const QSparqlQuery &query)
158 {
159     return runQuery(query, SLOT(onSparqlQueryFinished()));
160 }
161
162 QSparqlResult *
163 QTrackerAbstractRequest::runQuery(const QSparqlQuery &query, const char *slot)
164 {
165     // FIXME: somehow merge with the sync version when QSparqlQueryOptions arrive (and batch saving)
166
167     QSparqlConnection &connection = QctSparqlConnectionManager::defaultConnection();
168
169     if (not connection.isValid()) {
170         reportError(QLatin1String("No valid QtSparql connection."));
171         return 0;
172     }
173
174     return runQuery(query, slot, connection);
175 }
176
177 QSparqlResult *
178 QTrackerAbstractRequest::runQuery(const QSparqlQuery &query, const char *slot,
179                                   QSparqlConnection &connection)
180 {
181     // FIXME: somehow merge with the sync version when QSparqlQueryOptions arrive (and batch saving)
182
183     if (engine()->hasDebugFlag(queryDebugFlag(query))) {
184         qDebug() << query.query();
185     }
186
187     QScopedPointer<QSparqlResult> result(connection.exec(query));
188
189     if (result->hasError()) {
190         reportError(result->lastError());
191         return 0;
192     }
193
194     connect(result.data(), SIGNAL(finished()), slot);
195     result->setParent(this);
196
197     return result.take();
198 }
199
200 QSparqlResult *
201 QTrackerAbstractRequest::runQuerySync(const QSparqlQuery &query)
202 {
203     // FIXME: somehow merge with the async version when QSparqlQueryOptions arrive (and batch saving)
204
205     QSparqlConnection &connection = QctSparqlConnectionManager::defaultConnection();
206
207     if (not connection.isValid()) {
208         reportError(QLatin1String("No valid QtSparql connection."));
209         return 0;
210     }
211
212     return runQuerySync(query, connection);
213 }
214
215 QSparqlResult *
216 QTrackerAbstractRequest::runQuerySync(const QSparqlQuery &query, QSparqlConnection &connection)
217 {
218     // FIXME: somehow merge with the async version when QSparqlQueryOptions arrive (and batch saving)
219
220     if (engine()->hasDebugFlag(queryDebugFlag(query))) {
221         qDebug() << query.query();
222     }
223
224     QScopedPointer<QSparqlResult> result(connection.syncExec(query));
225
226     if (result->hasError()) {
227         reportError(result->lastError());
228         return 0;
229     }
230
231     static bool warningNotShownYet = true;
232
233     if (result->hasFeature(QSparqlResult::Sync)) {
234         warningNotShownYet = true;
235     } else if (warningNotShownYet) {
236         qctWarn(QString::fromLatin1("QtSparql driver %1 doesn't support synchronous data access. "
237                                     "Expect significantly increased memory consumption from fallback "
238                                     "implementation. Consider using a different QtSparql driver.").
239                     arg(connection.driverName()));
240
241         warningNotShownYet = false;
242     }
243
244     result->setParent(this);
245     return result.take();
246 }
247
248 void
249 QTrackerAbstractRequest::reportError(const QString &message, QContactManager::Error error)
250 {
251     qctWarn(QString::fromLatin1("%1 failed: %2").
252             arg(QLatin1String(metaObject()->className()),
253                 qctTruncate(message)));
254
255     setLastError(error);
256     emitResultLater();
257 }
258
259 void
260 QTrackerAbstractRequest::reportError(const QList<QSparqlError> &errors, const QString &details)
261 {
262     if (not details.isEmpty()) {
263         qctWarn(QString::fromLatin1("%1 failed: %2").
264                 arg(QLatin1String(metaObject()->className()), details));
265     }
266
267     setLastError(QContactManager::UnspecifiedError);
268
269     foreach(const QSparqlError &e, errors) {
270         qctWarn(QString::fromLatin1("%1 failed: %2").
271                 arg(QLatin1String(metaObject()->className()),
272                     qctTruncate(e.message())));
273
274         const QContactManager::Error error = translateError(e);
275
276         if (QContactManager::OutOfMemoryError == error) {
277             setLastError(error);
278         } else if (lastError() != error && lastError() == QContactManager::UnspecifiedError) {
279             setLastError(error);
280         }
281     }
282
283     emitResultLater();
284 }
285
286 void
287 QTrackerAbstractRequest::reportError(const QSparqlError &error, const QString &details)
288 {
289     const QString message = details % QLatin1String(details.isEmpty() ? "": ": ") % error.message();
290     reportError(message, translateError(error));
291 }
292
293 void
294 QTrackerAbstractRequest::onSparqlQueryFinished()
295 {
296     QSparqlResult *const result = qobject_cast<QSparqlResult *>(sender());
297
298     if (0 == result) {
299         qctWarn("Ignoring signal from invalid sender.");
300         return;
301     }
302
303     if (result->hasError()) {
304         // reportError emits a signal which might cause deletion of the sender
305         const QSparqlError sparqlError = result->lastError();
306         const QContactManager::Error error = translateError(sparqlError);
307
308         // give the implementing request a chance to clean-up the request from its internal lists
309         if (handleQueryError(result, error)) {
310             reportError(sparqlError.message(), error);
311         }
312     } else {
313         if ((engine()->hasDebugFlag(QContactTrackerEngine::ShowModels)) && (result->isTable())) {
314             for(bool hasRow = result->first(); hasRow; hasRow = result->next()) {
315                 qDebug() << result->current();
316             }
317
318             // rewind result
319             result->first();
320             result->previous();
321         }
322
323         if (handleQuerySuccess(result)) {
324             // Even if handleQuerySuccess() returns true, we still check the contents
325             // of m_lastError since that fact that the sparql query went well does not
326             // imply that the request succeeded completely.
327             // This is especially true with requests having an errorMap, where parts of
328             // the request can succeed even if some others failed.
329             emitResult(m_lastError);
330         }
331     }
332 }