Merge lp:~boiko/history-service/rtm-fix_group_thread_matching into lp:history-service/rtm-14.09
- rtm-fix_group_thread_matching
- Merge into rtm-14.09
Status: | Merged |
---|---|
Approved by: | Tiago Salem Herrmann |
Approved revision: | 173 |
Merged at revision: | 173 |
Proposed branch: | lp:~boiko/history-service/rtm-fix_group_thread_matching |
Merge into: | lp:history-service/rtm-14.09 |
Diff against target: |
397 lines (+159/-51) 6 files modified
plugins/sqlite/sqlitehistoryeventview.cpp (+14/-2) plugins/sqlite/sqlitehistoryplugin.cpp (+6/-22) plugins/sqlite/sqlitehistoryplugin.h (+1/-1) plugins/sqlite/sqlitehistorythreadview.cpp (+14/-2) plugins/sqlite/tests/SqlitePluginTest.cpp (+31/-13) src/tests/EventViewTest.cpp (+93/-11) |
To merge this branch: | bzr merge lp:~boiko/history-service/rtm-fix_group_thread_matching |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tiago Salem Herrmann (community) | Approve | ||
Review via email: mp+247917@code.launchpad.net |
Commit message
Use QSqlQuery:
Description of the change
Use QSqlQuery:
== Checklist ==
Are there any related MPs required for this MP to build/function as expected? Please list.
No
Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes)
Yes
Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator?
Yes
Did you successfully run all tests found in your component's Test Plan (https:/
Yes
If you changed the UI, was the change specified/approved by design?
N/A
If you changed UI labels, did you update the pot file?
N/A
If you changed the packaging (debian), did you add a core-dev as a reviewer to this MP?
N/A
Preview Diff
1 | === modified file 'plugins/sqlite/sqlitehistoryeventview.cpp' |
2 | --- plugins/sqlite/sqlitehistoryeventview.cpp 2015-01-23 20:21:34 +0000 |
3 | +++ plugins/sqlite/sqlitehistoryeventview.cpp 2015-01-28 23:16:22 +0000 |
4 | @@ -38,7 +38,8 @@ |
5 | mQuery.setForwardOnly(true); |
6 | |
7 | // FIXME: validate the filter |
8 | - QString condition = mPlugin->filterToString(filter); |
9 | + QVariantMap filterValues; |
10 | + QString condition = mPlugin->filterToString(filter, filterValues); |
11 | QString order; |
12 | if (!sort.sortField().isNull()) { |
13 | order = QString("ORDER BY %1 %2").arg(sort.sortField(), sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); |
14 | @@ -48,7 +49,18 @@ |
15 | QString queryText = QString("CREATE TEMP TABLE %1 AS ").arg(mTemporaryTable); |
16 | queryText += mPlugin->sqlQueryForEvents(type, condition, order); |
17 | |
18 | - if (!mQuery.exec(queryText)) { |
19 | + if (!mQuery.prepare(queryText)) { |
20 | + mValid = false; |
21 | + Q_EMIT Invalidated(); |
22 | + qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); |
23 | + return; |
24 | + } |
25 | + |
26 | + Q_FOREACH(const QString &key, filterValues.keys()) { |
27 | + mQuery.bindValue(key, filterValues[key]); |
28 | + } |
29 | + |
30 | + if (!mQuery.exec()) { |
31 | mValid = false; |
32 | Q_EMIT Invalidated(); |
33 | qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); |
34 | |
35 | === modified file 'plugins/sqlite/sqlitehistoryplugin.cpp' |
36 | --- plugins/sqlite/sqlitehistoryplugin.cpp 2015-01-25 13:25:56 +0000 |
37 | +++ plugins/sqlite/sqlitehistoryplugin.cpp 2015-01-28 23:16:22 +0000 |
38 | @@ -662,7 +662,7 @@ |
39 | return QDateTime(timestamp.date(), timestamp.time(), Qt::UTC).toLocalTime().toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
40 | } |
41 | |
42 | -QString SQLiteHistoryPlugin::filterToString(const History::Filter &filter, const QString &propertyPrefix) const |
43 | +QString SQLiteHistoryPlugin::filterToString(const History::Filter &filter, QVariantMap &bindValues, const QString &propertyPrefix) const |
44 | { |
45 | QString result; |
46 | History::Filters filters; |
47 | @@ -690,7 +690,7 @@ |
48 | count = filters.count(); |
49 | for (int i = 0; i < count; ++i) { |
50 | // run recursively through the inner filters |
51 | - result += QString("(%1)").arg(filterToString(filters[i], propertyPrefix)); |
52 | + result += QString("(%1)").arg(filterToString(filters[i], bindValues, propertyPrefix)); |
53 | if (i != count-1) { |
54 | result += linking; |
55 | } |
56 | @@ -698,36 +698,20 @@ |
57 | result += " )"; |
58 | break; |
59 | default: |
60 | - // FIXME: remove the toString() functionality or replace it by a better implementation |
61 | if (filterProperty.isEmpty() || filterValue.isNull()) { |
62 | break; |
63 | } |
64 | |
65 | - switch (filterValue.type()) { |
66 | - case QVariant::String: |
67 | - // FIXME: need to escape strings |
68 | - // wrap strings |
69 | - value = QString("'%1'").arg(escapeFilterValue(filterValue.toString())); |
70 | - break; |
71 | - case QVariant::Bool: |
72 | - value = filterValue.toBool() ? "1" : "0"; |
73 | - break; |
74 | - case QVariant::Int: |
75 | - value = QString::number(filterValue.toInt()); |
76 | - break; |
77 | - case QVariant::Double: |
78 | - value = QString::number(filterValue.toDouble()); |
79 | - break; |
80 | - default: |
81 | - value = filterValue.toString(); |
82 | - } |
83 | + QString bindId = QString(":filterValue%1").arg(bindValues.count()); |
84 | |
85 | QString propertyName = propertyPrefix.isNull() ? filterProperty : QString("%1.%2").arg(propertyPrefix, filterProperty); |
86 | // FIXME: need to check for other match flags and multiple match flags |
87 | if (filter.matchFlags() & History::MatchContains) { |
88 | + // FIXME: maybe we should use QString("%1 LIKE '\%'||%2'\%'").arg(bindId) ?? needs more time for investigating |
89 | result = QString("%1 LIKE '\%%2\%' ESCAPE '\\'").arg(propertyName, escapeFilterValue(filterValue.toString())); |
90 | } else { |
91 | - result = QString("%1=%2").arg(propertyName, value); |
92 | + result = QString("%1=%2").arg(propertyName, bindId); |
93 | + bindValues[bindId] = filterValue; |
94 | } |
95 | } |
96 | |
97 | |
98 | === modified file 'plugins/sqlite/sqlitehistoryplugin.h' |
99 | --- plugins/sqlite/sqlitehistoryplugin.h 2015-01-24 19:34:40 +0000 |
100 | +++ plugins/sqlite/sqlitehistoryplugin.h 2015-01-28 23:16:22 +0000 |
101 | @@ -80,7 +80,7 @@ |
102 | |
103 | static QString toLocalTimeString(const QDateTime ×tamp); |
104 | |
105 | - QString filterToString(const History::Filter &filter, const QString &propertyPrefix = QString::null) const; |
106 | + QString filterToString(const History::Filter &filter, QVariantMap &bindValues, const QString &propertyPrefix = QString::null) const; |
107 | QString escapeFilterValue(const QString &value) const; |
108 | }; |
109 | |
110 | |
111 | === modified file 'plugins/sqlite/sqlitehistorythreadview.cpp' |
112 | --- plugins/sqlite/sqlitehistorythreadview.cpp 2015-01-23 20:21:34 +0000 |
113 | +++ plugins/sqlite/sqlitehistorythreadview.cpp 2015-01-28 23:16:22 +0000 |
114 | @@ -39,7 +39,8 @@ |
115 | mQuery.setForwardOnly(true); |
116 | |
117 | // FIXME: validate the filter |
118 | - QString condition = mPlugin->filterToString(filter); |
119 | + QVariantMap filterValues; |
120 | + QString condition = mPlugin->filterToString(filter, filterValues); |
121 | QString order; |
122 | if (!sort.sortField().isNull()) { |
123 | order = QString("ORDER BY %1 %2").arg(sort.sortField(), sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); |
124 | @@ -49,8 +50,19 @@ |
125 | QString queryText = QString("CREATE TEMP TABLE %1 AS ").arg(mTemporaryTable); |
126 | queryText += mPlugin->sqlQueryForThreads(type, condition, order); |
127 | |
128 | + if (!mQuery.prepare(queryText)) { |
129 | + qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); |
130 | + mValid = false; |
131 | + Q_EMIT Invalidated(); |
132 | + return; |
133 | + } |
134 | + |
135 | + Q_FOREACH(const QString &key, filterValues.keys()) { |
136 | + mQuery.bindValue(key, filterValues[key]); |
137 | + } |
138 | + |
139 | // create the temporary table |
140 | - if (!mQuery.exec(queryText)) { |
141 | + if (!mQuery.exec()) { |
142 | qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); |
143 | mValid = false; |
144 | Q_EMIT Invalidated(); |
145 | |
146 | === modified file 'plugins/sqlite/tests/SqlitePluginTest.cpp' |
147 | --- plugins/sqlite/tests/SqlitePluginTest.cpp 2015-01-25 13:25:56 +0000 |
148 | +++ plugins/sqlite/tests/SqlitePluginTest.cpp 2015-01-28 23:16:22 +0000 |
149 | @@ -718,35 +718,41 @@ |
150 | void SqlitePluginTest::testFilterToString_data() |
151 | { |
152 | QTest::addColumn<QVariantMap>("filterProperties"); |
153 | + QTest::addColumn<QVariantMap>("filterValues"); |
154 | QTest::addColumn<QString>("propertyPrefix"); |
155 | QTest::addColumn<QString>("resultString"); |
156 | |
157 | History::Filter filter; |
158 | + QVariantMap filterValues; |
159 | filter.setFilterProperty("testProperty"); |
160 | filter.setFilterValue("stringValue"); |
161 | - QTest::newRow("simple string filter") << filter.properties() << QString() << "testProperty='stringValue'"; |
162 | - |
163 | - filter.setFilterValue("escaped\\0\\n%"); |
164 | - QTest::newRow("check that strings are escaped") << filter.properties() << QString() << "testProperty='escaped\\\\0\\\\n\\%'"; |
165 | + filterValues[":filterValue0"] = filter.filterValue(); |
166 | + QTest::newRow("simple string filter") << filter.properties() << filterValues << QString() << "testProperty=:filterValue0"; |
167 | |
168 | filter.setFilterValue(12345); |
169 | - QTest::newRow("simple integer filter") << filter.properties() << QString() << "testProperty=12345"; |
170 | + filterValues[":filterValue0"] = filter.filterValue(); |
171 | + QTest::newRow("simple integer filter") << filter.properties() << filterValues << QString() << "testProperty=:filterValue0"; |
172 | |
173 | filter.setFilterValue(true); |
174 | - QTest::newRow("simple true boolean filter") << filter.properties() << QString() << "testProperty=1"; |
175 | + filterValues[":filterValue0"] = filter.filterValue(); |
176 | + QTest::newRow("simple true boolean filter") << filter.properties() << filterValues << QString() << "testProperty=:filterValue0"; |
177 | |
178 | filter.setFilterValue(false); |
179 | - QTest::newRow("simple false boolean filter") << filter.properties() << QString() << "testProperty=0"; |
180 | + filterValues[":filterValue0"] = filter.filterValue(); |
181 | + QTest::newRow("simple false boolean filter") << filter.properties() << filterValues << QString() << "testProperty=:filterValue0"; |
182 | |
183 | filter.setFilterValue(12345); |
184 | - QTest::newRow("filter with a prefix") << filter.properties() << QString("prefix") << "prefix.testProperty=12345"; |
185 | + filterValues[":filterValue0"] = filter.filterValue(); |
186 | + QTest::newRow("filter with a prefix") << filter.properties() << filterValues << QString("prefix") << "prefix.testProperty=:filterValue0"; |
187 | |
188 | filter.setMatchFlags(History::MatchContains); |
189 | filter.setFilterValue("partialString"); |
190 | - QTest::newRow("match contains") << filter.properties() << QString() << "testProperty LIKE '\%partialString\%' ESCAPE '\\'"; |
191 | + filterValues.clear(); |
192 | + QTest::newRow("match contains") << filter.properties() << filterValues << QString() << "testProperty LIKE '\%partialString\%' ESCAPE '\\'"; |
193 | |
194 | filter.setFilterValue("%"); |
195 | - QTest::newRow("partial match escaped") << filter.properties() << QString() << "testProperty LIKE '\%\\\%\%' ESCAPE '\\'"; |
196 | + filterValues.clear(); |
197 | + QTest::newRow("partial match escaped") << filter.properties() << filterValues << QString() << "testProperty LIKE '\%\\\%\%' ESCAPE '\\'"; |
198 | |
199 | History::IntersectionFilter intersectionFilter; |
200 | filter.setMatchFlags(History::MatchFlags()); |
201 | @@ -756,7 +762,11 @@ |
202 | intersectionFilter.append(filter); |
203 | filter.setFilterValue("a string"); |
204 | intersectionFilter.append(filter); |
205 | - QTest::newRow("intersection filter") << intersectionFilter.properties() << QString() << "( (testProperty=12345) AND (testProperty=1) AND (testProperty='a string') )"; |
206 | + filterValues.clear(); |
207 | + filterValues[":filterValue0"] = 12345; |
208 | + filterValues[":filterValue1"] = true; |
209 | + filterValues[":filterValue2"] = "a string"; |
210 | + QTest::newRow("intersection filter") << intersectionFilter.properties() << filterValues << QString() << "( (testProperty=:filterValue0) AND (testProperty=:filterValue1) AND (testProperty=:filterValue2) )"; |
211 | |
212 | History::UnionFilter unionFilter; |
213 | filter.setFilterValue(12345); |
214 | @@ -765,17 +775,25 @@ |
215 | unionFilter.append(filter); |
216 | filter.setFilterValue("a string"); |
217 | unionFilter.append(filter); |
218 | - QTest::newRow("union filter") << unionFilter.properties() << QString() << "( (testProperty=12345) OR (testProperty=1) OR (testProperty='a string') )"; |
219 | + filterValues.clear(); |
220 | + filterValues[":filterValue0"] = 12345; |
221 | + filterValues[":filterValue1"] = true; |
222 | + filterValues[":filterValue2"] = "a string"; |
223 | + QTest::newRow("union filter") << unionFilter.properties() << filterValues << QString() << "( (testProperty=:filterValue0) OR (testProperty=:filterValue1) OR (testProperty=:filterValue2) )"; |
224 | } |
225 | |
226 | void SqlitePluginTest::testFilterToString() |
227 | { |
228 | QFETCH(QVariantMap, filterProperties); |
229 | + QFETCH(QVariantMap, filterValues); |
230 | QFETCH(QString, propertyPrefix); |
231 | QFETCH(QString, resultString); |
232 | |
233 | - QString result = mPlugin->filterToString(History::Filter::fromProperties(filterProperties), propertyPrefix); |
234 | + QVariantMap resultValues; |
235 | + QString result = mPlugin->filterToString(History::Filter::fromProperties(filterProperties), resultValues, propertyPrefix); |
236 | QCOMPARE(result, resultString); |
237 | + QCOMPARE(resultValues, filterValues); |
238 | + |
239 | } |
240 | |
241 | void SqlitePluginTest::testEscapeFilterValue_data() |
242 | |
243 | === modified file 'src/tests/EventViewTest.cpp' |
244 | --- src/tests/EventViewTest.cpp 2013-12-09 21:18:14 +0000 |
245 | +++ src/tests/EventViewTest.cpp 2015-01-28 23:16:22 +0000 |
246 | @@ -39,6 +39,7 @@ |
247 | private Q_SLOTS: |
248 | void initTestCase(); |
249 | void testNextPage(); |
250 | + void testFilter_data(); |
251 | void testFilter(); |
252 | void testSort(); |
253 | |
254 | @@ -66,28 +67,66 @@ |
255 | events = view->nextPage(); |
256 | } |
257 | |
258 | - QCOMPARE(allEvents.count(), EVENT_COUNT * 2); |
259 | + QCOMPARE(allEvents.count(), EVENT_COUNT * 2 + 1); // include the group text event |
260 | Q_FOREACH(const History::Event &event, events) { |
261 | QCOMPARE(event.type(), History::EventTypeText); |
262 | } |
263 | } |
264 | |
265 | -void EventViewTest::testFilter() |
266 | +void EventViewTest::testFilter_data() |
267 | { |
268 | + QTest::addColumn<QVariantMap>("filterProperties"); |
269 | + QTest::addColumn<History::EventType>("eventType"); |
270 | + QTest::addColumn<int>("resultCount"); |
271 | + QTest::addColumn<QVariantMap>("firstEventProperties"); |
272 | + |
273 | History::IntersectionFilter filter; |
274 | filter.append(History::Filter(History::FieldAccountId, "account0")); |
275 | filter.append(History::Filter(History::FieldThreadId, "participant0")); |
276 | filter.append(History::Filter(History::FieldEventId, "event21")); |
277 | - |
278 | - History::EventViewPtr view = History::Manager::instance()->queryEvents(History::EventTypeVoice, History::Sort(History::FieldAccountId), filter); |
279 | + History::VoiceEvent voiceEvent = History::Manager::instance()->getSingleEvent(History::EventTypeVoice, |
280 | + "account0", |
281 | + "participant0", |
282 | + "event21"); |
283 | + QVERIFY(!voiceEvent.isNull()); |
284 | + QTest::newRow("filter by accountId, threadId and eventId") << filter.properties() << History::EventTypeVoice << 1 << voiceEvent.properties(); |
285 | + |
286 | + filter.clear(); |
287 | + QStringList participants; |
288 | + participants << "groupParticipant1" << "groupParticipant2"; |
289 | + History::Thread thread = History::Manager::instance()->threadForParticipants("groupAccount", |
290 | + History::EventTypeText, |
291 | + participants, |
292 | + History::MatchCaseSensitive); |
293 | + QVERIFY(!thread.isNull()); |
294 | + History::TextEvent textEvent = History::Manager::instance()->getSingleEvent(History::EventTypeText, |
295 | + thread.accountId(), |
296 | + thread.threadId(), |
297 | + "groupEvent0"); |
298 | + QVERIFY(!textEvent.isNull()); |
299 | + filter.append(History::Filter(History::FieldAccountId, thread.accountId())); |
300 | + filter.append(History::Filter(History::FieldThreadId, thread.threadId())); |
301 | + QTest::newRow("filter for a group conversation") << filter.properties() << History::EventTypeText << 1 << textEvent.properties(); |
302 | +} |
303 | + |
304 | +void EventViewTest::testFilter() |
305 | +{ |
306 | + QFETCH(QVariantMap, filterProperties); |
307 | + QFETCH(History::EventType, eventType); |
308 | + QFETCH(int, resultCount); |
309 | + QFETCH(QVariantMap, firstEventProperties); |
310 | + |
311 | + History::Filter filter = History::Filter::fromProperties(filterProperties); |
312 | + History::EventViewPtr view = History::Manager::instance()->queryEvents(eventType, |
313 | + History::Sort(History::FieldAccountId), |
314 | + filter); |
315 | QVERIFY(view->isValid()); |
316 | + |
317 | History::Events events = view->nextPage(); |
318 | - QCOMPARE(events.count(), 1); |
319 | + QCOMPARE(events.count(), resultCount); |
320 | + |
321 | History::Event event = events.first(); |
322 | - QCOMPARE(event.accountId(), QString("account0")); |
323 | - QCOMPARE(event.type(), History::EventTypeVoice); |
324 | - QCOMPARE(event.threadId(), QString("participant0")); |
325 | - QCOMPARE(event.eventId(), QString("event21")); |
326 | + QCOMPARE(event.properties(), firstEventProperties); |
327 | |
328 | // make sure no more items are returned |
329 | QVERIFY(view->nextPage().isEmpty()); |
330 | @@ -106,7 +145,7 @@ |
331 | } |
332 | |
333 | QCOMPARE(allEvents.first().eventId(), QString("event00")); |
334 | - QCOMPARE(allEvents.last().eventId(), QString("event%1").arg(EVENT_COUNT-1)); |
335 | + QCOMPARE(allEvents.last().eventId(), QString("groupEvent0")); |
336 | |
337 | History::Sort descendingSort(History::FieldEventId, Qt::DescendingOrder); |
338 | allEvents.clear(); |
339 | @@ -118,7 +157,7 @@ |
340 | events = view->nextPage(); |
341 | } |
342 | |
343 | - QCOMPARE(allEvents.first().eventId(), QString("event%1").arg(EVENT_COUNT-1)); |
344 | + QCOMPARE(allEvents.first().eventId(), QString("groupEvent0")); |
345 | QCOMPARE(allEvents.last().eventId(), QString("event00")); |
346 | } |
347 | |
348 | @@ -166,6 +205,49 @@ |
349 | } |
350 | QVERIFY(History::Manager::instance()->writeEvents(events)); |
351 | } |
352 | + |
353 | + // create a text thread with multiple participants |
354 | + QStringList participants; |
355 | + participants << "groupParticipant1" << "groupParticipant2"; |
356 | + |
357 | + History::Thread groupTextThread = History::Manager::instance()->threadForParticipants("groupAccount", |
358 | + History::EventTypeText, |
359 | + participants, |
360 | + History::MatchCaseSensitive, |
361 | + true); |
362 | + QVERIFY(!groupTextThread.isNull()); |
363 | + |
364 | + // and write a single event to it, just to make sure it works |
365 | + History::TextEvent groupTextEvent("groupAccount", |
366 | + groupTextThread.threadId(), |
367 | + "groupEvent0", |
368 | + "groupSender", |
369 | + QDateTime::currentDateTime(), |
370 | + true, |
371 | + "A group message", |
372 | + History::MessageTypeText); |
373 | + QVERIFY(History::Manager::instance()->writeEvents(History::Events() << groupTextEvent)); |
374 | + |
375 | + // create a text thread with multiple participants |
376 | + participants.clear(); |
377 | + participants << "groupParticipant1" << "groupParticipant2"; |
378 | + |
379 | + History::Thread groupVoiceThread = History::Manager::instance()->threadForParticipants("groupAccount", |
380 | + History::EventTypeVoice, |
381 | + participants, |
382 | + History::MatchCaseSensitive, |
383 | + true); |
384 | + QVERIFY(!groupVoiceThread.isNull()); |
385 | + |
386 | + // and write a single event to it, just to make sure it works |
387 | + History::VoiceEvent groupVoiceEvent("groupAccount", |
388 | + groupVoiceThread.threadId(), |
389 | + "groupEvent0", |
390 | + "groupSender", |
391 | + QDateTime::currentDateTime(), |
392 | + true, |
393 | + true); |
394 | + QVERIFY(History::Manager::instance()->writeEvents(History::Events() << groupVoiceEvent)); |
395 | } |
396 | |
397 | QTEST_MAIN(EventViewTest) |
Looks good.
--Checklist--
Did you perform an exploratory manual test run of the code change and any related functionality on
device or emulator?
Yes
Did CI run pass? If not, please explain why.
No, it does not run for rtm branches.
Have you checked that submitter has accurately filled out the submitter checklist and has taken no shortcut?
Yes