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