diff --git a/0001-sqlite-Check-that-there-are-values-to-be-set-when-bi.patch b/0001-sqlite-Check-that-there-are-values-to-be-set-when-bi.patch new file mode 100644 index 0000000..57a2fef --- /dev/null +++ b/0001-sqlite-Check-that-there-are-values-to-be-set-when-bi.patch @@ -0,0 +1,33 @@ +From cd12671fac459b71d2f5f65d3e221e4e8fca25f1 Mon Sep 17 00:00:00 2001 +From: Andy Shaw +Date: Thu, 7 Dec 2017 16:01:48 +0100 +Subject: [PATCH 1/3] sqlite: Check that there are values to be set when + binding + +If the values vector is empty then we know already that the paramCount +will still be invalid, so we should just accept that and not check the +reused named placeholders. + +Task-number: QTBUG-64923 +Change-Id: Ifaa755540c4574f1f76d3f9f129bf0f66b837b70 +Reviewed-by: Edward Welbourne +--- + src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +index e9f5ee9508..67dd1a6ee5 100644 +--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp ++++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +@@ -467,7 +467,7 @@ bool QSQLiteResult::exec() + + #if (SQLITE_VERSION_NUMBER >= 3003011) + // In the case of the reuse of a named placeholder +- if (!paramCountIsValid) { ++ if (paramCount < values.count()) { + const auto countIndexes = [](int counter, const QList& indexList) { + return counter + indexList.length(); + }; +-- +2.16.2 + diff --git a/0002-sqlite-Bind-duplicated-named-placeholders-correctly.patch b/0002-sqlite-Bind-duplicated-named-placeholders-correctly.patch new file mode 100644 index 0000000..b38d35b --- /dev/null +++ b/0002-sqlite-Bind-duplicated-named-placeholders-correctly.patch @@ -0,0 +1,91 @@ +From 0c8aa32d9a1b1952c5d53529655c485f5b34a695 Mon Sep 17 00:00:00 2001 +From: Andy Shaw +Date: Tue, 2 Jan 2018 09:33:48 +0100 +Subject: [PATCH 2/3] sqlite: Bind duplicated named placeholders correctly + +sqlite will reuse the index for bound parameters when the named +placeholder is duplicated so we only need to call bind one time for each +placeholder. Therefore we need to have just one instance of each value +when doing the bind. + +Task-number: QTBUG-65150 +Change-Id: I75c4bcc4563e43c180a59a7a4cbb770dbe994642 +Reviewed-by: Jesus Fernandez +--- + src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp | 16 ++++++++++++++- + tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp | 25 +++++++++++++++++++++++ + 2 files changed, 40 insertions(+), 1 deletion(-) + +diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +index 67dd1a6ee5..444b18686a 100644 +--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp ++++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +@@ -446,7 +446,7 @@ static QString timespecToString(const QDateTime &dateTime) + bool QSQLiteResult::exec() + { + Q_D(QSQLiteResult); +- const QVector values = boundValues(); ++ QVector values = boundValues(); + + d->skippedStatus = false; + d->skipRow = false; +@@ -478,6 +478,20 @@ bool QSQLiteResult::exec() + countIndexes); + + paramCountIsValid = bindParamCount == values.count(); ++ // When using named placeholders, it will reuse the index for duplicated ++ // placeholders. So we need to ensure the QVector has only one instance of ++ // each value as SQLite will do the rest for us. ++ QVector prunedValues; ++ QList handledIndexes; ++ for (int i = 0, currentIndex = 0; i < values.size(); ++i) { ++ if (handledIndexes.contains(i)) ++ continue; ++ const auto placeHolder = QString::fromUtf8(sqlite3_bind_parameter_name(d->stmt, currentIndex + 1)); ++ handledIndexes << d->indexes[placeHolder]; ++ prunedValues << values.at(d->indexes[placeHolder].first()); ++ ++currentIndex; ++ } ++ values = prunedValues; + } + #endif + +diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +index 3ea13896d8..1a0340f153 100644 +--- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp ++++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +@@ -2017,6 +2017,31 @@ void tst_QSqlQuery::prepare_bind_exec() + QCOMPARE( q.value(1).toString(), QString("name") ); + QCOMPARE( q.value(2).toString(), QString("name") ); + ++ // Test that duplicated named placeholders before the next unique one works correctly - QTBUG-65150 ++ QVERIFY(q.prepare("insert into " + qtest_prepare + " (id, name, name2) values (:id, :id, :name)")); ++ for (i = 104; i < 106; ++i) { ++ q.bindValue(":id", i); ++ q.bindValue(":name", "name"); ++ QVERIFY(q.exec()); ++ } ++ QVERIFY(q.exec("select * from " + qtest_prepare + " where id > 103 order by id")); ++ QVERIFY(q.next()); ++ QCOMPARE(q.value(0).toInt(), 104); ++ QCOMPARE(q.value(1).toString(), QString("104")); ++ QCOMPARE(q.value(2).toString(), QString("name")); ++ ++ // Test that duplicated named placeholders in any order ++ QVERIFY(q.prepare("insert into " + qtest_prepare + " (id, name, name2) values (:id, :name, :id)")); ++ for (i = 107; i < 109; ++i) { ++ q.bindValue(":id", i); ++ q.bindValue(":name", "name"); ++ QVERIFY(q.exec()); ++ } ++ QVERIFY(q.exec("select * from " + qtest_prepare + " where id > 106 order by id")); ++ QVERIFY(q.next()); ++ QCOMPARE(q.value(0).toInt(), 107); ++ QCOMPARE(q.value(1).toString(), QString("name")); ++ QCOMPARE(q.value(2).toString(), QString("107")); + } // end of SQLite scope + } + +-- +2.16.2 + diff --git a/0003-sqlite-Prevent-a-crash-when-sqlite-does-not-detect-a.patch b/0003-sqlite-Prevent-a-crash-when-sqlite-does-not-detect-a.patch new file mode 100644 index 0000000..25c3809 --- /dev/null +++ b/0003-sqlite-Prevent-a-crash-when-sqlite-does-not-detect-a.patch @@ -0,0 +1,97 @@ +From f9b3ad5f5f6a36e7c45bdfbe61b364dcba70836d Mon Sep 17 00:00:00 2001 +From: Andy Shaw +Date: Wed, 7 Mar 2018 15:12:13 +0100 +Subject: [PATCH 3/3] sqlite: Prevent a crash when sqlite does not detect any + parameters + +When using a virtual table inside a SQLite database it is possible that +it does not report the right number of parameters. Therefore we need +to account for this case to prevent it from crashing when trying to +bind parameters it thinks does not exist. + +Task-number: QTBUG-66816 +Change-Id: I3ff70bb1fe73091f43c3df53616f75858e451cfd +Reviewed-by: Jarek Kobus +Reviewed-by: Edward Welbourne +--- + src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp | 5 ++- + tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp | 41 +++++++++++++++++++++++ + 2 files changed, 45 insertions(+), 1 deletion(-) + +diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +index 444b18686a..08875299b2 100644 +--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp ++++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp +@@ -467,7 +467,10 @@ bool QSQLiteResult::exec() + + #if (SQLITE_VERSION_NUMBER >= 3003011) + // In the case of the reuse of a named placeholder +- if (paramCount < values.count()) { ++ // We need to check explicitly that paramCount is greater than 1, as sqlite ++ // can end up in a case where for virtual tables it returns 0 even though it ++ // has parameters ++ if (paramCount > 1 && paramCount < values.count()) { + const auto countIndexes = [](int counter, const QList& indexList) { + return counter + indexList.length(); + }; +diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +index 1a0340f153..c57dbb8a01 100644 +--- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp ++++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +@@ -172,6 +172,8 @@ private slots: + void emptyTableNavigate(); + void timeStampParsing_data() { generic_data(); } + void timeStampParsing(); ++ void sqliteVirtualTable_data() { generic_data("QSQLITE"); } ++ void sqliteVirtualTable(); + + #ifdef NOT_READY_YET + void task_229811(); +@@ -4167,5 +4169,44 @@ void tst_QSqlQuery::QTBUG_57138() + QCOMPARE(q.value(2).toDateTime(), tzoffset); + } + ++void tst_QSqlQuery::sqliteVirtualTable() ++{ ++ // Virtual tables can behave differently when it comes to prepared ++ // queries, so we need to check these explicitly ++ QFETCH(QString, dbName); ++ QSqlDatabase db = QSqlDatabase::database(dbName); ++ CHECK_DATABASE(db); ++ const auto tableName = qTableName("sqliteVirtual", __FILE__, db); ++ QSqlQuery qry(db); ++ QVERIFY_SQL(qry, exec("create virtual table " + tableName + " using fts3(id, name)")); ++ ++ // Delibrately malform the query to try and provoke a potential crash situation ++ QVERIFY_SQL(qry, prepare("select * from " + tableName + " where name match '?'")); ++ qry.addBindValue("Andy"); ++ QVERIFY(!qry.exec()); ++ ++ QVERIFY_SQL(qry, prepare("insert into " + tableName + "(id, name) VALUES (?, ?)")); ++ qry.addBindValue(1); ++ qry.addBindValue("Andy"); ++ QVERIFY_SQL(qry, exec()); ++ ++ QVERIFY_SQL(qry, exec("select * from " + tableName)); ++ QVERIFY(qry.next()); ++ QCOMPARE(qry.value(0).toInt(), 1); ++ QCOMPARE(qry.value(1).toString(), "Andy"); ++ ++ QVERIFY_SQL(qry, prepare("insert into " + tableName + "(id, name) values (:id, :name)")); ++ qry.bindValue(":id", 2); ++ qry.bindValue(":name", "Peter"); ++ QVERIFY_SQL(qry, exec()); ++ ++ QVERIFY_SQL(qry, prepare("select * from " + tableName + " where name match ?")); ++ qry.addBindValue("Peter"); ++ QVERIFY_SQL(qry, exec()); ++ QVERIFY(qry.next()); ++ QCOMPARE(qry.value(0).toInt(), 2); ++ QCOMPARE(qry.value(1).toString(), "Peter"); ++} ++ + QTEST_MAIN( tst_QSqlQuery ) + #include "tst_qsqlquery.moc" +-- +2.16.2 + diff --git a/libqt5-qtbase.changes b/libqt5-qtbase.changes index 9eddb52..d7fbcb7 100644 --- a/libqt5-qtbase.changes +++ b/libqt5-qtbase.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Fri Apr 27 16:25:43 UTC 2018 - fabian@ritter-vogt.de + +- Add patches to fix crashes with certain sqlite queries: + * 0001-sqlite-Check-that-there-are-values-to-be-set-when-bi.patch + * 0002-sqlite-Bind-duplicated-named-placeholders-correctly.patch + * 0003-sqlite-Prevent-a-crash-when-sqlite-does-not-detect-a.patch + ------------------------------------------------------------------- Tue Mar 27 18:22:40 UTC 2018 - fabian@ritter-vogt.de diff --git a/libqt5-qtbase.spec b/libqt5-qtbase.spec index a2b173b..8ee4063 100644 --- a/libqt5-qtbase.spec +++ b/libqt5-qtbase.spec @@ -102,6 +102,9 @@ Patch2026: 0016-CUPS-Rework-set-clearCupsOption-API.patch Patch2027: 0017-Cups-Print-Dialog-Change-the-message-box-titles-to-C.patch Patch2028: 0018-Fix-build-due-to-missing-QDebug-include.patch Patch2029: 0001-Do-a-static_cast-in-bit-blasts-that-are-UB.patch +Patch2030: 0001-sqlite-Check-that-there-are-values-to-be-set-when-bi.patch +Patch2031: 0002-sqlite-Bind-duplicated-named-placeholders-correctly.patch +Patch2032: 0003-sqlite-Prevent-a-crash-when-sqlite-does-not-detect-a.patch BuildRequires: alsa-devel BuildRequires: cups-devel BuildRequires: double-conversion-devel